mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
fix conflicts
This commit is contained in:
commit
948d2db073
@ -112,18 +112,21 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
|||||||
required this.border2,
|
required this.border2,
|
||||||
required this.highlight,
|
required this.highlight,
|
||||||
required this.drag_indicator,
|
required this.drag_indicator,
|
||||||
|
required this.shadow,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Color? border;
|
final Color? border;
|
||||||
final Color? border2;
|
final Color? border2;
|
||||||
final Color? highlight;
|
final Color? highlight;
|
||||||
final Color? drag_indicator;
|
final Color? drag_indicator;
|
||||||
|
final Color? shadow;
|
||||||
|
|
||||||
static final light = ColorThemeExtension(
|
static final light = ColorThemeExtension(
|
||||||
border: Color(0xFFCCCCCC),
|
border: Color(0xFFCCCCCC),
|
||||||
border2: Color(0xFFBBBBBB),
|
border2: Color(0xFFBBBBBB),
|
||||||
highlight: Color(0xFFE5E5E5),
|
highlight: Color(0xFFE5E5E5),
|
||||||
drag_indicator: Colors.grey[800],
|
drag_indicator: Colors.grey[800],
|
||||||
|
shadow: Colors.black,
|
||||||
);
|
);
|
||||||
|
|
||||||
static final dark = ColorThemeExtension(
|
static final dark = ColorThemeExtension(
|
||||||
@ -131,19 +134,24 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
|||||||
border2: Color(0xFFE5E5E5),
|
border2: Color(0xFFE5E5E5),
|
||||||
highlight: Color(0xFF3F3F3F),
|
highlight: Color(0xFF3F3F3F),
|
||||||
drag_indicator: Colors.grey,
|
drag_indicator: Colors.grey,
|
||||||
|
shadow: Colors.grey,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ThemeExtension<ColorThemeExtension> copyWith(
|
ThemeExtension<ColorThemeExtension> copyWith({
|
||||||
{Color? border,
|
Color? border,
|
||||||
Color? border2,
|
Color? border2,
|
||||||
Color? highlight,
|
Color? highlight,
|
||||||
Color? drag_indicator}) {
|
Color? drag_indicator,
|
||||||
|
Color? shadow,
|
||||||
|
}) {
|
||||||
return ColorThemeExtension(
|
return ColorThemeExtension(
|
||||||
border: border ?? this.border,
|
border: border ?? this.border,
|
||||||
border2: border2 ?? this.border2,
|
border2: border2 ?? this.border2,
|
||||||
highlight: highlight ?? this.highlight,
|
highlight: highlight ?? this.highlight,
|
||||||
drag_indicator: drag_indicator ?? this.drag_indicator);
|
drag_indicator: drag_indicator ?? this.drag_indicator,
|
||||||
|
shadow: shadow ?? this.shadow,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -157,6 +165,7 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
|||||||
border2: Color.lerp(border2, other.border2, t),
|
border2: Color.lerp(border2, other.border2, t),
|
||||||
highlight: Color.lerp(highlight, other.highlight, t),
|
highlight: Color.lerp(highlight, other.highlight, t),
|
||||||
drag_indicator: Color.lerp(drag_indicator, other.drag_indicator, t),
|
drag_indicator: Color.lerp(drag_indicator, other.drag_indicator, t),
|
||||||
|
shadow: Color.lerp(shadow, other.shadow, t),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -372,6 +372,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
offstage: _dragging.isTrue,
|
offstage: _dragging.isTrue,
|
||||||
child: Material(
|
child: Material(
|
||||||
elevation: _MenubarTheme.elevation,
|
elevation: _MenubarTheme.elevation,
|
||||||
|
shadowColor: MyTheme.color(context).shadow,
|
||||||
child: _DraggableShowHide(
|
child: _DraggableShowHide(
|
||||||
dragging: _dragging,
|
dragging: _dragging,
|
||||||
fractionX: _fractionX,
|
fractionX: _fractionX,
|
||||||
@ -421,6 +422,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
children: [
|
children: [
|
||||||
Material(
|
Material(
|
||||||
elevation: _MenubarTheme.elevation,
|
elevation: _MenubarTheme.elevation,
|
||||||
|
shadowColor: MyTheme.color(context).shadow,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(4.0)),
|
borderRadius: BorderRadius.all(Radius.circular(4.0)),
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.menuBarTheme
|
.menuBarTheme
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/common.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:get/get.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
@ -45,6 +45,7 @@ class FileModel {
|
|||||||
|
|
||||||
late final GetSessionID getSessionID;
|
late final GetSessionID getSessionID;
|
||||||
String get sessionID => getSessionID();
|
String get sessionID => getSessionID();
|
||||||
|
late final FileDialogEventLoop evtLoop;
|
||||||
|
|
||||||
FileModel(this.parent) {
|
FileModel(this.parent) {
|
||||||
getSessionID = () => parent.target?.id ?? "";
|
getSessionID = () => parent.target?.id ?? "";
|
||||||
@ -64,14 +65,17 @@ class FileModel {
|
|||||||
jobController: jobController,
|
jobController: jobController,
|
||||||
fileFetcher: fileFetcher,
|
fileFetcher: fileFetcher,
|
||||||
getOtherSideDirectoryData: () => localController.directoryData());
|
getOtherSideDirectoryData: () => localController.directoryData());
|
||||||
|
evtLoop = FileDialogEventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onReady() async {
|
Future<void> onReady() async {
|
||||||
|
await evtLoop.onReady();
|
||||||
await localController.onReady();
|
await localController.onReady();
|
||||||
await remoteController.onReady();
|
await remoteController.onReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
|
await evtLoop.close();
|
||||||
parent.target?.dialogManager.dismissAll();
|
parent.target?.dialogManager.dismissAll();
|
||||||
await localController.close();
|
await localController.close();
|
||||||
await remoteController.close();
|
await remoteController.close();
|
||||||
@ -90,14 +94,26 @@ class FileModel {
|
|||||||
fileFetcher.tryCompleteTask(evt['value'], evt['is_local']);
|
fileFetcher.tryCompleteTask(evt['value'], evt['is_local']);
|
||||||
}
|
}
|
||||||
|
|
||||||
void overrideFileConfirm(Map<String, dynamic> evt) async {
|
Future<void> postOverrideFileConfirm(Map<String, dynamic> evt) async {
|
||||||
final resp = await showFileConfirmDialog(
|
evtLoop.pushEvent(
|
||||||
translate("Overwrite"), "${evt['read_path']}", true);
|
_FileDialogEvent(WeakReference(this), FileDialogType.overwrite, evt));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> overrideFileConfirm(Map<String, dynamic> 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;
|
final id = int.tryParse(evt['id']) ?? 0;
|
||||||
if (false == resp) {
|
if (false == resp) {
|
||||||
final jobIndex = jobController.getJob(id);
|
final jobIndex = jobController.getJob(id);
|
||||||
if (jobIndex != -1) {
|
if (jobIndex != -1) {
|
||||||
jobController.cancelJob(id);
|
await jobController.cancelJob(id);
|
||||||
final job = jobController.jobTable[jobIndex];
|
final job = jobController.jobTable[jobIndex];
|
||||||
job.state = JobState.done;
|
job.state = JobState.done;
|
||||||
jobController.jobTable.refresh();
|
jobController.jobTable.refresh();
|
||||||
@ -111,7 +127,11 @@ class FileModel {
|
|||||||
// overwrite
|
// overwrite
|
||||||
need_override = true;
|
need_override = true;
|
||||||
}
|
}
|
||||||
bind.sessionSetConfirmOverrideFile(
|
// Update the loop config.
|
||||||
|
if (fileConfirmCheckboxRemember) {
|
||||||
|
evtLoop.setSkip(!need_override);
|
||||||
|
}
|
||||||
|
await bind.sessionSetConfirmOverrideFile(
|
||||||
id: sessionID,
|
id: sessionID,
|
||||||
actId: id,
|
actId: id,
|
||||||
fileNum: int.parse(evt['file_num']),
|
fileNum: int.parse(evt['file_num']),
|
||||||
@ -119,12 +139,16 @@ class FileModel {
|
|||||||
remember: fileConfirmCheckboxRemember,
|
remember: fileConfirmCheckboxRemember,
|
||||||
isUpload: evt['is_upload'] == "true");
|
isUpload: evt['is_upload'] == "true");
|
||||||
}
|
}
|
||||||
|
// Update the loop config.
|
||||||
|
if (fileConfirmCheckboxRemember) {
|
||||||
|
evtLoop.setOverrideConfirm(resp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fileConfirmCheckboxRemember = false;
|
bool fileConfirmCheckboxRemember = false;
|
||||||
|
|
||||||
Future<bool?> showFileConfirmDialog(
|
Future<bool?> showFileConfirmDialog(
|
||||||
String title, String content, bool showCheckbox) async {
|
String title, String content, bool showCheckbox, bool isIdentical) async {
|
||||||
fileConfirmCheckboxRemember = false;
|
fileConfirmCheckboxRemember = false;
|
||||||
return await parent.target?.dialogManager.show<bool?>(
|
return await parent.target?.dialogManager.show<bool?>(
|
||||||
(setState, Function(bool? v) close) {
|
(setState, Function(bool? v) close) {
|
||||||
@ -149,6 +173,17 @@ class FileModel {
|
|||||||
style: const TextStyle(fontWeight: FontWeight.bold)),
|
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
Text(content),
|
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
|
showCheckbox
|
||||||
? CheckboxListTile(
|
? CheckboxListTile(
|
||||||
contentPadding: const EdgeInsets.all(0),
|
contentPadding: const EdgeInsets.all(0),
|
||||||
@ -677,8 +712,8 @@ class JobController {
|
|||||||
debugPrint("jobError $evt");
|
debugPrint("jobError $evt");
|
||||||
}
|
}
|
||||||
|
|
||||||
void cancelJob(int id) async {
|
Future<void> cancelJob(int id) async {
|
||||||
bind.sessionCancelJob(id: sessionID, actId: id);
|
await bind.sessionCancelJob(id: sessionID, actId: id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadLastJob(Map<String, dynamic> evt) {
|
void loadLastJob(Map<String, dynamic> evt) {
|
||||||
@ -1167,3 +1202,74 @@ List<Entry> _sortList(List<Entry> list, SortBy sortType, bool ascending) {
|
|||||||
}
|
}
|
||||||
return [];
|
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<FileDialogType, Map<String, dynamic>> {
|
||||||
|
WeakReference<FileModel> 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<Map<String, dynamic>>? 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<FileDialogType, Map<String, dynamic>> {
|
||||||
|
bool? _overrideConfirm;
|
||||||
|
bool _skip = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onPreConsume(
|
||||||
|
BaseEvent<FileDialogType, Map<String, dynamic>> evt) async {
|
||||||
|
var event = evt as _FileDialogEvent;
|
||||||
|
event.setOverrideConfirm(_overrideConfirm);
|
||||||
|
event.setSkip(_skip);
|
||||||
|
debugPrint(
|
||||||
|
"FileDialogEventLoop: consuming<jobId: ${evt.data['id']} overrideConfirm: $_overrideConfirm, skip: $_skip>");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onEventsClear() {
|
||||||
|
_overrideConfirm = null;
|
||||||
|
_skip = false;
|
||||||
|
return super.onEventsClear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOverrideConfirm(bool? confirm) {
|
||||||
|
_overrideConfirm = confirm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSkip(bool skip) {
|
||||||
|
_skip = skip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -173,7 +173,7 @@ class FfiModel with ChangeNotifier {
|
|||||||
} else if (name == 'job_error') {
|
} else if (name == 'job_error') {
|
||||||
parent.target?.fileModel.jobController.jobError(evt);
|
parent.target?.fileModel.jobController.jobError(evt);
|
||||||
} else if (name == 'override_file_confirm') {
|
} else if (name == 'override_file_confirm') {
|
||||||
parent.target?.fileModel.overrideFileConfirm(evt);
|
parent.target?.fileModel.postOverrideFileConfirm(evt);
|
||||||
} else if (name == 'load_last_job') {
|
} else if (name == 'load_last_job') {
|
||||||
parent.target?.fileModel.jobController.loadLastJob(evt);
|
parent.target?.fileModel.jobController.loadLastJob(evt);
|
||||||
} else if (name == 'update_folder_files') {
|
} else if (name == 'update_folder_files') {
|
||||||
|
|||||||
79
flutter/lib/utils/event_loop.dart
Normal file
79
flutter/lib/utils/event_loop.dart
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
typedef EventCallback<Data> = Future<dynamic> Function(Data data);
|
||||||
|
|
||||||
|
abstract class BaseEvent<EventType, Data> {
|
||||||
|
EventType type;
|
||||||
|
Data data;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
BaseEvent(this.type, this.data);
|
||||||
|
|
||||||
|
/// Consume this event.
|
||||||
|
@visibleForTesting
|
||||||
|
Future<dynamic> consume() async {
|
||||||
|
final cb = findCallback(type);
|
||||||
|
if (cb == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return cb(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EventCallback<Data>? findCallback(EventType type);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class BaseEventLoop<EventType, Data> {
|
||||||
|
final List<BaseEvent<EventType, Data>> _evts = [];
|
||||||
|
Timer? _timer;
|
||||||
|
|
||||||
|
List<BaseEvent<EventType, Data>> get evts => _evts;
|
||||||
|
|
||||||
|
Future<void> onReady() async {
|
||||||
|
// Poll every 100ms.
|
||||||
|
_timer = Timer.periodic(Duration(milliseconds: 100), _handleTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An Event is about to be consumed.
|
||||||
|
Future<void> onPreConsume(BaseEvent<EventType, Data> evt) async {}
|
||||||
|
/// An Event was consumed.
|
||||||
|
Future<void> onPostConsume(BaseEvent<EventType, Data> evt) async {}
|
||||||
|
/// Events are all handled and cleared.
|
||||||
|
Future<void> onEventsClear() async {}
|
||||||
|
/// Events start to consume.
|
||||||
|
Future<void> onEventsStartConsuming() async {}
|
||||||
|
|
||||||
|
Future<void> _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<void> close() async {
|
||||||
|
_timer?.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void pushEvent(BaseEvent<EventType, Data> evt) {
|
||||||
|
_evts.add(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
_evts.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -301,6 +301,7 @@ message FileTransferDigest {
|
|||||||
uint64 last_modified = 3;
|
uint64 last_modified = 3;
|
||||||
uint64 file_size = 4;
|
uint64 file_size = 4;
|
||||||
bool is_upload = 5;
|
bool is_upload = 5;
|
||||||
|
bool is_identical = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message FileTransferBlock {
|
message FileTransferBlock {
|
||||||
|
|||||||
@ -27,6 +27,7 @@ message PunchHole {
|
|||||||
bytes socket_addr = 1;
|
bytes socket_addr = 1;
|
||||||
string relay_server = 2;
|
string relay_server = 2;
|
||||||
NatType nat_type = 3;
|
NatType nat_type = 3;
|
||||||
|
string request_region = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message TestNatRequest {
|
message TestNatRequest {
|
||||||
@ -51,6 +52,7 @@ message PunchHoleSent {
|
|||||||
string relay_server = 3;
|
string relay_server = 3;
|
||||||
NatType nat_type = 4;
|
NatType nat_type = 4;
|
||||||
string version = 5;
|
string version = 5;
|
||||||
|
string request_region = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RegisterPk {
|
message RegisterPk {
|
||||||
@ -105,6 +107,7 @@ message RequestRelay {
|
|||||||
string licence_key = 6;
|
string licence_key = 6;
|
||||||
ConnType conn_type = 7;
|
ConnType conn_type = 7;
|
||||||
string token = 8;
|
string token = 8;
|
||||||
|
string request_region = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RelayResponse {
|
message RelayResponse {
|
||||||
@ -117,6 +120,7 @@ message RelayResponse {
|
|||||||
}
|
}
|
||||||
string refuse_reason = 6;
|
string refuse_reason = 6;
|
||||||
string version = 7;
|
string version = 7;
|
||||||
|
string request_region = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SoftwareUpdate { string url = 1; }
|
message SoftwareUpdate { string url = 1; }
|
||||||
@ -128,6 +132,7 @@ message SoftwareUpdate { string url = 1; }
|
|||||||
message FetchLocalAddr {
|
message FetchLocalAddr {
|
||||||
bytes socket_addr = 1;
|
bytes socket_addr = 1;
|
||||||
string relay_server = 2;
|
string relay_server = 2;
|
||||||
|
string request_region = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LocalAddr {
|
message LocalAddr {
|
||||||
@ -136,6 +141,7 @@ message LocalAddr {
|
|||||||
string relay_server = 3;
|
string relay_server = 3;
|
||||||
string id = 4;
|
string id = 4;
|
||||||
string version = 5;
|
string version = 5;
|
||||||
|
string request_region = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PeerDiscovery {
|
message PeerDiscovery {
|
||||||
|
|||||||
@ -43,6 +43,7 @@ lazy_static::lazy_static! {
|
|||||||
static ref CONFIG: Arc<RwLock<Config>> = Arc::new(RwLock::new(Config::load()));
|
static ref CONFIG: Arc<RwLock<Config>> = Arc::new(RwLock::new(Config::load()));
|
||||||
static ref CONFIG2: Arc<RwLock<Config2>> = Arc::new(RwLock::new(Config2::load()));
|
static ref CONFIG2: Arc<RwLock<Config2>> = Arc::new(RwLock::new(Config2::load()));
|
||||||
static ref LOCAL_CONFIG: Arc<RwLock<LocalConfig>> = Arc::new(RwLock::new(LocalConfig::load()));
|
static ref LOCAL_CONFIG: Arc<RwLock<LocalConfig>> = Arc::new(RwLock::new(LocalConfig::load()));
|
||||||
|
pub static ref CONFIG_OIDC: Arc<RwLock<ConfigOidc>> = Arc::new(RwLock::new(ConfigOidc::load()));
|
||||||
pub static ref ONLINE: Arc<Mutex<HashMap<String, i64>>> = Default::default();
|
pub static ref ONLINE: Arc<Mutex<HashMap<String, i64>>> = Default::default();
|
||||||
pub static ref PROD_RENDEZVOUS_SERVER: Arc<RwLock<String>> = Arc::new(RwLock::new(match option_env!("RENDEZVOUS_SERVER") {
|
pub static ref PROD_RENDEZVOUS_SERVER: Arc<RwLock<String>> = Arc::new(RwLock::new(match option_env!("RENDEZVOUS_SERVER") {
|
||||||
Some(key) if !key.is_empty() => key,
|
Some(key) if !key.is_empty() => key,
|
||||||
@ -257,6 +258,35 @@ pub struct PeerInfoSerde {
|
|||||||
pub platform: String,
|
pub platform: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
pub struct ConfigOidc {
|
||||||
|
#[serde(default)]
|
||||||
|
pub max_auth_count: usize,
|
||||||
|
#[serde(default)]
|
||||||
|
pub callback_url: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub providers: HashMap<String, ConfigOidcProvider>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
pub struct ConfigOidcProvider {
|
||||||
|
// seconds. 0 means never expires
|
||||||
|
#[serde(default)]
|
||||||
|
pub refresh_token_expires_in: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub client_id: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub client_secret: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub issuer: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub authorization_endpoint: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub token_endpoint: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub userinfo_endpoint: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct TransferSerde {
|
pub struct TransferSerde {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -1366,6 +1396,29 @@ impl UserDefaultConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ConfigOidc {
|
||||||
|
fn suffix() -> &'static str {
|
||||||
|
"_oidc"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load() -> Self {
|
||||||
|
Config::load_::<Self>(Self::suffix())._load_env()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _load_env(mut self) -> Self {
|
||||||
|
use std::env;
|
||||||
|
for (k, mut v) in &mut self.providers {
|
||||||
|
if let Ok(client_id) = env::var(format!("OIDC-{}-CLIENT-ID", k.to_uppercase())) {
|
||||||
|
v.client_id = client_id;
|
||||||
|
}
|
||||||
|
if let Ok(client_secret) = env::var(format!("OIDC-{}-CLIENT-SECRET", k.to_uppercase())) {
|
||||||
|
v.client_secret = client_secret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@ -823,14 +823,19 @@ pub fn is_write_need_confirmation(
|
|||||||
let modified_time = metadata.modified()?;
|
let modified_time = metadata.modified()?;
|
||||||
let remote_mt = Duration::from_secs(digest.last_modified);
|
let remote_mt = Duration::from_secs(digest.last_modified);
|
||||||
let local_mt = modified_time.duration_since(UNIX_EPOCH)?;
|
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() {
|
if remote_mt == local_mt && digest.file_size == metadata.len() {
|
||||||
return Ok(DigestCheckResult::IsSame);
|
is_identical = true;
|
||||||
}
|
}
|
||||||
Ok(DigestCheckResult::NeedConfirm(FileTransferDigest {
|
Ok(DigestCheckResult::NeedConfirm(FileTransferDigest {
|
||||||
id: digest.id,
|
id: digest.id,
|
||||||
file_num: digest.file_num,
|
file_num: digest.file_num,
|
||||||
last_modified: local_mt.as_secs(),
|
last_modified: local_mt.as_secs(),
|
||||||
file_size: metadata.len(),
|
file_size: metadata.len(),
|
||||||
|
is_identical,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -954,6 +954,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
digest.file_num,
|
digest.file_num,
|
||||||
read_path,
|
read_path,
|
||||||
true,
|
true,
|
||||||
|
digest.is_identical
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -997,6 +998,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
digest.file_num,
|
digest.file_num,
|
||||||
write_path,
|
write_path,
|
||||||
false,
|
false,
|
||||||
|
digest.is_identical
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -420,7 +420,7 @@ impl InvokeUiSession for FlutterHandler {
|
|||||||
// unused in flutter // TEST flutter
|
// unused in flutter // TEST flutter
|
||||||
fn confirm_delete_files(&self, _id: i32, _i: i32, _name: String) {}
|
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(
|
self.push_event(
|
||||||
"override_file_confirm",
|
"override_file_confirm",
|
||||||
vec![
|
vec![
|
||||||
@ -428,6 +428,7 @@ impl InvokeUiSession for FlutterHandler {
|
|||||||
("file_num", &file_num.to_string()),
|
("file_num", &file_num.to_string()),
|
||||||
("read_path", &to),
|
("read_path", &to),
|
||||||
("is_upload", &is_upload.to_string()),
|
("is_upload", &is_upload.to_string()),
|
||||||
|
("is_identical", &is_identical.to_string())
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", "此文件与对方的一致"),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", "Tom brugernavn"),
|
("Empty Username", "Tom brugernavn"),
|
||||||
("Empty Password", "Tom adgangskode"),
|
("Empty Password", "Tom adgangskode"),
|
||||||
("Me", "Mig"),
|
("Me", "Mig"),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", "Leerer Benutzername"),
|
("Empty Username", "Leerer Benutzername"),
|
||||||
("Empty Password", "Leeres Passwort"),
|
("Empty Password", "Leeres Passwort"),
|
||||||
("Me", "Ich"),
|
("Me", "Ich"),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", "Κενό όνομα χρήστη"),
|
("Empty Username", "Κενό όνομα χρήστη"),
|
||||||
("Empty Password", "Κενός κωδικός πρόσβασης"),
|
("Empty Password", "Κενός κωδικός πρόσβασης"),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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_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_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."),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", "Nombre de usuario vacío"),
|
("Empty Username", "Nombre de usuario vacío"),
|
||||||
("Empty Password", "Contraseña vacía"),
|
("Empty Password", "Contraseña vacía"),
|
||||||
("Me", "Yo"),
|
("Me", "Yo"),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", "Nome Utente Vuoto"),
|
("Empty Username", "Nome Utente Vuoto"),
|
||||||
("Empty Password", "Password Vuota"),
|
("Empty Password", "Password Vuota"),
|
||||||
("Me", "Io"),
|
("Me", "Io"),
|
||||||
|
("identical_file_tip", "Questo file è identico a quello del peer."),
|
||||||
("Show monitors in menu bar", "Mostra schermi nella barra di menù"),
|
("Show monitors in menu bar", "Mostra schermi nella barra di menù"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", "Gebruikersnaam Leeg"),
|
("Empty Username", "Gebruikersnaam Leeg"),
|
||||||
("Empty Password", "Wachtwoord Leeg"),
|
("Empty Password", "Wachtwoord Leeg"),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -477,6 +477,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Empty Username", ""),
|
("Empty Username", ""),
|
||||||
("Empty Password", ""),
|
("Empty Password", ""),
|
||||||
("Me", ""),
|
("Me", ""),
|
||||||
|
("identical_file_tip", ""),
|
||||||
("Show monitors in menu bar", ""),
|
("Show monitors in menu bar", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 jt = file_transfer.job_table;
|
||||||
|
var identical_msg = is_identical ? translate("identical_file_tip"): "";
|
||||||
msgbox("custom-skip", "Confirm Write Strategy", "<div .form> \
|
msgbox("custom-skip", "Confirm Write Strategy", "<div .form> \
|
||||||
<div>" + translate('Overwrite') + translate('files') + ".</div> \
|
<div>" + translate('Overwrite') + " " + translate('files') + ".</div> \
|
||||||
<div>" + translate('This file exists, skip or overwrite this file?') + "</div> \
|
<div>" + translate('This file exists, skip or overwrite this file?') + "</div> \
|
||||||
<div.ellipsis style=\"font-weight: bold;\" .text>" + to + "</div> \
|
<div.ellipsis style=\"font-weight: bold;\" .text>" + to + "</div> \
|
||||||
|
<div>" + identical_msg + "</div> \
|
||||||
<div><button|checkbox(remember) {ts}>" + translate('Do this for all conflicts') + "</button></div> \
|
<div><button|checkbox(remember) {ts}>" + translate('Do this for all conflicts') + "</button></div> \
|
||||||
</div>", "", function(res=null) {
|
</div>", "", function(res=null) {
|
||||||
if (!res) {
|
if (!res) {
|
||||||
|
|||||||
@ -197,10 +197,10 @@ impl InvokeUiSession for SciterHandler {
|
|||||||
self.call("confirmDeleteFiles", &make_args!(id, i, name));
|
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(
|
self.call(
|
||||||
"overrideFileConfirm",
|
"overrideFileConfirm",
|
||||||
&make_args!(id, file_num, to, is_upload),
|
&make_args!(id, file_num, to, is_upload, is_identical),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -872,7 +872,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
|
|||||||
only_count: bool,
|
only_count: bool,
|
||||||
);
|
);
|
||||||
fn confirm_delete_files(&self, id: i32, i: i32, name: String);
|
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 update_block_input_state(&self, on: bool);
|
||||||
fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64);
|
fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64);
|
||||||
fn adapt_size(&self);
|
fn adapt_size(&self);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user