JJ_MKVMaker/lib/ui/monitor_page.dart

149 lines
6.0 KiB
Dart

import 'package:flutter/material.dart';
import '../app/app_controller.dart';
import '../app/live_sync.dart';
import '../app/settings.dart';
import '../l10n/tr.dart';
import 'app_actions.dart';
import 'copy_sync_settings.dart' show addLiveSyncPairDialog, LiveSyncRunOptions;
import 'monitor/copy_task_card.dart';
import 'monitor/live_pair_card.dart';
import 'monitor/live_sync_flow.dart';
import 'theme.dart';
// 모니터링의 부품은 lib/ui/monitor/ 의 모듈에 있다. 예전부터 이 파일에서 가져다 쓰던 이름은 그대로 내보낸다.
export 'monitor/live_sync_flow.dart' show confirmLiveDeletes, confirmLiveDeleteNow, confirmEmptySourceSync, askLiveSyncStart;
export 'monitor/transfer_widgets.dart' show TransferProgressLines, PruneChoice, pruneLabel;
/// 모니터링 (파일 탐색기 > 모니터링): 복사 · rsync 와 lsync (실시간 동기화) 를 따로 보여 준다.
class MonitorPage extends StatelessWidget {
final AppController c;
final int initialTab;
const MonitorPage({super.key, required this.c, this.initialTab = 0});
static Future<void> open(BuildContext context, AppController c, {int tab = 0}) => Navigator.of(context)
.push(MaterialPageRoute<void>(builder: (_) => MonitorPage(c: c, initialTab: tab)));
@override
Widget build(BuildContext context) => DefaultTabController(
length: 2,
initialIndex: initialTab,
child: Scaffold(
body: Column(children: [
Builder(builder: (context) {
// 좁은 화면: 둘째 줄 (높이 48) 에 맞게 탭에 아이콘을 옆으로
final compact = isCompact(context);
Tab tab(IconData icon, String text) => compact
? Tab(height: 46, child: Row(mainAxisSize: MainAxisSize.min, children: [Icon(icon, size: 16), const SizedBox(width: 6), Text(text)]))
: Tab(icon: Icon(icon, size: 18), text: text);
return AppTopBar(
nav: const AppNavButtons(),
actions: AppActions(c: c),
middle: Row(children: [
Text(tr('모니터링'), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
const SizedBox(width: 16),
Expanded(
child: TabBar(
isScrollable: true,
tabAlignment: TabAlignment.start,
tabs: [
tab(Icons.copy_all_outlined, tr('복사 · rsync')),
tab(Icons.sync, tr('lsync (실시간 동기화)')),
],
),
),
]),
);
}),
Expanded(
child: TabBarView(children: [
CopyTaskList(c: c),
_LiveTab(c: c),
]),
),
]),
),
);
}
// ───────── lsync (실시간 동기화) ─────────
class _LiveTab extends StatefulWidget {
final AppController c;
const _LiveTab({required this.c});
@override
State<_LiveTab> createState() => _LiveTabState();
}
class _LiveTabState extends State<_LiveTab> {
AppController get c => widget.c;
@override
void initState() {
super.initState();
// 열 때 맞출 것을 새로 센다
final live = LiveSync.instance;
if (live != null) {
for (final x in c.settings.copy.liveSyncPairs) {
live.refreshPending(x);
}
}
}
/// 최종 정리를 시작했으면 진행을 볼 수 있게 복사 · rsync 탭으로
Future<void> _finalize(LiveSyncPair x) async {
if (await finalizeLiveSync(context, c, x) && mounted) DefaultTabController.of(context).animateTo(0);
}
@override
Widget build(BuildContext context) {
final live = LiveSync.instance;
return ListenableBuilder(
listenable: Listenable.merge([c, ?live]),
builder: (context, _) {
final pairs = c.settings.copy.liveSyncPairs;
return ListView(padding: const EdgeInsets.all(12), children: [
Row(children: [
Expanded(
child: Text(
tr('원본이 바뀌면 대상에 맞춥니다. 일정을 정하면 그 시간에만 맞추고, 그 밖의 시간에는 바뀐 것을 모아 두었다가 다음 시작 시간에 맞춥니다.'),
style: TextStyle(fontSize: 12, color: JjColors.textDim),
),
),
TextButton.icon(onPressed: () => addLiveSyncPairDialog(context, c), icon: const Icon(Icons.add), label: Text(tr('추가'))),
]),
// 백그라운드로 실행 · 다시 켤 때 동기화 (환경 설정과 같은 값) · 모두 시작 / 멈춤
Wrap(spacing: 12, runSpacing: 4, crossAxisAlignment: WrapCrossAlignment.center, children: [
LiveSyncRunOptions(c: c, dense: true),
if (live != null && pairs.any((x) => x.enabled))
live.paused.isEmpty
? TextButton.icon(
onPressed: live.pauseAll, icon: const Icon(Icons.pause, size: 18), label: Text(tr('모두 멈춤')))
: FilledButton.tonalIcon(
onPressed: live.resumeAll,
icon: const Icon(Icons.play_arrow, size: 18),
label: Text(trf('모두 시작 (멈춘 것 {0}개)', [pairs.where(live.isPaused).length]))),
]),
const SizedBox(height: 8),
if (pairs.isEmpty)
Padding(
padding: const EdgeInsets.all(24),
child: Center(child: Text(tr('실시간 동기화가 없습니다. [추가] 하거나 복사 · rsync 탭에서 [lsync 로 이동] 하세요.'),
style: TextStyle(color: JjColors.textDim))),
),
for (final x in pairs)
LivePairCard(
c: c,
live: live,
pair: x,
onFinalize: () => _finalize(x),
onShowCopyList: () => DefaultTabController.of(context).animateTo(0),
),
]);
},
);
}
}