diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index 4170a5461..10fab63d3 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -4,7 +4,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; -import 'package:flutter_hbb/consts.dart'; +import 'package:flutter_hbb/utils/event_loop.dart'; import 'package:get/get.dart'; import 'package:path/path.dart' as path; @@ -45,6 +45,7 @@ class FileModel { late final GetSessionID getSessionID; String get sessionID => getSessionID(); + late final FileDialogEventLoop evtLoop; FileModel(this.parent) { getSessionID = () => parent.target?.id ?? ""; @@ -64,14 +65,17 @@ class FileModel { jobController: jobController, fileFetcher: fileFetcher, getOtherSideDirectoryData: () => localController.directoryData()); + evtLoop = FileDialogEventLoop(); } Future onReady() async { + await evtLoop.onReady(); await localController.onReady(); await remoteController.onReady(); } Future close() async { + await evtLoop.close(); parent.target?.dialogManager.dismissAll(); await localController.close(); await remoteController.close(); @@ -90,14 +94,26 @@ class FileModel { fileFetcher.tryCompleteTask(evt['value'], evt['is_local']); } - void overrideFileConfirm(Map evt) async { - final resp = await showFileConfirmDialog( - translate("Overwrite"), "${evt['read_path']}", true); + Future postOverrideFileConfirm(Map evt) async { + evtLoop.pushEvent( + _FileDialogEvent(WeakReference(this), FileDialogType.overwrite, evt)); + } + + Future overrideFileConfirm(Map evt, + {bool? overrideConfirm, bool skip = false}) async { + // If `skip == true`, it means to skip this file without showing dialog. + // Because `resp` may be null after the user operation or the last remembered operation, + // and we should distinguish them. + final resp = overrideConfirm ?? + (!skip + ? await showFileConfirmDialog(translate("Overwrite"), + "${evt['read_path']}", true, evt['is_identical'] == "true") + : null); final id = int.tryParse(evt['id']) ?? 0; if (false == resp) { final jobIndex = jobController.getJob(id); if (jobIndex != -1) { - jobController.cancelJob(id); + await jobController.cancelJob(id); final job = jobController.jobTable[jobIndex]; job.state = JobState.done; jobController.jobTable.refresh(); @@ -111,7 +127,11 @@ class FileModel { // overwrite need_override = true; } - bind.sessionSetConfirmOverrideFile( + // Update the loop config. + if (fileConfirmCheckboxRemember) { + evtLoop.setSkip(!need_override); + } + await bind.sessionSetConfirmOverrideFile( id: sessionID, actId: id, fileNum: int.parse(evt['file_num']), @@ -119,12 +139,16 @@ class FileModel { remember: fileConfirmCheckboxRemember, isUpload: evt['is_upload'] == "true"); } + // Update the loop config. + if (fileConfirmCheckboxRemember) { + evtLoop.setOverrideConfirm(resp); + } } bool fileConfirmCheckboxRemember = false; Future showFileConfirmDialog( - String title, String content, bool showCheckbox) async { + String title, String content, bool showCheckbox, bool isIdentical) async { fileConfirmCheckboxRemember = false; return await parent.target?.dialogManager.show( (setState, Function(bool? v) close) { @@ -149,6 +173,17 @@ class FileModel { style: const TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 5), Text(content), + Offstage( + offstage: !isIdentical, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 12), + Text(translate("identical_file_tip"), + style: const TextStyle(fontWeight: FontWeight.w500)) + ], + ), + ), showCheckbox ? CheckboxListTile( contentPadding: const EdgeInsets.all(0), @@ -677,8 +712,8 @@ class JobController { debugPrint("jobError $evt"); } - void cancelJob(int id) async { - bind.sessionCancelJob(id: sessionID, actId: id); + Future cancelJob(int id) async { + await bind.sessionCancelJob(id: sessionID, actId: id); } void loadLastJob(Map evt) { @@ -1167,3 +1202,74 @@ List _sortList(List list, SortBy sortType, bool ascending) { } return []; } + +/// Define a general queue which can accepts different dialog type. +/// +/// [Visibility] +/// The `_FileDialogType` and `_DialogEvent` are invisible for other models. +enum FileDialogType { overwrite, unknown } + +class _FileDialogEvent extends BaseEvent> { + WeakReference fileModel; + bool? _overrideConfirm; + bool _skip = false; + + _FileDialogEvent(this.fileModel, super.type, super.data); + + void setOverrideConfirm(bool? confirm) { + _overrideConfirm = confirm; + } + + void setSkip(bool skip) { + _skip = skip; + } + + @override + EventCallback>? findCallback(FileDialogType type) { + final model = fileModel.target; + if (model == null) { + return null; + } + switch (type) { + case FileDialogType.overwrite: + return (data) async { + return await model.overrideFileConfirm(data, + overrideConfirm: _overrideConfirm, skip: _skip); + }; + default: + debugPrint("Unknown event type: $type with $data"); + return null; + } + } +} + +class FileDialogEventLoop + extends BaseEventLoop> { + bool? _overrideConfirm; + bool _skip = false; + + @override + Future onPreConsume( + BaseEvent> evt) async { + var event = evt as _FileDialogEvent; + event.setOverrideConfirm(_overrideConfirm); + event.setSkip(_skip); + debugPrint( + "FileDialogEventLoop: consuming"); + } + + @override + Future onEventsClear() { + _overrideConfirm = null; + _skip = false; + return super.onEventsClear(); + } + + void setOverrideConfirm(bool? confirm) { + _overrideConfirm = confirm; + } + + void setSkip(bool skip) { + _skip = skip; + } +} diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 94e28ea21..fd88a5332 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -173,7 +173,7 @@ class FfiModel with ChangeNotifier { } else if (name == 'job_error') { parent.target?.fileModel.jobController.jobError(evt); } else if (name == 'override_file_confirm') { - parent.target?.fileModel.overrideFileConfirm(evt); + parent.target?.fileModel.postOverrideFileConfirm(evt); } else if (name == 'load_last_job') { parent.target?.fileModel.jobController.loadLastJob(evt); } else if (name == 'update_folder_files') { diff --git a/flutter/lib/utils/event_loop.dart b/flutter/lib/utils/event_loop.dart new file mode 100644 index 000000000..a982cf9e2 --- /dev/null +++ b/flutter/lib/utils/event_loop.dart @@ -0,0 +1,79 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; + +typedef EventCallback = Future Function(Data data); + +abstract class BaseEvent { + EventType type; + Data data; + + /// Constructor. + BaseEvent(this.type, this.data); + + /// Consume this event. + @visibleForTesting + Future consume() async { + final cb = findCallback(type); + if (cb == null) { + return null; + } else { + return cb(data); + } + } + + EventCallback? findCallback(EventType type); +} + +abstract class BaseEventLoop { + final List> _evts = []; + Timer? _timer; + + List> get evts => _evts; + + Future onReady() async { + // Poll every 100ms. + _timer = Timer.periodic(Duration(milliseconds: 100), _handleTimer); + } + + /// An Event is about to be consumed. + Future onPreConsume(BaseEvent evt) async {} + /// An Event was consumed. + Future onPostConsume(BaseEvent evt) async {} + /// Events are all handled and cleared. + Future onEventsClear() async {} + /// Events start to consume. + Future onEventsStartConsuming() async {} + + Future _handleTimer(Timer timer) async { + if (_evts.isEmpty) { + return; + } + timer.cancel(); + _timer = null; + // Handle the logic. + await onEventsStartConsuming(); + while (_evts.isNotEmpty) { + final evt = _evts.first; + _evts.remove(evt); + await onPreConsume(evt); + await evt.consume(); + await onPostConsume(evt); + } + await onEventsClear(); + // Now events are all processed. + _timer = Timer.periodic(Duration(milliseconds: 100), _handleTimer); + } + + Future close() async { + _timer?.cancel(); + } + + void pushEvent(BaseEvent evt) { + _evts.add(evt); + } + + void clear() { + _evts.clear(); + } +} diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index be3a1e51e..0c29a5f19 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -301,6 +301,7 @@ message FileTransferDigest { uint64 last_modified = 3; uint64 file_size = 4; bool is_upload = 5; + bool is_identical = 6; } message FileTransferBlock { diff --git a/libs/hbb_common/src/fs.rs b/libs/hbb_common/src/fs.rs index ea54e113a..41160f49d 100644 --- a/libs/hbb_common/src/fs.rs +++ b/libs/hbb_common/src/fs.rs @@ -823,14 +823,19 @@ pub fn is_write_need_confirmation( let modified_time = metadata.modified()?; let remote_mt = Duration::from_secs(digest.last_modified); let local_mt = modified_time.duration_since(UNIX_EPOCH)?; + // [Note] + // We decide to give the decision whether to override the existing file to users, + // which obey the behavior of the file manager in our system. + let mut is_identical = false; if remote_mt == local_mt && digest.file_size == metadata.len() { - return Ok(DigestCheckResult::IsSame); + is_identical = true; } Ok(DigestCheckResult::NeedConfirm(FileTransferDigest { id: digest.id, file_num: digest.file_num, last_modified: local_mt.as_secs(), file_size: metadata.len(), + is_identical, ..Default::default() })) } else { diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 1c7788193..c37f235c0 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -954,6 +954,7 @@ impl Remote { digest.file_num, read_path, true, + digest.is_identical ); } } @@ -997,6 +998,7 @@ impl Remote { digest.file_num, write_path, false, + digest.is_identical ); } } diff --git a/src/flutter.rs b/src/flutter.rs index 354e418eb..2262df660 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -420,7 +420,7 @@ impl InvokeUiSession for FlutterHandler { // unused in flutter // TEST flutter fn confirm_delete_files(&self, _id: i32, _i: i32, _name: String) {} - fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool) { + fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool, is_identical: bool) { self.push_event( "override_file_confirm", vec![ @@ -428,6 +428,7 @@ impl InvokeUiSession for FlutterHandler { ("file_num", &file_num.to_string()), ("read_path", &to), ("is_upload", &is_upload.to_string()), + ("is_identical", &is_identical.to_string()) ], ); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 93d9c76fe..34671474f 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index bdc2257b3..6778a8818 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "此文件与对方的一致") ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 651043850..b60b927f0 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 31ca52c11..9b6ac2be5 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", "Tom brugernavn"), ("Empty Password", "Tom adgangskode"), ("Me", "Mig"), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 09fad80eb..042756885 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", "Leerer Benutzername"), ("Empty Password", "Leeres Passwort"), ("Me", "Ich"), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 923d4b64e..771fa8797 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", "Κενό όνομα χρήστη"), ("Empty Password", "Κενός κωδικός πρόσβασης"), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index b44ff2e40..c44c9c9f9 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -52,5 +52,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("empty_favorite_tip", "No favorite peers yet?\nLet's find someone to connect with and add it to your favorites!"), ("empty_lan_tip", "Oh no, it looks like we haven't discovered any peers yet."), ("empty_address_book_tip", "Oh dear, it appears that there are currently no peers listed in your address book."), + ("identical_file_tip", "This file is identical with the peer's one.") ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 57a519338..3219e3b42 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index d15061286..58f331d01 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", "Nombre de usuario vacío"), ("Empty Password", "Contraseña vacía"), ("Me", "Yo"), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index be9821083..b57f0dbe9 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 5798253ad..ed8089774 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 672c65bbb..3b281a25f 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index a6272dbd3..7e6095715 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 2333e23dd..f860dbc88 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", "Nome Utente Vuoto"), ("Empty Password", "Password Vuota"), ("Me", "Io"), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 5b1bcec4c..366082cc4 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 3b31b631b..5244704bb 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index ccce435fc..120bcb81e 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 9574581e5..dadc27705 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", "Gebruikersnaam Leeg"), ("Empty Password", "Wachtwoord Leeg"), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 5534bbe1b..c75b392da 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 780bc46c6..0ed7642f3 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 5cfc2e5a3..66dbcd6b7 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index c354c4685..3efbcc281 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 3abea8029..91ef210c8 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 820b7a7db..0b891ff03 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index e81124d5c..29fb6be3c 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index d237b8895..3a923d742 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 8e7ecf824..cf077ac58 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index e0807d892..66f140e99 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 085ed025d..9f4ad1835 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 6b3e34e48..1ada40612 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index c92044e39..9215464ea 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index a16bf26b8..b18488b26 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 302dd9166..f5c0c22f7 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index e72be8ea5..e8bc76c8e 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -477,5 +477,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", ""), ("Empty Password", ""), ("Me", ""), + ("identical_file_tip", "") ].iter().cloned().collect(); } diff --git a/src/ui/file_transfer.tis b/src/ui/file_transfer.tis index f69f6d323..6c741b31f 100644 --- a/src/ui/file_transfer.tis +++ b/src/ui/file_transfer.tis @@ -778,12 +778,14 @@ handler.confirmDeleteFiles = function(id, i, name) { }); } -handler.overrideFileConfirm = function(id, file_num, to, is_upload) { +handler.overrideFileConfirm = function(id, file_num, to, is_upload, is_identical) { var jt = file_transfer.job_table; + var identical_msg = is_identical ? translate("identical_file_tip"): ""; msgbox("custom-skip", "Confirm Write Strategy", "
\ -
" + translate('Overwrite') + translate('files') + ".
\ +
" + translate('Overwrite') + " " + translate('files') + ".
\
" + translate('This file exists, skip or overwrite this file?') + "
\ " + to + "
\ +
" + identical_msg + "
\
" + translate('Do this for all conflicts') + "
\ ", "", function(res=null) { if (!res) { diff --git a/src/ui/remote.rs b/src/ui/remote.rs index ed16f1e0e..68decf955 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -197,10 +197,10 @@ impl InvokeUiSession for SciterHandler { self.call("confirmDeleteFiles", &make_args!(id, i, name)); } - fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool) { + fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool, is_identical: bool) { self.call( "overrideFileConfirm", - &make_args!(id, file_num, to, is_upload), + &make_args!(id, file_num, to, is_upload, is_identical), ); } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 11bcff925..b90f5fbea 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -872,7 +872,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { only_count: bool, ); fn confirm_delete_files(&self, id: i32, i: i32, name: String); - fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool); + fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool, is_identical: bool); fn update_block_input_state(&self, on: bool); fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64); fn adapt_size(&self);