diff --git a/lib/app/components.dart b/lib/app/components.dart index 7d3c683..2299eeb 100644 --- a/lib/app/components.dart +++ b/lib/app/components.dart @@ -48,6 +48,23 @@ class AppComponent { /// 처음 설치 때 켜진 것 static List get defaultInstalled => [for (final c in all) if (c.builtIn) c.id]; + /// 225: 화면 순서를 [known] (이 판이 아는 화면, 새 순서) 로 바꾸되, [old] 안의 모르는 화면 (다른 판의 Tor 등) 은 그 자리에 남긴다 + static List reorder(List old, List known) { + final next = known.iterator; + final out = []; + for (final x in old) { + if (byId(x) == null) { + out.add(x); + } else if (next.moveNext()) { + out.add(next.current); + } + } + while (next.moveNext()) { + out.add(next.current); + } + return out; + } + /// 설치된 화면들을 [order] 순서로 (순서에 없는 것은 기본 순서대로 뒤에) static List pages(List installed, List order) { final ids = [ diff --git a/lib/app/settings.dart b/lib/app/settings.dart index cddf534..079b064 100644 --- a/lib/app/settings.dart +++ b/lib/app/settings.dart @@ -879,13 +879,15 @@ class AppSettings { ..rsyncPaths = [for (final x in (j['rsyncPaths'] as List?) ?? const []) '$x'] ..components = j['components'] is List ? [ - for (final x in j['components'] as List) if (AppComponent.byId('$x') != null) '$x', + // 225: 이 판이 모르는 것 (다른 판 · 새 판의 컴포넌트 - Tor 등) 도 지우지 않고 남긴다 (40: 모르는 설정은 남김). + // 화면 · 기능은 아는 것만 쓴다 (AppComponent.pages · contains) + for (final x in j['components'] as List) '$x', // 123: 새 화면 "AI 그림" 은 예전 설정에도 한 번 켜서 넣는다 (끄기: 환경 설정 > 컴포넌트) if (j['aiImageV1'] != true && !(j['components'] as List).contains('aiimage')) 'aiimage', ] : AppComponent.defaultInstalled ..navOrder = j['navOrder'] is List - ? [for (final x in j['navOrder'] as List) if (AppComponent.defaultOrder.contains('$x')) '$x'] + ? [for (final x in j['navOrder'] as List) '$x'] // 225: 모르는 화면도 자리째 남긴다 : AppComponent.defaultOrder ..swipeNav = j['swipeNav'] as bool? ?? true ..imageExts = j['imageExts'] is List diff --git a/lib/ui/component_settings.dart b/lib/ui/component_settings.dart index e2f2e3e..b191b77 100644 --- a/lib/ui/component_settings.dart +++ b/lib/ui/component_settings.dart @@ -176,7 +176,9 @@ class ComponentSettings extends StatelessWidget { void move(int from, int to) { final ids = [for (final p in pages) p.id]; ids.insert(to, ids.removeAt(from)); - c.updateSettings((v) => v.navOrder = [...ids, ...AppComponent.defaultOrder.where((x) => !ids.contains(x))]); + // 225: 이 판이 모르는 화면 (다른 판의 Tor 등) 은 지우지 않고 그 자리에 + c.updateSettings((v) => + v.navOrder = AppComponent.reorder(v.navOrder, [...ids, ...AppComponent.defaultOrder.where((x) => !ids.contains(x))])); } return Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ diff --git a/test/components_test.dart b/test/components_test.dart index eba6650..a461b17 100644 --- a/test/components_test.dart +++ b/test/components_test.dart @@ -34,6 +34,24 @@ void main() { expect(AppSettings.fromJson({}).components, AppComponent.defaultInstalled); }); + test('225: 이 판이 모르는 컴포넌트 (다른 판의 Tor 등) 는 설정에서 지우지 않는다 · 순서를 바꿔도 그 자리에', () { + final s = AppSettings.fromJson({ + 'components': ['mkv', 'tor', 'explorer'], + 'navOrder': ['mkv', 'tor', 'explorer', 'browser'], + 'aiImageV1': true, + }); + expect(s.components, ['mkv', 'tor', 'explorer'], reason: '모르는 tor 도 남김'); + expect(s.navOrder, ['mkv', 'tor', 'explorer', 'browser']); + // 화면은 아는 것만 + expect([for (final p in AppComponent.pages(s.components, s.navOrder)) p.id], ['mkv', 'explorer']); + final back = AppSettings.fromJson(s.toJson()); + expect([back.components, back.navOrder], [s.components, s.navOrder], reason: '저장해도 남음'); + // 순서 바꾸기 (explorer 를 맨 앞으로): tor 는 둘째 자리 그대로 + expect(AppComponent.reorder(s.navOrder, ['explorer', 'mkv', 'browser']), ['explorer', 'tor', 'mkv', 'browser']); + // 예전 순서에 없던 아는 화면은 뒤에 + expect(AppComponent.reorder(['tor', 'mkv'], ['mkv', 'explorer']), ['tor', 'mkv', 'explorer']); + }); + testWidgets('위쪽 이동 버튼: 설치한 화면만 · 설정을 바꾸면 바로 · 하나뿐이면 그것만', (tester) async { tester.view.physicalSize = const Size(1500, 900); tester.view.devicePixelRatio = 1;