모니터링 [실행] 의 이동도 미리 보기 · 확인 (210)
관리자 지시 (데이터): 모니터링 카드의 [실행] 은 따로 묻는 창이 없어 이동 (원본 지우기) 을 곧바로 했다. 이동이면 "원본을 지우는 작업입니다 (이동)" 창에 옮길 목록을 보이고, 목록이 나온 뒤에만 [실행]. 복사만은 지금처럼 바로. 탐색기 이동 · Rsync 화면 · [최종 정리] 는 이미 자기 확인 창이 있어 두 번 묻지 않는다. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>fix_mkv
parent
cd00d3ce3a
commit
954b86b84c
|
|
@ -1358,6 +1358,8 @@
|
|||
"원본으로": "Back to the original",
|
||||
"원본을 읽을 수 없어 멈춤": "Stopped: can't read the source",
|
||||
"원본을 읽을 수 없어 멈췄습니다": "Stopped because the source can't be read",
|
||||
"원본을 지우는 실행이라, 옮길 목록이 나온 뒤에 실행할 수 있습니다.": "This run removes source files, so it can run once the list of files to move is shown.",
|
||||
"원본을 지우는 작업입니다 (이동)": "This task removes source files (move)",
|
||||
"원본의 {0}배": "{0}× original",
|
||||
"원본의 빈 폴더 정리 중…": "Cleaning up empty folders in the source…",
|
||||
"원본의 파일을 모두 대상으로 옮기고 (원본에서 지움), 원본에 남은 빈 폴더를 지웁니다.": "Moves all files from the source to the target (deleting them from the source), then deletes the empty folders left in the source.",
|
||||
|
|
@ -1478,6 +1480,7 @@
|
|||
"이동 폴더 (환경 설정에서 바꿀 수 있음)": "Move folder (can be changed in Settings)",
|
||||
"이동 폴더로 (확인 뒤, 기본)": "To the move folder (after confirming, default)",
|
||||
"이동: {0} → {1}{2}": "Moved: {0} → {1}{2}",
|
||||
"이동: 대상에 옮긴 파일은 원본에서 지워집니다": "Move: files moved to the destination are removed from the source",
|
||||
"이동: 옮길 폴더를 골라 이동 버튼을 만듭니다 (환경 설정 > MKV 만들기 > 이동 버튼에서 여러 개 만들 수 있음)": "Move: choose a folder to create a move button (you can make several in Settings > Make MKV > Move buttons)",
|
||||
"이동: 이 동영상을 이동 폴더로 옮깁니다{0}": "Move: moves this video to the move folder{0}",
|
||||
"이동: 체크한 동영상 {0}개를 이동 폴더로 옮깁니다": "Move: moves the {0} checked videos to the move folder",
|
||||
|
|
|
|||
|
|
@ -1358,6 +1358,8 @@
|
|||
"원본으로": "元の画像で",
|
||||
"원본을 읽을 수 없어 멈춤": "元を読めないため停止",
|
||||
"원본을 읽을 수 없어 멈췄습니다": "元を読めないため停止しました",
|
||||
"원본을 지우는 실행이라, 옮길 목록이 나온 뒤에 실행할 수 있습니다.": "元のファイルを削除する実行のため、移動する一覧が表示されてから実行できます。",
|
||||
"원본을 지우는 작업입니다 (이동)": "元のファイルを削除する作業です (移動)",
|
||||
"원본의 {0}배": "元の {0}倍",
|
||||
"원본의 빈 폴더 정리 중…": "元の空フォルダーを整理中…",
|
||||
"원본의 파일을 모두 대상으로 옮기고 (원본에서 지움), 원본에 남은 빈 폴더를 지웁니다.": "元のファイルをすべて対象へ移し (元から削除)、元に残った空フォルダーを削除します。",
|
||||
|
|
@ -1478,6 +1480,7 @@
|
|||
"이동 폴더 (환경 설정에서 바꿀 수 있음)": "移動フォルダー(環境設定で変更できます)",
|
||||
"이동 폴더로 (확인 뒤, 기본)": "移動フォルダーへ (確認後、既定)",
|
||||
"이동: {0} → {1}{2}": "移動: {0} → {1}{2}",
|
||||
"이동: 대상에 옮긴 파일은 원본에서 지워집니다": "移動: 移動先に移したファイルは元の場所から削除されます",
|
||||
"이동: 옮길 폴더를 골라 이동 버튼을 만듭니다 (환경 설정 > MKV 만들기 > 이동 버튼에서 여러 개 만들 수 있음)": "移動: 移動先フォルダーを選んで移動ボタンを作ります(環境設定 > MKV 作成 > 移動ボタンで複数作れます)",
|
||||
"이동: 이 동영상을 이동 폴더로 옮깁니다{0}": "移動: この動画を移動フォルダーに移します{0}",
|
||||
"이동: 체크한 동영상 {0}개를 이동 폴더로 옮깁니다": "移動: チェックした動画 {0}個を移動フォルダーに移します",
|
||||
|
|
|
|||
|
|
@ -1358,6 +1358,8 @@
|
|||
"원본으로": "回到原图",
|
||||
"원본을 읽을 수 없어 멈춤": "无法读取源,已停止",
|
||||
"원본을 읽을 수 없어 멈췄습니다": "无法读取源,已停止",
|
||||
"원본을 지우는 실행이라, 옮길 목록이 나온 뒤에 실행할 수 있습니다.": "此操作会删除源文件,需在显示要移动的列表后才能执行。",
|
||||
"원본을 지우는 작업입니다 (이동)": "此任务会删除源文件(移动)",
|
||||
"원본의 {0}배": "原始的 {0} 倍",
|
||||
"원본의 빈 폴더 정리 중…": "正在清理源中的空文件夹…",
|
||||
"원본의 파일을 모두 대상으로 옮기고 (원본에서 지움), 원본에 남은 빈 폴더를 지웁니다.": "将源中的所有文件移到目标 (从源中删除),然后删除源中剩下的空文件夹。",
|
||||
|
|
@ -1478,6 +1480,7 @@
|
|||
"이동 폴더 (환경 설정에서 바꿀 수 있음)": "移动目标文件夹(可在设置中更改)",
|
||||
"이동 폴더로 (확인 뒤, 기본)": "移到移动文件夹 (确认后,默认)",
|
||||
"이동: {0} → {1}{2}": "已移动: {0} → {1}{2}",
|
||||
"이동: 대상에 옮긴 파일은 원본에서 지워집니다": "移动:已移到目标的文件会从源中删除",
|
||||
"이동: 옮길 폴더를 골라 이동 버튼을 만듭니다 (환경 설정 > MKV 만들기 > 이동 버튼에서 여러 개 만들 수 있음)": "移动: 选择目标文件夹以创建移动按钮(可在 设置 > 制作 MKV > 移动按钮 中创建多个)",
|
||||
"이동: 이 동영상을 이동 폴더로 옮깁니다{0}": "移动: 将此视频移到移动目标文件夹{0}",
|
||||
"이동: 체크한 동영상 {0}개를 이동 폴더로 옮깁니다": "移动: 将勾选的 {0} 个视频移到移动目标文件夹",
|
||||
|
|
|
|||
|
|
@ -332,8 +332,9 @@ class _CopyCardState extends State<_CopyCard> {
|
|||
}
|
||||
|
||||
Future<void> _run(CopyTask t) async {
|
||||
// 104: 지우기가 들어 있으면 Rsync 화면과 같은 미리 보기를 거친다 (그사이 대상에 생긴 파일을 묻지 않고 지우지 않게)
|
||||
if (!await confirmDeletingRun(context, t) || !mounted) return;
|
||||
// 104: 지우기가 들어 있으면 Rsync 화면과 같은 미리 보기를 거친다 (그사이 대상에 생긴 파일을 묻지 않고 지우지 않게).
|
||||
// 210: 이동 (원본 지우기) 도 - 카드의 [실행] 은 따로 묻는 창이 없어 곧바로 원본을 지웠다. 복사만은 지금처럼 바로
|
||||
if (!await confirmDeletingRun(context, t, includeMove: true) || !mounted) return;
|
||||
widget.c.note(trf('rsync 시작 ({0}): {1} → {2}', [tr('모니터링의 [실행]'), t.sources.join(', '), t.dest]));
|
||||
String? exe;
|
||||
if (t.method == 'rsync') {
|
||||
|
|
|
|||
|
|
@ -167,8 +167,11 @@ bool taskDeletes(CopyTask t) {
|
|||
|
||||
/// 104: 지우기가 들어 있는 작업은 어디서 실행하든 (모니터링 [실행] · 옵션 저장 뒤 실행 · 최종 정리) Rsync 화면과 같은 미리 보기를 거친다.
|
||||
/// 지울 목록이 나온 뒤에만 [실행] 이 켜진다. 지우기가 없으면 묻지 않고 true
|
||||
Future<bool> confirmDeletingRun(BuildContext context, CopyTask t) async {
|
||||
if (!taskDeletes(t)) return true;
|
||||
/// [includeMove] (210): 이동 (원본 파일 지우기) 도 미리 보기 · 확인을 거친다 - 모니터링의 [실행] 처럼 따로 묻는 창이 없을 때
|
||||
Future<bool> confirmDeletingRun(BuildContext context, CopyTask t, {bool includeMove = false}) async {
|
||||
final deletes = taskDeletes(t);
|
||||
final moves = includeMove && t.move;
|
||||
if (!deletes && !moves) return true;
|
||||
List<PreviewItem>? preview;
|
||||
Object? error;
|
||||
final update = optionsUpdate(t.options);
|
||||
|
|
@ -181,7 +184,7 @@ Future<bool> confirmDeletingRun(BuildContext context, CopyTask t) async {
|
|||
return AlertDialog(
|
||||
scrollable: true,
|
||||
actionsOverflowDirection: VerticalDirection.down, // 110: [취소] 가 먼저
|
||||
title: Text(tr('지우기가 들어 있는 작업입니다')),
|
||||
title: Text(deletes ? tr('지우기가 들어 있는 작업입니다') : tr('원본을 지우는 작업입니다 (이동)')),
|
||||
content: SizedBox(
|
||||
width: 560,
|
||||
child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
|
|
@ -191,10 +194,17 @@ Future<bool> confirmDeletingRun(BuildContext context, CopyTask t) async {
|
|||
Text('${vDisplay(s)} → ${vDisplay(dstOf(s))}', style: const TextStyle(fontSize: 11, color: JjColors.textDim)),
|
||||
],
|
||||
Text('${t.method} ${t.options}', style: const TextStyle(fontSize: 12, fontFamily: 'monospace', color: JjColors.textDim)),
|
||||
// 210: 이동이면 실행 뒤 원본에서 지워진다는 것을 분명히
|
||||
if (t.move)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(tr('이동: 대상에 옮긴 파일은 원본에서 지워집니다'),
|
||||
style: const TextStyle(fontSize: 12, color: Colors.orangeAccent)),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SyncPreviewView(
|
||||
compute: () async => [
|
||||
for (final s in t.sources) ...await previewSync(s, dstOf(s), toRight: true, delete: true, update: update),
|
||||
for (final s in t.sources) ...await previewSync(s, dstOf(s), toRight: true, delete: deletes, update: update),
|
||||
],
|
||||
onStart: () => set(() {
|
||||
preview = null;
|
||||
|
|
@ -213,7 +223,9 @@ Future<bool> confirmDeletingRun(BuildContext context, CopyTask t) async {
|
|||
? Text(
|
||||
error != null
|
||||
? tr('지우기가 들어간 실행이라, 비교하지 못하면 실행할 수 없습니다. 원본 · 대상을 확인한 뒤 [다시 비교] 를 누르세요.')
|
||||
: tr('지우기가 들어간 실행이라, 지울 목록이 나온 뒤에 실행할 수 있습니다.'),
|
||||
: deletes
|
||||
? tr('지우기가 들어간 실행이라, 지울 목록이 나온 뒤에 실행할 수 있습니다.')
|
||||
: tr('원본을 지우는 실행이라, 옮길 목록이 나온 뒤에 실행할 수 있습니다.'),
|
||||
style: const TextStyle(fontSize: 12, color: Colors.orangeAccent),
|
||||
)
|
||||
: null,
|
||||
|
|
@ -223,7 +235,7 @@ Future<bool> confirmDeletingRun(BuildContext context, CopyTask t) async {
|
|||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(ctx, false), child: Text(tr('취소'))),
|
||||
FilledButton(
|
||||
style: dels > 0 ? FilledButton.styleFrom(backgroundColor: JjColors.danger) : null,
|
||||
style: dels > 0 || moves ? FilledButton.styleFrom(backgroundColor: JjColors.danger) : null,
|
||||
onPressed: preview == null ? null : () => Navigator.pop(ctx, true),
|
||||
child: Text(tr('실행')), // 110: 글자를 바꾸지 않는다 (버튼 자리가 움직이지 않게)
|
||||
),
|
||||
|
|
|
|||
|
|
@ -167,6 +167,32 @@ void main() {
|
|||
expect(find.text('아직 실행 안 함'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('210: 모니터링 [실행] 의 이동 (원본 지우기) 도 미리 보기 · 확인을 거친다 (취소하면 원본 그대로) · 복사는 바로', (tester) async {
|
||||
final src = p.join(tmp.path, 'mv'), dst = p.join(tmp.path, 'mvd');
|
||||
final a = File(p.join(src, 'a.txt'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('a');
|
||||
Directory(dst).createSync();
|
||||
c.settings.copyTasks = [CopyTask(id: '1', sources: [src], dest: dst, contents: true, move: true, method: 'builtin')];
|
||||
tester.view.physicalSize = const Size(1500, 1000);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.reset);
|
||||
await tester.pumpWidget(MaterialApp(home: MonitorPage(c: c)));
|
||||
await tester.pump();
|
||||
await tester.tap(find.widgetWithText(FilledButton, '실행').first);
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
expect(find.text('원본을 지우는 작업입니다 (이동)'), findsOneWidget);
|
||||
expect(find.text('이동: 대상에 옮긴 파일은 원본에서 지워집니다'), findsOneWidget);
|
||||
await settle(tester, () => find.textContaining('비교하는 중').evaluate().isEmpty, rounds: 200);
|
||||
expect(find.textContaining('a.txt'), findsWidgets, reason: '옮길 목록');
|
||||
await tester.tap(find.text('취소').last);
|
||||
await tester.pumpAndSettle();
|
||||
await settle(tester, () => false, rounds: 10);
|
||||
expect(a.existsSync(), isTrue, reason: '취소하면 원본 그대로');
|
||||
expect(File(p.join(dst, 'a.txt')).existsSync(), isFalse);
|
||||
expect(find.text('아직 실행 안 함'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('105: 원본이 대상 안 + 창에서 [원본 파일 지우기] 로 지우기 옵션이 생기면 막는다', (tester) async {
|
||||
final outer = p.join(tmp.path, 'O'), inner = p.join(outer, 'in');
|
||||
File(p.join(inner, 'n.txt'))
|
||||
|
|
|
|||
Loading…
Reference in New Issue