diff --git a/lib/ui/explorer_page.dart b/lib/ui/explorer_page.dart index 8e17a52..c0665dd 100644 --- a/lib/ui/explorer_page.dart +++ b/lib/ui/explorer_page.dart @@ -313,9 +313,36 @@ class _ExplorerPageState extends State { for (final pane in _panes) { pane.addListener(_onPane); } + _resumeReload = AppLifecycleListener(onResume: () => unawaited(_reloadOpen(localOnly: true))); _init(); } + /// 232: 앱으로 돌아오면 (다른 앱 · 탐색기에서 파일을 바꾸고 오면) 보이는 폴더를 다시 읽는다 + late final AppLifecycleListener _resumeReload; + bool _reloading = false; + + /// 열려 있는 (보이는) 폴더를 두 창 모두 다시 읽는다 - [새로 고침] (183) 과 같다. + /// [localOnly]: 돌아올 때는 WebDAV 폴더는 빼고 (네트워크가 아직 안 붙었을 때 오류가 뜨지 않게 - [새로 고침] 으로는 읽음) + Future _reloadOpen({bool localOnly = false}) async { + if (_reloading) return; + _reloading = true; + try { + // 180: 목록을 먼저 비우지 않고 새로 읽은 것으로 바꾼다 (비우면 목록이 사라졌다 생겨 스크롤 · 자리가 바뀐다) + for (final x in _panes) { + final open = {...x.expanded, x.current}; + for (final d in open) { + if (!mounted) return; + if (localOnly && isDav(d)) continue; + await _load(x, d, force: true); + } + x.cache.removeWhere((k, _) => !open.contains(k)); + x.changed(); + } + } finally { + _reloading = false; + } + } + @override void dispose() { if (widget.rsync) { @@ -328,6 +355,7 @@ class _ExplorerPageState extends State { pane.dispose(); } if (_allFiles != null) _life.dispose(); + _resumeReload.dispose(); super.dispose(); } @@ -1372,16 +1400,8 @@ class _ExplorerPageState extends State { case ExplorerButton.both: await _runRsync(right: true, left: true); case ExplorerButton.refresh: - // 183: 두 창 모두 (밖에서 지운 파일이 다른 창에 남지 않게). - // 180: 목록을 먼저 비우지 않고 새로 읽은 것으로 바꾼다 (비우면 목록이 사라졌다 생겨 스크롤 · 자리가 바뀐다) - for (final x in _panes) { - final open = {...x.expanded, x.current}; - for (final d in open) { - await _load(x, d, force: true); - } - x.cache.removeWhere((k, _) => !open.contains(k)); - x.changed(); - } + // 183: 두 창 모두 (밖에서 지운 파일이 다른 창에 남지 않게) + await _reloadOpen(); case ExplorerButton.play: final t = _targets(pane); final videos = t.where((x) => !vIsDirSync(x) && isVideoFile(x)).toList(); diff --git a/test/explorer_steady_test.dart b/test/explorer_steady_test.dart index 984fdf9..eb925b6 100644 --- a/test/explorer_steady_test.dart +++ b/test/explorer_steady_test.dart @@ -156,6 +156,32 @@ void main() { await settle(tester, () => find.text('f03.txt').evaluate().isEmpty); expect(find.text('f03.txt'), findsNothing); }); + + testWidgets('232: 앱으로 돌아오면 보이는 폴더를 다시 읽는다 (다른 앱에서 지우고 · 만든 것이 바로 보이게)', (tester) async { + c.settings + ..explorerLayout = 'dual' + ..explorerPaths = [left, left]; + tester.view.physicalSize = const Size(1280, 800); + tester.view.devicePixelRatio = 1; + addTearDown(tester.view.reset); + await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c)))); + await settle(tester, () => find.text('f03.txt').evaluate().length == 2); + // 다른 앱으로 갔다가 (그사이 밖에서 지우고 만듦) + tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.inactive); + tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.hidden); + await tester.runAsync(() async { + await File(p.join(left, 'f03.txt')).delete(); + await File(p.join(left, '0_made_outside.txt')).writeAsString('m'); // 맨 위에 보이게 (목록이 길다) + }); + await settle(tester, () => false, rounds: 5); + expect(find.text('f03.txt'), findsNWidgets(2), reason: '돌아오기 전에는 그대로'); + // 돌아옴 + tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.inactive); + tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed); + await settle(tester, () => find.text('f03.txt').evaluate().isEmpty && find.text('0_made_outside.txt').evaluate().length == 2); + expect(find.text('f03.txt'), findsNothing); + expect(find.text('0_made_outside.txt'), findsNWidgets(2)); + }); } Finder markOf(String name) => find.descendant(