모니터링 [실행] 뒤 옵션 칸 포커스 풀기 · 실패 글에 예외 이름 없이 (236 · 237)
- 236 (버그, 기기 확인): 실행 뒤 포커스가 옵션 칸에 남아 키보드가 올라와 끝 알림을 가렸다 - 237 (사용자 편의, 기기 확인): 카드 실패 글에서 'ProcessException:' · Command 줄을 뺌 (userErrorText). 원문은 작업 기록에 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>fix_mkv
parent
90246717ec
commit
41d4d8afbf
|
|
@ -142,7 +142,7 @@ class CopyCenter extends ChangeNotifier {
|
||||||
if (job.leftInSource > 0) trf('{0}개는 원본에 남음 (건너뜀)', [job.leftInSource]),
|
if (job.leftInSource > 0) trf('{0}개는 원본에 남음 (건너뜀)', [job.leftInSource]),
|
||||||
if (job.pruned > 0) trf('빈 폴더 {0}개 지움', [job.pruned]),
|
if (job.pruned > 0) trf('빈 폴더 {0}개 지움', [job.pruned]),
|
||||||
].join(' · ')
|
].join(' · ')
|
||||||
: '$e',
|
: userErrorText(e), // 237: 예외 이름 없이 (원래 글은 작업 기록에)
|
||||||
lastFiles: job.doneFiles,
|
lastFiles: job.doneFiles,
|
||||||
lastLeft: e == null ? job.leftInSource : 0,
|
lastLeft: e == null ? job.leftInSource : 0,
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,15 @@ import '../l10n/tr.dart';
|
||||||
/// - 아래쪽: 지금 항목 (폴더) 의 파일 중 몇 개를 했는지
|
/// - 아래쪽: 지금 항목 (폴더) 의 파일 중 몇 개를 했는지
|
||||||
/// 215: rsync 가 실패했을 때 보일 글 - 원본을 못 지웠으면 쉬운 말 한 줄, 그리고 오류 · 권한 줄을 끝쪽부터 (전송한 파일 이름이 아니라).
|
/// 215: rsync 가 실패했을 때 보일 글 - 원본을 못 지웠으면 쉬운 말 한 줄, 그리고 오류 · 권한 줄을 끝쪽부터 (전송한 파일 이름이 아니라).
|
||||||
/// 예전에는 출력의 앞쪽 8줄이라 파일 이름만 보이고 "Permission denied" · "rsync error … (code 23)" 가 빠지기 쉬웠다
|
/// 예전에는 출력의 앞쪽 8줄이라 파일 이름만 보이고 "Permission denied" · "rsync error … (code 23)" 가 빠지기 쉬웠다
|
||||||
|
/// 237: 사용자에게 보이는 실패 글 - "ProcessException:" 같은 예외 이름 · "Command:" 줄은 빼고 내용만.
|
||||||
|
/// 원래 글 ('$e') 은 작업 기록에 그대로 남긴다
|
||||||
|
String userErrorText(Object e) => switch (e) {
|
||||||
|
ProcessException(:final message) when message.isNotEmpty => message,
|
||||||
|
FileSystemException(:final message, :final path, :final osError) =>
|
||||||
|
[message, if (path != null && path.isNotEmpty) path, if (osError != null && osError.message.isNotEmpty) osError.message].join(' · '),
|
||||||
|
_ => '$e'.replaceFirst(RegExp(r'^\w*(Exception|Error)(\([^)]*\))?: '), ''),
|
||||||
|
};
|
||||||
|
|
||||||
String rsyncFailureText(int code, List<String> errLines, List<String> allLines) {
|
String rsyncFailureText(int code, List<String> errLines, List<String> allLines) {
|
||||||
final all = [...errLines, ...allLines];
|
final all = [...errLines, ...allLines];
|
||||||
final removeFailed = all.any((l) {
|
final removeFailed = all.any((l) {
|
||||||
|
|
|
||||||
|
|
@ -333,6 +333,8 @@ class _CopyCardState extends State<_CopyCard> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _run(CopyTask t) async {
|
Future<void> _run(CopyTask t) async {
|
||||||
|
// 236: 옵션 칸의 포커스를 푼다 - 남아 있으면 폰 · 태블릿에서 키보드가 올라와 끝 알림을 가린다
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus();
|
||||||
// 104: 지우기가 들어 있으면 Rsync 화면과 같은 미리 보기를 거친다 (그사이 대상에 생긴 파일을 묻지 않고 지우지 않게).
|
// 104: 지우기가 들어 있으면 Rsync 화면과 같은 미리 보기를 거친다 (그사이 대상에 생긴 파일을 묻지 않고 지우지 않게).
|
||||||
// 210: 이동 (원본 지우기) 도 - 카드의 [실행] 은 따로 묻는 창이 없어 곧바로 원본을 지웠다. 복사만은 지금처럼 바로
|
// 210: 이동 (원본 지우기) 도 - 카드의 [실행] 은 따로 묻는 창이 없어 곧바로 원본을 지웠다. 복사만은 지금처럼 바로
|
||||||
if (!await confirmDeletingRun(context, t, includeMove: true, skipNewer: widget.c.settings.copySkipNewer) || !mounted) return;
|
if (!await confirmDeletingRun(context, t, includeMove: true, skipNewer: widget.c.settings.copySkipNewer) || !mounted) return;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import 'package:jj_mkvmaker/app/app_controller.dart';
|
||||||
import 'package:jj_mkvmaker/app/copy_center.dart';
|
import 'package:jj_mkvmaker/app/copy_center.dart';
|
||||||
import 'package:jj_mkvmaker/app/live_sync.dart';
|
import 'package:jj_mkvmaker/app/live_sync.dart';
|
||||||
import 'package:jj_mkvmaker/app/settings.dart';
|
import 'package:jj_mkvmaker/app/settings.dart';
|
||||||
|
import 'package:jj_mkvmaker/app/transfer_job.dart' show userErrorText;
|
||||||
import 'package:jj_mkvmaker/platform/windows/desktop_storage_service.dart';
|
import 'package:jj_mkvmaker/platform/windows/desktop_storage_service.dart';
|
||||||
import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart';
|
import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart';
|
||||||
import 'package:jj_mkvmaker/services/platform_services.dart';
|
import 'package:jj_mkvmaker/services/platform_services.dart';
|
||||||
|
|
@ -217,6 +218,36 @@ void main() {
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('236: 모니터링 카드 [실행] 뒤 옵션 칸에 포커스가 남지 않는다 (키보드가 저절로 올라와 끝 알림을 가리지 않게)', skip: !Platform.isWindows, (tester) async {
|
||||||
|
final src = p.join(tmp.path, 'cp'), dst = p.join(tmp.path, 'cpd');
|
||||||
|
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, method: 'robocopy', options: '/E')];
|
||||||
|
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.showKeyboard(find.widgetWithText(TextField, '/E')); // 옵션 칸을 눌러 고치는 중 (현재 방식은 칸이 꺼져 있어 robocopy)
|
||||||
|
await tester.pump();
|
||||||
|
expect(tester.widget<EditableText>(find.byType(EditableText).first).focusNode.hasFocus, isTrue);
|
||||||
|
// 고치지 않았으니 묻지 않고 바로 (복사, 지우기 없음)
|
||||||
|
await tester.tap(find.widgetWithText(FilledButton, '실행').first);
|
||||||
|
await tester.pump();
|
||||||
|
expect(tester.widget<EditableText>(find.byType(EditableText).first).focusNode.hasFocus, isFalse);
|
||||||
|
CopyCenter.of(c).jobs['1']?.cancel();
|
||||||
|
await settle(tester, () => CopyCenter.of(c).jobs['1']?.finished ?? true, rounds: 200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('237: 사용자에게 보이는 실패 글에는 예외 이름 · 명령 줄이 없다', () {
|
||||||
|
expect(userErrorText(const ProcessException('rsync', ['-a'], '원본 파일을 지우지 못했습니다', 23)), '원본 파일을 지우지 못했습니다');
|
||||||
|
expect(userErrorText(const FileSystemException('지울 수 없음', r'C:\x', OSError('액세스가 거부되었습니다', 5))), r'지울 수 없음 · C:\x · 액세스가 거부되었습니다');
|
||||||
|
expect(userErrorText(Exception('끊김')), '끊김');
|
||||||
|
expect(userErrorText('그냥 글'), '그냥 글');
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('105: 원본이 대상 안 + 창에서 [원본 파일 지우기] 로 지우기 옵션이 생기면 막는다', (tester) async {
|
testWidgets('105: 원본이 대상 안 + 창에서 [원본 파일 지우기] 로 지우기 옵션이 생기면 막는다', (tester) async {
|
||||||
final outer = p.join(tmp.path, 'O'), inner = p.join(outer, 'in');
|
final outer = p.join(tmp.path, 'O'), inner = p.join(outer, 'in');
|
||||||
File(p.join(inner, 'n.txt'))
|
File(p.join(inner, 'n.txt'))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue