import 'dart:async'; import 'package:flutter/foundation.dart'; import '../../app/transfer_job.dart'; import '../../core/file_ops.dart' show samePath; import '../../core/vfs.dart' show vBasename, vDirname, vExists, vJoin; import 'explorer_controller.dart'; /// 약속 (E5d-5): 지금 도는 복사 · 이동 · rsync 작업 (아래에서 올라오는 진행 패널의 목록). 23: 여럿이 함께 돈다. /// 끝난 것은 진행 막대가 잠깐 100% 를 보인 뒤 ([dropDelay]) 목록에서 내려간다. 바뀌면 알림 class ExplorerJobs extends ChangeNotifier { ExplorerJobs(this.ctl); final ExplorerController ctl; static const dropDelay = Duration(milliseconds: 1200); List list = const []; bool get isEmpty => list.isEmpty; /// 새 작업을 목록에 (끝난 것은 이때 뺀다) void start(TransferJob job) { list = [...list.where((j) => !j.finished), job]; notifyListeners(); } /// 같은 것을 같은 곳으로 복사 · 이동하는 중인지 (그것만 막는다) bool busyWith(List sources, String dest) => list.any((j) => !j.finished && samePath(j.dest, dest) && j.sources.toSet().containsAll(sources)); /// [jobs] 를 잠깐 뒤 목록에서 뺀다 Future dropLater(Iterable jobs) async { await Future.delayed(dropDelay); if (!ctl.host.alive || !list.any(jobs.contains)) return; list = [for (final j in list) if (!jobs.contains(j)) j]; notifyListeners(); } /// X-plore 숨기기: 옮긴 것은 숨긴 목록도 새 자리로 (원래 자리에 없고 새 자리에 있는 것만 - 같은 이름 바꾸기 · 실패는 그대로) Future followMoved(TransferJob job) async { if (!job.move || job.contents || ctl.c.settings.explorerHiddenItems.isEmpty) return; final moved = <(String, String)>[]; for (final s in job.sources) { final to = vJoin(job.dest, vBasename(s)); if (!await vExists(s) && await vExists(to)) moved.add((s, to)); } await ctl.followHidden(moved: moved); } /// 52: 이 화면을 떠난 사이에 시작된 (또는 앞의 화면이 시작한) 작업을 다시 보이고, 끝나면 새로 읽고 막대를 내린다 void adopt(TransferJob job) { if (list.contains(job)) return; list = [...list, job]; notifyListeners(); unawaited(job.done.then((_) async { if (!ctl.host.alive) return; await followMoved(job); if (!ctl.host.alive) return; await ctl.refreshAll([job.dest, ...job.sources.map(vDirname)]); for (final x in ctl.panes) { x.changed(); } await dropLater([job]); })); } }