diff --git a/assets/l10n/en.json b/assets/l10n/en.json index 9471eeb..16726a3 100644 --- a/assets/l10n/en.json +++ b/assets/l10n/en.json @@ -2392,6 +2392,7 @@ "읽기 전용 · 보기와 받기만 됩니다": "Read-only · view and download only", "읽기 전용 공유입니다 (보기 · 받기만)": "This share is read-only (view and download only)", "읽기 전용 매체입니다": "Read-only media", + "읽기 전용이라 옮길 수 없습니다 - 복사를 쓰세요": "It is read-only, so it cannot be moved - use Copy", "읽기 전용입니다: 보기와 받기만 되고, 올리기 · 지우기 · 이름 바꾸기는 되지 않습니다. 이 폴더 밖과 링크는 열리지 않습니다.": "Read-only: files can be viewed and downloaded, but not uploaded, deleted or renamed. Anything outside this folder and links cannot be opened.", "읽는 중 · 폴더 {0}개 · {1}": "Reading · {0} folders · {1}", "읽을 수 없습니다": "Cannot read", diff --git a/assets/l10n/ja.json b/assets/l10n/ja.json index 1cda6aa..06098d9 100644 --- a/assets/l10n/ja.json +++ b/assets/l10n/ja.json @@ -2392,6 +2392,7 @@ "읽기 전용 · 보기와 받기만 됩니다": "読み取り専用 · 表示とダウンロードのみ", "읽기 전용 공유입니다 (보기 · 받기만)": "読み取り専用の共有です (表示 · ダウンロードのみ)", "읽기 전용 매체입니다": "読み取り専用メディアです", + "읽기 전용이라 옮길 수 없습니다 - 복사를 쓰세요": "読み取り専用のため移動できません - コピーを使ってください", "읽기 전용입니다: 보기와 받기만 되고, 올리기 · 지우기 · 이름 바꾸기는 되지 않습니다. 이 폴더 밖과 링크는 열리지 않습니다.": "読み取り専用です: 表示とダウンロードのみで、アップロード · 削除 · 名前の変更はできません。このフォルダーの外とリンクは開けません。", "읽는 중 · 폴더 {0}개 · {1}": "読み込み中 · フォルダー {0} 件 · {1}", "읽을 수 없습니다": "読み込めません", diff --git a/assets/l10n/zh-Hans.json b/assets/l10n/zh-Hans.json index 0016fef..46a3752 100644 --- a/assets/l10n/zh-Hans.json +++ b/assets/l10n/zh-Hans.json @@ -2392,6 +2392,7 @@ "읽기 전용 · 보기와 받기만 됩니다": "只读 · 仅可查看和下载", "읽기 전용 공유입니다 (보기 · 받기만)": "这是只读共享 (仅可查看和下载)", "읽기 전용 매체입니다": "只读介质", + "읽기 전용이라 옮길 수 없습니다 - 복사를 쓰세요": "此处为只读,无法移动 - 请使用复制", "읽기 전용입니다: 보기와 받기만 되고, 올리기 · 지우기 · 이름 바꾸기는 되지 않습니다. 이 폴더 밖과 링크는 열리지 않습니다.": "只读:只能查看和下载,不能上传、删除或重命名。无法打开此文件夹之外的内容和链接。", "읽는 중 · 폴더 {0}개 · {1}": "正在读取 · {0} 个文件夹 · {1}", "읽을 수 없습니다": "无法读取", diff --git a/lib/ui/explorer_page.dart b/lib/ui/explorer_page.dart index 3854211..a1952bc 100644 --- a/lib/ui/explorer_page.dart +++ b/lib/ui/explorer_page.dart @@ -2596,6 +2596,14 @@ class _ExplorerPageState extends State { if (done != null && mounted) await _refreshAll([done]); } + /// 관리자 지시 (10/21, 28 지적): 시스템 폴더 · 루트 (shz://) 가 원본이나 받는 곳인 이동은 시작하지 않는다 - + /// 복사는 되고 원본 지우기에서 오류로 끝나던 것 (끌어 놓기 · 붙여넣기 · 버튼 · 메뉴 모두 여기를 지남). 막았으면 true + bool _blockShizukuMove(List sources, String? dest, {required bool move}) { + if (!move || !(sources.any(isShizuku) || (dest != null && isShizuku(dest)))) return false; + _snack(tr('읽기 전용이라 옮길 수 없습니다 - 복사를 쓰세요')); + return true; + } + /// 압축 안을 바꾸려 하면 막고 알린다 (읽기 전용). 막았으면 true /// [self] 면 압축 파일 자신도 (그 안으로 넣는 곳일 때) bool _blockArchive(List paths, {bool self = false}) { @@ -3266,6 +3274,7 @@ class _ExplorerPageState extends State { /// 다른 창의 지금 폴더로 복사 · 이동 (진행 창 · 취소) /// 복사 · 이동 버튼: 두 창이면 다른 창의 지금 폴더로, 그 밖의 배치는 담아 두기 (붙여넣기로) Future _transfer(_Pane pane, List sources, {required bool move}) async { + if (_blockShizukuMove(sources, null, move: move)) return; // X-plore 비교 f: 압축 안의 것 - 복사는 꺼내기 (86 의 풀기 검사 그대로), 이동은 막음 if (sources.any(_insideArchive)) { if (move) { @@ -3316,6 +3325,7 @@ class _ExplorerPageState extends State { } void _toClipboard(List sources, {required bool move}) { + if (_blockShizukuMove(sources, null, move: move)) return; if (sources.isEmpty) { _snack(_pickFirst(move)); return; @@ -3401,6 +3411,7 @@ class _ExplorerPageState extends State { _snack(_pickFirst(move)); return false; } + if (_blockShizukuMove(sources, dest, move: move)) return false; // X-plore 비교 f: 압축 안으로는 넣지 않고 (읽기 전용), 압축 안의 것을 붙여넣으면 꺼내기로 if (_blockArchive([dest], self: true)) return false; if (sources.any(_insideArchive)) { diff --git a/test/shizuku_test.dart b/test/shizuku_test.dart index d3dfe20..945d292 100644 --- a/test/shizuku_test.dart +++ b/test/shizuku_test.dart @@ -165,6 +165,14 @@ void main() { await t.tap(find.text('이름 변경').first); await wait(t, () => false); expect(find.byType(TextField), findsNothing, reason: '이름 바꾸기 창도 열리지 않음'); + // 28 지적 · 관리자 지시 (10/21): 이동은 시작 전에 막음 (예전에는 복사는 되고 원본 지우기에서 오류) + ScaffoldMessenger.of(t.element(find.byType(ExplorerPage))).clearSnackBars(); + await wait(t, () => false); + await t.tap(find.text('이동').first); + await wait(t, () => find.textContaining('옮길 수 없습니다').evaluate().isNotEmpty); + expect(find.text('읽기 전용이라 옮길 수 없습니다 - 복사를 쓰세요'), findsOneWidget); + expect(find.textContaining('담았습니다'), findsNothing, reason: '이동으로 담지도 않음'); + expect(fake.calls.where((x) => x.startsWith('copy:')), isEmpty); // 다시 열기: 경고 없이 (이번 실행에서 이미 봄) await t.pumpWidget(const SizedBox()); await t.runAsync(() => t.pumpWidget(MaterialApp(home: ExplorerPage(key: UniqueKey(), c: ctl()))));