From 1c919ce8c6b8ec4eeec4256b2c0dca5daf94416d Mon Sep 17 00:00:00 2001 From: RyuJongJin <266630912+RyuJongJin@users.noreply.github.com> Date: Fri, 9 Oct 2026 18:41:03 +0900 Subject: [PATCH] =?UTF-8?q?52=20=EB=B3=B5=EC=82=AC=20=C2=B7=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EC=A7=84=ED=96=89=20=EB=A7=89=EB=8C=80:=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=EC=9D=84=20=EB=96=A0=EB=82=AC=EB=8B=A4=20?= =?UTF-8?q?=EB=8F=8C=EC=95=84=EC=99=80=EB=8F=84=20=EB=8B=A4=EC=8B=9C=20?= =?UTF-8?q?=EB=B3=B4=EC=9D=B4=EA=B3=A0,=20=EB=81=9D=EB=82=98=EB=A9=B4=20?= =?UTF-8?q?=EB=B0=9B=EB=8A=94=20=ED=8F=B4=EB=8D=94=EB=A5=BC=20=EC=83=88?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=BD=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 화면을 열 때 CopyCenter 에서 아직 도는 작업을 찾아 진행 막대를 다시 붙이고, 끝나면 받는 · 원본 폴더를 새로 읽은 뒤 내린다 (.jjpart 가 남아 보이지 않게) - 시험: 속도 제한으로 오래 걸리는 복사 도중 화면을 없앴다가 다시 열기 (수정 전 코드에서는 실패) Co-Authored-By: Claude Opus 5.5 --- lib/ui/explorer_page.dart | 22 ++++++++++++++++++++++ test/explorer_conflict_test.dart | 27 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/lib/ui/explorer_page.dart b/lib/ui/explorer_page.dart index 60602e7..33e7fba 100644 --- a/lib/ui/explorer_page.dart +++ b/lib/ui/explorer_page.dart @@ -285,6 +285,13 @@ class _ExplorerPageState extends State { } if (!mounted) return; setState(() => _ready = true); + // 52: 다른 화면에 다녀와도 아직 도는 복사 · 이동의 진행 막대를 다시 보이고, 끝나면 그 폴더들을 새로 읽는다 + final center = CopyCenter.peekOf(c); + if (center != null) { + for (final job in center.jobs.values.where((j) => !j.finished)) { + _adoptJob(job); + } + } // 165: 드라이브 종류 · 연결은 드라이브마다 따로 뒤에서 (느린 드라이브가 있어도 다른 드라이브 · 창은 바로) if (Platform.isWindows) { for (final (path, _) in _local) { @@ -1446,6 +1453,21 @@ class _ExplorerPageState extends State { /// (Rsync 화면의 ⇄ 는 둘이 함께) List _jobs = const []; + /// 52: 이 화면을 떠난 사이에 시작된 (또는 앞의 화면이 시작한) 작업을 다시 보이고, 끝나면 새로 읽고 막대를 내린다 + void _adoptJob(TransferJob job) { + if (_jobs.contains(job)) return; + setState(() => _jobs = [..._jobs, job]); + unawaited(job.done.then((_) async { + if (!mounted) return; + await _refreshAll([job.dest, ...job.sources.map(vDirname)]); + for (final x in _panes) { + x.changed(); + } + await Future.delayed(const Duration(milliseconds: 1200)); + if (mounted && _jobs.contains(job)) setState(() => _jobs = [for (final j in _jobs) if (j != job) j]); + })); + } + /// 한쪽이라도 WebDAV 면 rsync 대신 앱이 직접 맞춘다 (rsync 는 WebDAV 를 모름) bool _viaDav(Iterable paths) => paths.any(isDav); diff --git a/test/explorer_conflict_test.dart b/test/explorer_conflict_test.dart index c1e90d8..44cbbee 100644 --- a/test/explorer_conflict_test.dart +++ b/test/explorer_conflict_test.dart @@ -105,6 +105,33 @@ void main() { expect(Directory(p.join(right, 'left')).existsSync(), isFalse); }); + testWidgets('52: 복사 중에 화면을 떠났다 돌아와도 진행 막대가 다시 보이고, 끝나면 받는 폴더를 새로 읽는다 (.jjpart 가 남아 보이지 않음)', (tester) async { + File(p.join(left, 'big.bin')).writeAsBytesSync(List.filled(150 * 1024, 3)); + c.settings.copyBandwidthKBps = 60; // 약 2.5초 걸리게 + tester.view.physicalSize = const Size(1600, 900); + tester.view.devicePixelRatio = 1; + addTearDown(tester.view.reset); + await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c)))); + await settle(tester, () => find.text('big.bin').evaluate().isNotEmpty); + await tester.runAsync(() => tester.tap(find.text('big.bin'))); + await settle(tester, () => false, rounds: 10); + await tester.runAsync(() => tester.tap(find.text('복사').first)); + await tester.pump(); + await tester.runAsync(() => tester.tap(find.widgetWithText(FilledButton, '복사'))); + await settle(tester, () => find.byType(LinearProgressIndicator).evaluate().isNotEmpty); + // 다른 화면으로 (탐색기 화면이 없어짐) → 돌아옴 + await tester.runAsync(() => tester.pumpWidget(const MaterialApp(home: Scaffold(body: Text('다른 화면'))))); + await tester.pump(); + await tester.runAsync(() => tester.pumpWidget(MaterialApp(home: ExplorerPage(c: c)))); + await settle(tester, () => find.byType(LinearProgressIndicator).evaluate().isNotEmpty, rounds: 30); + expect(find.byType(LinearProgressIndicator), findsWidgets, reason: '돌아와도 진행 막대'); + // 끝나면 오른쪽 창에 big.bin 이 보이고 .jjpart 는 없다 + await settle(tester, () => File(p.join(right, 'big.bin')).existsSync() && find.text('big.bin').evaluate().length >= 2, + rounds: 300); + expect(find.text('big.bin'), findsNWidgets(2)); + expect(find.textContaining('.jjpart'), findsNothing); + }); + testWidgets('48: 건너뛰기를 고르면 그대로 두고 건너뛴 것을 알린다', (tester) async { await startCopy(tester); await tester.tap(find.text('건너뛰기'));