mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-04-20 05:43:20 +03:00
ask for note at end of connection (#13499)
Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
@@ -44,7 +44,7 @@ import 'package:flutter_hbb/native/win32.dart'
|
|||||||
if (dart.library.html) 'package:flutter_hbb/web/win32.dart';
|
if (dart.library.html) 'package:flutter_hbb/web/win32.dart';
|
||||||
import 'package:flutter_hbb/native/common.dart'
|
import 'package:flutter_hbb/native/common.dart'
|
||||||
if (dart.library.html) 'package:flutter_hbb/web/common.dart';
|
if (dart.library.html) 'package:flutter_hbb/web/common.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:flutter_hbb/utils/http_service.dart' as http;
|
||||||
|
|
||||||
final globalKey = GlobalKey<NavigatorState>();
|
final globalKey = GlobalKey<NavigatorState>();
|
||||||
final navigationBarKey = GlobalKey();
|
final navigationBarKey = GlobalKey();
|
||||||
@@ -1681,13 +1681,12 @@ class LastWindowPosition {
|
|||||||
this.offsetHeight, this.isMaximized, this.isFullscreen);
|
this.offsetHeight, this.isMaximized, this.isFullscreen);
|
||||||
|
|
||||||
bool equals(LastWindowPosition other) {
|
bool equals(LastWindowPosition other) {
|
||||||
return (
|
return ((width == other.width) &&
|
||||||
(width == other.width) &&
|
(height == other.height) &&
|
||||||
(height == other.height) &&
|
(offsetWidth == other.offsetWidth) &&
|
||||||
(offsetWidth == other.offsetWidth) &&
|
(offsetHeight == other.offsetHeight) &&
|
||||||
(offsetHeight == other.offsetHeight) &&
|
(isMaximized == other.isMaximized) &&
|
||||||
(isMaximized == other.isMaximized) &&
|
(isFullscreen == other.isFullscreen));
|
||||||
(isFullscreen == other.isFullscreen));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
@@ -1815,7 +1814,8 @@ Future<void> saveWindowPosition(WindowType type,
|
|||||||
|
|
||||||
final WindowKey key = (type: type, windowId: windowId);
|
final WindowKey key = (type: type, windowId: windowId);
|
||||||
|
|
||||||
final bool haveNewWindowPosition = (_lastWindowPosition == null) || !pos.equals(_lastWindowPosition!);
|
final bool haveNewWindowPosition =
|
||||||
|
(_lastWindowPosition == null) || !pos.equals(_lastWindowPosition!);
|
||||||
final bool isPreviousNewWindowPositionPending = _saveWindowDebounce.isRunning;
|
final bool isPreviousNewWindowPositionPending = _saveWindowDebounce.isRunning;
|
||||||
|
|
||||||
if (haveNewWindowPosition || isPreviousNewWindowPositionPending) {
|
if (haveNewWindowPosition || isPreviousNewWindowPositionPending) {
|
||||||
@@ -1841,10 +1841,11 @@ Future<void> _saveWindowPositionActual(WindowKey key) async {
|
|||||||
await bind.setLocalFlutterOption(
|
await bind.setLocalFlutterOption(
|
||||||
k: windowFramePrefix + key.type.name, v: pos.toString());
|
k: windowFramePrefix + key.type.name, v: pos.toString());
|
||||||
|
|
||||||
if ((key.type == WindowType.RemoteDesktop || key.type == WindowType.ViewCamera) &&
|
if ((key.type == WindowType.RemoteDesktop ||
|
||||||
|
key.type == WindowType.ViewCamera) &&
|
||||||
key.windowId != null) {
|
key.windowId != null) {
|
||||||
await _saveSessionWindowPosition(
|
await _saveSessionWindowPosition(key.type, key.windowId!,
|
||||||
key.type, key.windowId!, pos.isMaximized ?? false, pos.isFullscreen ?? false, pos);
|
pos.isMaximized ?? false, pos.isFullscreen ?? false, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,20 +7,29 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_hbb/common/shared_state.dart';
|
import 'package:flutter_hbb/common/shared_state.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
|
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
|
||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
|
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||||
import 'package:flutter_hbb/models/peer_model.dart';
|
import 'package:flutter_hbb/models/peer_model.dart';
|
||||||
import 'package:flutter_hbb/models/peer_tab_model.dart';
|
import 'package:flutter_hbb/models/peer_tab_model.dart';
|
||||||
import 'package:flutter_hbb/models/state_model.dart';
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
|
import 'package:flutter_hbb/utils/http_service.dart' as http;
|
||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import 'address_book.dart';
|
import 'address_book.dart';
|
||||||
|
|
||||||
void clientClose(SessionID sessionId, OverlayDialogManager dialogManager) {
|
void clientClose(SessionID sessionId, FFI ffi) async {
|
||||||
msgBox(sessionId, 'info', 'Close', 'Are you sure to close the connection?',
|
if (allowAskForNoteAtEndOfConnection(ffi, true)) {
|
||||||
'', dialogManager);
|
if (await showConnEndAuditDialogCloseCanceled(ffi: ffi)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
closeConnection();
|
||||||
|
} else {
|
||||||
|
msgBox(sessionId, 'info', 'Close', 'Are you sure to close the connection?',
|
||||||
|
'', ffi.dialogManager);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ValidationRule {
|
abstract class ValidationRule {
|
||||||
@@ -1509,56 +1518,71 @@ showSetOSAccount(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildNoteTextField({
|
||||||
|
required TextEditingController controller,
|
||||||
|
required VoidCallback onEscape,
|
||||||
|
}) {
|
||||||
|
final focusNode = FocusNode(
|
||||||
|
onKey: (FocusNode node, RawKeyEvent evt) {
|
||||||
|
if (evt.logicalKey.keyLabel == 'Enter') {
|
||||||
|
if (evt is RawKeyDownEvent) {
|
||||||
|
int pos = controller.selection.base.offset;
|
||||||
|
controller.text =
|
||||||
|
'${controller.text.substring(0, pos)}\n${controller.text.substring(pos)}';
|
||||||
|
controller.selection =
|
||||||
|
TextSelection.fromPosition(TextPosition(offset: pos + 1));
|
||||||
|
}
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
if (evt.logicalKey.keyLabel == 'Esc') {
|
||||||
|
if (evt is RawKeyDownEvent) {
|
||||||
|
onEscape();
|
||||||
|
}
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
} else {
|
||||||
|
return KeyEventResult.ignored;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return TextField(
|
||||||
|
autofocus: true,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
textInputAction: TextInputAction.newline,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: translate('input note here'),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
contentPadding: EdgeInsets.all(12),
|
||||||
|
),
|
||||||
|
minLines: 5,
|
||||||
|
maxLines: null,
|
||||||
|
maxLength: 256,
|
||||||
|
controller: controller,
|
||||||
|
focusNode: focusNode,
|
||||||
|
).workaroundFreezeLinuxMint();
|
||||||
|
}
|
||||||
|
|
||||||
showAuditDialog(FFI ffi) async {
|
showAuditDialog(FFI ffi) async {
|
||||||
final controller = TextEditingController(text: ffi.auditNote);
|
final controller = TextEditingController(
|
||||||
|
text: bind.sessionGetLastAuditNote(sessionId: ffi.sessionId));
|
||||||
ffi.dialogManager.show((setState, close, context) {
|
ffi.dialogManager.show((setState, close, context) {
|
||||||
submit() {
|
submit() {
|
||||||
var text = controller.text;
|
var text = controller.text;
|
||||||
bind.sessionSendNote(sessionId: ffi.sessionId, note: text);
|
bind.sessionSendNote(sessionId: ffi.sessionId, note: text);
|
||||||
ffi.auditNote = text;
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
late final focusNode = FocusNode(
|
|
||||||
onKey: (FocusNode node, RawKeyEvent evt) {
|
|
||||||
if (evt.logicalKey.keyLabel == 'Enter') {
|
|
||||||
if (evt is RawKeyDownEvent) {
|
|
||||||
int pos = controller.selection.base.offset;
|
|
||||||
controller.text =
|
|
||||||
'${controller.text.substring(0, pos)}\n${controller.text.substring(pos)}';
|
|
||||||
controller.selection =
|
|
||||||
TextSelection.fromPosition(TextPosition(offset: pos + 1));
|
|
||||||
}
|
|
||||||
return KeyEventResult.handled;
|
|
||||||
}
|
|
||||||
if (evt.logicalKey.keyLabel == 'Esc') {
|
|
||||||
if (evt is RawKeyDownEvent) {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
return KeyEventResult.handled;
|
|
||||||
} else {
|
|
||||||
return KeyEventResult.ignored;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
title: Text(translate('Note')),
|
title: Text(translate('Note')),
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
width: 250,
|
width: 250,
|
||||||
height: 120,
|
height: 120,
|
||||||
child: TextField(
|
child: buildNoteTextField(
|
||||||
autofocus: true,
|
|
||||||
keyboardType: TextInputType.multiline,
|
|
||||||
textInputAction: TextInputAction.newline,
|
|
||||||
decoration: const InputDecoration.collapsed(
|
|
||||||
hintText: 'input note here',
|
|
||||||
),
|
|
||||||
maxLines: null,
|
|
||||||
maxLength: 256,
|
|
||||||
controller: controller,
|
controller: controller,
|
||||||
focusNode: focusNode,
|
onEscape: close,
|
||||||
).workaroundFreezeLinuxMint()),
|
)),
|
||||||
actions: [
|
actions: [
|
||||||
dialogButton('Cancel', onPressed: close, isOutline: true),
|
dialogButton('Cancel', onPressed: close, isOutline: true),
|
||||||
dialogButton('OK', onPressed: submit)
|
dialogButton('OK', onPressed: submit)
|
||||||
@@ -1569,6 +1593,223 @@ showAuditDialog(FFI ffi) async {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool allowAskForNoteAtEndOfConnection(FFI? ffi, bool closedByControlling) {
|
||||||
|
if (ffi == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return mainGetLocalBoolOptionSync(kOptionAllowAskForNoteAtEndOfConnection) &&
|
||||||
|
bind
|
||||||
|
.sessionGetAuditServerSync(sessionId: ffi.sessionId, typ: "conn")
|
||||||
|
.isNotEmpty &&
|
||||||
|
bind.sessionGetAuditGuid(sessionId: ffi.sessionId).isNotEmpty &&
|
||||||
|
bind.sessionGetLastAuditNote(sessionId: ffi.sessionId).isEmpty &&
|
||||||
|
(!closedByControlling ||
|
||||||
|
bind.willSessionCloseCloseSession(sessionId: ffi.sessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
// return value: close canceled
|
||||||
|
// true: return
|
||||||
|
// false: go on
|
||||||
|
Future<bool> desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
{required String id, required DesktopTabController tabController}) async {
|
||||||
|
try {
|
||||||
|
final page =
|
||||||
|
tabController.state.value.tabs.firstWhere((tab) => tab.key == id).page;
|
||||||
|
final ffi = (page as dynamic).ffi;
|
||||||
|
final res = await showConnEndAuditDialogCloseCanceled(ffi: ffi);
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to show audit dialog: $e');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return value:
|
||||||
|
// true: return
|
||||||
|
// false: go on
|
||||||
|
Future<bool> showConnEndAuditDialogCloseCanceled(
|
||||||
|
{required FFI ffi, String? type, String? title, String? text}) async {
|
||||||
|
final res = await _showConnEndAuditDialogCloseCanceled(
|
||||||
|
ffi: ffi, type: type, title: title, text: text);
|
||||||
|
if (res == true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return value:
|
||||||
|
// true: return
|
||||||
|
// false / null: go on
|
||||||
|
Future<bool?> _showConnEndAuditDialogCloseCanceled({
|
||||||
|
required FFI ffi,
|
||||||
|
String? type,
|
||||||
|
String? title,
|
||||||
|
String? text,
|
||||||
|
}) async {
|
||||||
|
final closedByControlling = type == null;
|
||||||
|
final showDialog = allowAskForNoteAtEndOfConnection(ffi, closedByControlling);
|
||||||
|
if (!showDialog) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ffi.dialogManager.dismissAll();
|
||||||
|
|
||||||
|
Future<void> updateAuditNoteByGuid(String auditGuid, String note) async {
|
||||||
|
debugPrint('Updating audit note for GUID: $auditGuid, note: $note');
|
||||||
|
try {
|
||||||
|
final apiServer = await bind.mainGetApiServer();
|
||||||
|
if (apiServer.isEmpty) {
|
||||||
|
debugPrint('API server is empty, cannot update audit note');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final url = '$apiServer/api/audit';
|
||||||
|
var headers = getHttpHeaders();
|
||||||
|
headers['Content-Type'] = "application/json";
|
||||||
|
final body = jsonEncode({
|
||||||
|
'guid': auditGuid,
|
||||||
|
'note': note,
|
||||||
|
});
|
||||||
|
|
||||||
|
final response = await http.put(
|
||||||
|
Uri.parse(url),
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
debugPrint('Successfully updated audit note for GUID: $auditGuid');
|
||||||
|
} else {
|
||||||
|
debugPrint(
|
||||||
|
'Failed to update audit note. Status: ${response.statusCode}, Body: ${response.body}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error updating audit note: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final controller = TextEditingController();
|
||||||
|
bool askForNote =
|
||||||
|
mainGetLocalBoolOptionSync(kOptionAllowAskForNoteAtEndOfConnection);
|
||||||
|
final isOptFixed = isOptionFixed(kOptionAllowAskForNoteAtEndOfConnection);
|
||||||
|
bool isInProgress = false;
|
||||||
|
|
||||||
|
return await ffi.dialogManager.show<bool>((setState, close, context) {
|
||||||
|
cancel() {
|
||||||
|
close(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
set() async {
|
||||||
|
if (isInProgress) return;
|
||||||
|
setState(() {
|
||||||
|
isInProgress = true;
|
||||||
|
});
|
||||||
|
var text = controller.text;
|
||||||
|
if (text.isNotEmpty) {
|
||||||
|
await updateAuditNoteByGuid(
|
||||||
|
bind.sessionGetAuditGuid(sessionId: ffi.sessionId), text)
|
||||||
|
.timeout(const Duration(seconds: 6), onTimeout: () {
|
||||||
|
debugPrint('updateAuditNoteByGuid timeout after 6s');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Save the "ask for note" preference
|
||||||
|
if (!isOptFixed) {
|
||||||
|
await mainSetLocalBoolOption(
|
||||||
|
kOptionAllowAskForNoteAtEndOfConnection, askForNote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submit() async {
|
||||||
|
await set();
|
||||||
|
close(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
final buttons = [
|
||||||
|
dialogButton('OK', onPressed: isInProgress ? null : submit)
|
||||||
|
];
|
||||||
|
if (type == 'relay-hint' || type == 'relay-hint2') {
|
||||||
|
buttons.add(dialogButton('Retry', onPressed: () async {
|
||||||
|
await set();
|
||||||
|
close(true);
|
||||||
|
ffi.ffiModel.reconnect(ffi.dialogManager, ffi.sessionId, false);
|
||||||
|
}));
|
||||||
|
if (type == 'relay-hint2') {
|
||||||
|
buttons.add(dialogButton('Connect via relay', onPressed: () async {
|
||||||
|
await set();
|
||||||
|
close(true);
|
||||||
|
ffi.ffiModel.reconnect(ffi.dialogManager, ffi.sessionId, true);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (closedByControlling) {
|
||||||
|
buttons.add(dialogButton('Cancel',
|
||||||
|
onPressed: isInProgress ? null : cancel, isOutline: true));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget content;
|
||||||
|
if (closedByControlling) {
|
||||||
|
content = SelectionArea(
|
||||||
|
child: msgboxContent(
|
||||||
|
'info', 'Close', 'Are you sure to close the connection?'));
|
||||||
|
} else {
|
||||||
|
content =
|
||||||
|
SelectionArea(child: msgboxContent(type, title ?? '', text ?? ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
return CustomAlertDialog(
|
||||||
|
title: null,
|
||||||
|
content: SizedBox(
|
||||||
|
width: 350,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
content,
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
SizedBox(
|
||||||
|
height: 120,
|
||||||
|
child: buildNoteTextField(
|
||||||
|
controller: controller,
|
||||||
|
onEscape: cancel,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (!isOptFixed) ...[
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
askForNote = !askForNote;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Checkbox(
|
||||||
|
value: askForNote,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
askForNote = value ?? false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
translate('note-at-conn-end-tip'),
|
||||||
|
style: const TextStyle(fontSize: 13),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (isInProgress)
|
||||||
|
const LinearProgressIndicator().marginOnly(top: 4),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
actions: buttons,
|
||||||
|
onSubmit: submit,
|
||||||
|
onCancel: cancel,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void showConfirmSwitchSidesDialog(
|
void showConfirmSwitchSidesDialog(
|
||||||
SessionID sessionId, String id, OverlayDialogManager dialogManager) async {
|
SessionID sessionId, String id, OverlayDialogManager dialogManager) async {
|
||||||
dialogManager.show((setState, close, context) {
|
dialogManager.show((setState, close, context) {
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ const String kOptionEnableTrustedDevices = "enable-trusted-devices";
|
|||||||
const String kOptionShowVirtualMouse = "show-virtual-mouse";
|
const String kOptionShowVirtualMouse = "show-virtual-mouse";
|
||||||
const String kOptionVirtualMouseScale = "virtual-mouse-scale";
|
const String kOptionVirtualMouseScale = "virtual-mouse-scale";
|
||||||
const String kOptionShowVirtualJoystick = "show-virtual-joystick";
|
const String kOptionShowVirtualJoystick = "show-virtual-joystick";
|
||||||
|
const String kOptionAllowAskForNoteAtEndOfConnection = "allow-ask-for-note";
|
||||||
|
|
||||||
// network options
|
// network options
|
||||||
const String kOptionAllowWebSocket = "allow-websocket";
|
const String kOptionAllowWebSocket = "allow-websocket";
|
||||||
@@ -324,7 +325,6 @@ const kRemoteViewStyleAdaptive = 'adaptive';
|
|||||||
/// [kRemoteViewStyleCustom] Show remote image at a user-defined scale percent.
|
/// [kRemoteViewStyleCustom] Show remote image at a user-defined scale percent.
|
||||||
const kRemoteViewStyleCustom = 'custom';
|
const kRemoteViewStyleCustom = 'custom';
|
||||||
|
|
||||||
|
|
||||||
/// [kRemoteScrollStyleAuto] Scroll image auto by position.
|
/// [kRemoteScrollStyleAuto] Scroll image auto by position.
|
||||||
const kRemoteScrollStyleAuto = 'scrollauto';
|
const kRemoteScrollStyleAuto = 'scrollauto';
|
||||||
|
|
||||||
@@ -361,12 +361,14 @@ const Set<PointerDeviceKind> kTouchBasedDeviceKinds = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Scale custom related constants
|
// Scale custom related constants
|
||||||
const String kCustomScalePercentKey = 'custom_scale_percent'; // Flutter option key for storing custom scale percent (integer 5-1000)
|
const String kCustomScalePercentKey =
|
||||||
|
'custom_scale_percent'; // Flutter option key for storing custom scale percent (integer 5-1000)
|
||||||
const int kScaleCustomMinPercent = 5;
|
const int kScaleCustomMinPercent = 5;
|
||||||
const int kScaleCustomPivotPercent = 100; // 100% should be at 1/3 of track
|
const int kScaleCustomPivotPercent = 100; // 100% should be at 1/3 of track
|
||||||
const int kScaleCustomMaxPercent = 1000;
|
const int kScaleCustomMaxPercent = 1000;
|
||||||
const double kScaleCustomPivotPos = 1.0 / 3.0; // first 1/3 → up to 100%
|
const double kScaleCustomPivotPos = 1.0 / 3.0; // first 1/3 → up to 100%
|
||||||
const double kScaleCustomDetentEpsilon = 0.006; // snap range around pivot (~0.6%)
|
const double kScaleCustomDetentEpsilon =
|
||||||
|
0.006; // snap range around pivot (~0.6%)
|
||||||
const Duration kDebounceCustomScaleDuration = Duration(milliseconds: 300);
|
const Duration kDebounceCustomScaleDuration = Duration(milliseconds: 300);
|
||||||
|
|
||||||
// ================================ mobile ================================
|
// ================================ mobile ================================
|
||||||
|
|||||||
@@ -561,6 +561,12 @@ class _GeneralState extends State<_General> {
|
|||||||
children.add(_OptionCheckBox(
|
children.add(_OptionCheckBox(
|
||||||
context, 'Allow linux headless', kOptionAllowLinuxHeadless));
|
context, 'Allow linux headless', kOptionAllowLinuxHeadless));
|
||||||
}
|
}
|
||||||
|
children.add(_OptionCheckBox(
|
||||||
|
context,
|
||||||
|
'note-at-conn-end-tip',
|
||||||
|
kOptionAllowAskForNoteAtEndOfConnection,
|
||||||
|
isServer: false,
|
||||||
|
));
|
||||||
return _Card(title: 'Other', children: children);
|
return _Card(title: 'Other', children: children);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1757,21 +1763,23 @@ class _DisplayState extends State<_Display> {
|
|||||||
groupValue: groupValue,
|
groupValue: groupValue,
|
||||||
label: 'Scrollbar',
|
label: 'Scrollbar',
|
||||||
onChanged: isOptFixed ? null : onChanged),
|
onChanged: isOptFixed ? null : onChanged),
|
||||||
_Radio(context,
|
if (!isWeb) ...[
|
||||||
value: kRemoteScrollStyleEdge,
|
_Radio(context,
|
||||||
groupValue: groupValue,
|
value: kRemoteScrollStyleEdge,
|
||||||
label: 'ScrollEdge',
|
groupValue: groupValue,
|
||||||
onChanged: isOptFixed ? null : onChanged),
|
label: 'ScrollEdge',
|
||||||
Offstage(
|
onChanged: isOptFixed ? null : onChanged),
|
||||||
offstage: groupValue != kRemoteScrollStyleEdge,
|
Offstage(
|
||||||
child: EdgeThicknessControl(
|
offstage: groupValue != kRemoteScrollStyleEdge,
|
||||||
value: double.tryParse(bind.mainGetUserDefaultOption(
|
child: EdgeThicknessControl(
|
||||||
key: kOptionEdgeScrollEdgeThickness)) ??
|
value: double.tryParse(bind.mainGetUserDefaultOption(
|
||||||
100.0,
|
key: kOptionEdgeScrollEdgeThickness)) ??
|
||||||
onChanged: isOptionFixed(kOptionEdgeScrollEdgeThickness)
|
100.0,
|
||||||
? null
|
onChanged: isOptionFixed(kOptionEdgeScrollEdgeThickness)
|
||||||
: onEdgeScrollEdgeThicknessChanged,
|
? null
|
||||||
)),
|
: onEdgeScrollEdgeThicknessChanged,
|
||||||
|
)),
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'dart:io';
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:extended_text/extended_text.dart';
|
import 'package:extended_text/extended_text.dart';
|
||||||
|
import 'package:flutter_hbb/common/widgets/dialog.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/dragable_divider.dart';
|
import 'package:flutter_hbb/desktop/widgets/dragable_divider.dart';
|
||||||
import 'package:percent_indicator/percent_indicator.dart';
|
import 'package:percent_indicator/percent_indicator.dart';
|
||||||
import 'package:desktop_drop/desktop_drop.dart';
|
import 'package:desktop_drop/desktop_drop.dart';
|
||||||
@@ -52,7 +53,7 @@ enum MouseFocusScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FileManagerPage extends StatefulWidget {
|
class FileManagerPage extends StatefulWidget {
|
||||||
const FileManagerPage(
|
FileManagerPage(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.password,
|
required this.password,
|
||||||
@@ -67,9 +68,16 @@ class FileManagerPage extends StatefulWidget {
|
|||||||
final bool? forceRelay;
|
final bool? forceRelay;
|
||||||
final String? connToken;
|
final String? connToken;
|
||||||
final DesktopTabController? tabController;
|
final DesktopTabController? tabController;
|
||||||
|
final SimpleWrapper<State<FileManagerPage>?> _lastState = SimpleWrapper(null);
|
||||||
|
|
||||||
|
FFI get ffi => (_lastState.value! as _FileManagerPageState)._ffi;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _FileManagerPageState();
|
State<StatefulWidget> createState() {
|
||||||
|
final state = _FileManagerPageState();
|
||||||
|
_lastState.value = state;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FileManagerPageState extends State<FileManagerPage>
|
class _FileManagerPageState extends State<FileManagerPage>
|
||||||
@@ -139,12 +147,26 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget willPopScope(Widget child) {
|
||||||
|
if (isWeb) {
|
||||||
|
return WillPopScope(
|
||||||
|
onWillPop: () async {
|
||||||
|
clientClose(_ffi.sessionId, _ffi);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
return Overlay(key: _overlayKeyState.key, initialEntries: [
|
return Overlay(key: _overlayKeyState.key, initialEntries: [
|
||||||
OverlayEntry(builder: (_) {
|
OverlayEntry(builder: (_) {
|
||||||
return Scaffold(
|
return willPopScope(Scaffold(
|
||||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
body: Row(
|
body: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -160,7 +182,7 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
Flexible(flex: 2, child: statusList())
|
Flexible(flex: 2, child: statusList())
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
));
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'dart:convert';
|
|||||||
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||||
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/common/widgets/dialog.dart';
|
||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/models/state_model.dart';
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/file_manager_page.dart';
|
import 'package:flutter_hbb/desktop/pages/file_manager_page.dart';
|
||||||
@@ -40,7 +41,15 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
|||||||
label: params['id'],
|
label: params['id'],
|
||||||
selectedIcon: selectedIcon,
|
selectedIcon: selectedIcon,
|
||||||
unselectedIcon: unselectedIcon,
|
unselectedIcon: unselectedIcon,
|
||||||
onTabCloseButton: () => tabController.closeBy(params['id']),
|
onTabCloseButton: () async {
|
||||||
|
if (await desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
id: params['id'],
|
||||||
|
tabController: tabController,
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tabController.closeBy(params['id']);
|
||||||
|
},
|
||||||
page: FileManagerPage(
|
page: FileManagerPage(
|
||||||
key: ValueKey(params['id']),
|
key: ValueKey(params['id']),
|
||||||
id: params['id'],
|
id: params['id'],
|
||||||
@@ -69,7 +78,15 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
|||||||
label: id,
|
label: id,
|
||||||
selectedIcon: selectedIcon,
|
selectedIcon: selectedIcon,
|
||||||
unselectedIcon: unselectedIcon,
|
unselectedIcon: unselectedIcon,
|
||||||
onTabCloseButton: () => tabController.closeBy(id),
|
onTabCloseButton: () async {
|
||||||
|
if (await desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
id: id,
|
||||||
|
tabController: tabController,
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tabController.closeBy(id);
|
||||||
|
},
|
||||||
page: FileManagerPage(
|
page: FileManagerPage(
|
||||||
key: ValueKey(id),
|
key: ValueKey(id),
|
||||||
id: id,
|
id: id,
|
||||||
@@ -132,6 +149,14 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
|||||||
|
|
||||||
Future<bool> handleWindowCloseButton() async {
|
Future<bool> handleWindowCloseButton() async {
|
||||||
final connLength = tabController.state.value.tabs.length;
|
final connLength = tabController.state.value.tabs.length;
|
||||||
|
if (connLength == 1) {
|
||||||
|
if (await desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
id: tabController.state.value.tabs[0].key,
|
||||||
|
tabController: tabController,
|
||||||
|
)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (connLength <= 1) {
|
if (connLength <= 1) {
|
||||||
tabController.clear();
|
tabController.clear();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class _PortForward {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PortForwardPage extends StatefulWidget {
|
class PortForwardPage extends StatefulWidget {
|
||||||
const PortForwardPage({
|
PortForwardPage({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.password,
|
required this.password,
|
||||||
@@ -42,9 +42,16 @@ class PortForwardPage extends StatefulWidget {
|
|||||||
final bool? forceRelay;
|
final bool? forceRelay;
|
||||||
final bool? isSharedPassword;
|
final bool? isSharedPassword;
|
||||||
final String? connToken;
|
final String? connToken;
|
||||||
|
final SimpleWrapper<State<PortForwardPage>?> _lastState = SimpleWrapper(null);
|
||||||
|
|
||||||
|
FFI get ffi => (_lastState.value! as _PortForwardPageState)._ffi;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PortForwardPage> createState() => _PortForwardPageState();
|
State<PortForwardPage> createState() {
|
||||||
|
final state = _PortForwardPageState();
|
||||||
|
_lastState.value = state;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PortForwardPageState extends State<PortForwardPage>
|
class _PortForwardPageState extends State<PortForwardPage>
|
||||||
|
|||||||
@@ -73,7 +73,10 @@ class RemotePage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _RemotePageState extends State<RemotePage>
|
class _RemotePageState extends State<RemotePage>
|
||||||
with AutomaticKeepAliveClientMixin, MultiWindowListener, TickerProviderStateMixin {
|
with
|
||||||
|
AutomaticKeepAliveClientMixin,
|
||||||
|
MultiWindowListener,
|
||||||
|
TickerProviderStateMixin {
|
||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
String keyboardMode = "legacy";
|
String keyboardMode = "legacy";
|
||||||
bool _isWindowBlur = false;
|
bool _isWindowBlur = false;
|
||||||
@@ -398,7 +401,7 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
super.build(context);
|
super.build(context);
|
||||||
return WillPopScope(
|
return WillPopScope(
|
||||||
onWillPop: () async {
|
onWillPop: () async {
|
||||||
clientClose(sessionId, _ffi.dialogManager);
|
clientClose(sessionId, _ffi);
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
child: MultiProvider(providers: [
|
child: MultiProvider(providers: [
|
||||||
|
|||||||
@@ -80,7 +80,15 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
label: peerId!,
|
label: peerId!,
|
||||||
selectedIcon: selectedIcon,
|
selectedIcon: selectedIcon,
|
||||||
unselectedIcon: unselectedIcon,
|
unselectedIcon: unselectedIcon,
|
||||||
onTabCloseButton: () => tabController.closeBy(peerId),
|
onTabCloseButton: () async {
|
||||||
|
if (await desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
id: peerId!,
|
||||||
|
tabController: tabController,
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tabController.closeBy(peerId!);
|
||||||
|
},
|
||||||
page: RemotePage(
|
page: RemotePage(
|
||||||
key: ValueKey(peerId),
|
key: ValueKey(peerId),
|
||||||
id: peerId!,
|
id: peerId!,
|
||||||
@@ -316,7 +324,13 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
translate('Close'),
|
translate('Close'),
|
||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
proc: () {
|
proc: () async {
|
||||||
|
if (await desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
id: key,
|
||||||
|
tabController: tabController,
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
tabController.closeBy(key);
|
tabController.closeBy(key);
|
||||||
cancelFunc();
|
cancelFunc();
|
||||||
},
|
},
|
||||||
@@ -369,6 +383,14 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
|
|
||||||
Future<bool> handleWindowCloseButton() async {
|
Future<bool> handleWindowCloseButton() async {
|
||||||
final connLength = tabController.length;
|
final connLength = tabController.length;
|
||||||
|
if (connLength == 1) {
|
||||||
|
if (await desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
id: tabController.state.value.tabs[0].key,
|
||||||
|
tabController: tabController,
|
||||||
|
)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (connLength <= 1) {
|
if (connLength <= 1) {
|
||||||
tabController.clear();
|
tabController.clear();
|
||||||
return true;
|
return true;
|
||||||
@@ -423,7 +445,15 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
label: id,
|
label: id,
|
||||||
selectedIcon: selectedIcon,
|
selectedIcon: selectedIcon,
|
||||||
unselectedIcon: unselectedIcon,
|
unselectedIcon: unselectedIcon,
|
||||||
onTabCloseButton: () => tabController.closeBy(id),
|
onTabCloseButton: () async {
|
||||||
|
if (await desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
id: id,
|
||||||
|
tabController: tabController,
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tabController.closeBy(id);
|
||||||
|
},
|
||||||
page: RemotePage(
|
page: RemotePage(
|
||||||
key: ValueKey(id),
|
key: ValueKey(id),
|
||||||
id: id,
|
id: id,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import 'package:xterm/xterm.dart';
|
|||||||
import 'terminal_connection_manager.dart';
|
import 'terminal_connection_manager.dart';
|
||||||
|
|
||||||
class TerminalPage extends StatefulWidget {
|
class TerminalPage extends StatefulWidget {
|
||||||
const TerminalPage({
|
TerminalPage({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.password,
|
required this.password,
|
||||||
@@ -25,9 +25,16 @@ class TerminalPage extends StatefulWidget {
|
|||||||
final bool? isSharedPassword;
|
final bool? isSharedPassword;
|
||||||
final String? connToken;
|
final String? connToken;
|
||||||
final int terminalId;
|
final int terminalId;
|
||||||
|
final SimpleWrapper<State<TerminalPage>?> _lastState = SimpleWrapper(null);
|
||||||
|
|
||||||
|
FFI get ffi => (_lastState.value! as _TerminalPageState)._ffi;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<TerminalPage> createState() => _TerminalPageState();
|
State<TerminalPage> createState() {
|
||||||
|
final state = _TerminalPageState();
|
||||||
|
_lastState.value = state;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TerminalPageState extends State<TerminalPage>
|
class _TerminalPageState extends State<TerminalPage>
|
||||||
@@ -59,12 +66,13 @@ class _TerminalPageState extends State<TerminalPage>
|
|||||||
// Initialize terminal connection
|
// Initialize terminal connection
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
widget.tabController.onSelected?.call(widget.id);
|
widget.tabController.onSelected?.call(widget.id);
|
||||||
|
|
||||||
// Check if this is a new connection or additional terminal
|
// Check if this is a new connection or additional terminal
|
||||||
// Note: When a connection exists, the ref count will be > 1 after this terminal is added
|
// Note: When a connection exists, the ref count will be > 1 after this terminal is added
|
||||||
final isExistingConnection = TerminalConnectionManager.hasConnection(widget.id) &&
|
final isExistingConnection =
|
||||||
TerminalConnectionManager.getTerminalCount(widget.id) > 1;
|
TerminalConnectionManager.hasConnection(widget.id) &&
|
||||||
|
TerminalConnectionManager.getTerminalCount(widget.id) > 1;
|
||||||
|
|
||||||
if (!isExistingConnection) {
|
if (!isExistingConnection) {
|
||||||
// First terminal - show loading dialog, wait for onReady
|
// First terminal - show loading dialog, wait for onReady
|
||||||
_ffi.dialogManager
|
_ffi.dialogManager
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:desktop_multi_window/desktop_multi_window.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
|
import 'package:flutter_hbb/common/widgets/dialog.dart';
|
||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/models/state_model.dart';
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||||
@@ -62,13 +63,20 @@ class _TerminalTabPageState extends State<TerminalTabPage> {
|
|||||||
}) {
|
}) {
|
||||||
final tabKey = '${peerId}_$terminalId';
|
final tabKey = '${peerId}_$terminalId';
|
||||||
final alias = bind.mainGetPeerOptionSync(id: peerId, key: 'alias');
|
final alias = bind.mainGetPeerOptionSync(id: peerId, key: 'alias');
|
||||||
final tabLabel = alias.isNotEmpty ? '$alias #$terminalId' : '$peerId #$terminalId';
|
final tabLabel =
|
||||||
|
alias.isNotEmpty ? '$alias #$terminalId' : '$peerId #$terminalId';
|
||||||
return TabInfo(
|
return TabInfo(
|
||||||
key: tabKey,
|
key: tabKey,
|
||||||
label: tabLabel,
|
label: tabLabel,
|
||||||
selectedIcon: selectedIcon,
|
selectedIcon: selectedIcon,
|
||||||
unselectedIcon: unselectedIcon,
|
unselectedIcon: unselectedIcon,
|
||||||
onTabCloseButton: () async {
|
onTabCloseButton: () async {
|
||||||
|
if (await desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
id: tabKey,
|
||||||
|
tabController: tabController,
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Close the terminal session first
|
// Close the terminal session first
|
||||||
final ffi = TerminalConnectionManager.getExistingConnection(peerId);
|
final ffi = TerminalConnectionManager.getExistingConnection(peerId);
|
||||||
if (ffi != null) {
|
if (ffi != null) {
|
||||||
@@ -409,6 +417,14 @@ class _TerminalTabPageState extends State<TerminalTabPage> {
|
|||||||
|
|
||||||
Future<bool> handleWindowCloseButton() async {
|
Future<bool> handleWindowCloseButton() async {
|
||||||
final connLength = tabController.state.value.tabs.length;
|
final connLength = tabController.state.value.tabs.length;
|
||||||
|
if (connLength == 1) {
|
||||||
|
if (await desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
id: tabController.state.value.tabs[0].key,
|
||||||
|
tabController: tabController,
|
||||||
|
)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (connLength <= 1) {
|
if (connLength <= 1) {
|
||||||
tabController.clear();
|
tabController.clear();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ class _ViewCameraPageState extends State<ViewCameraPage>
|
|||||||
super.build(context);
|
super.build(context);
|
||||||
return WillPopScope(
|
return WillPopScope(
|
||||||
onWillPop: () async {
|
onWillPop: () async {
|
||||||
clientClose(sessionId, _ffi.dialogManager);
|
clientClose(sessionId, _ffi);
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
child: MultiProvider(providers: [
|
child: MultiProvider(providers: [
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:desktop_multi_window/desktop_multi_window.dart';
|
|||||||
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/common/shared_state.dart';
|
import 'package:flutter_hbb/common/shared_state.dart';
|
||||||
|
import 'package:flutter_hbb/common/widgets/dialog.dart';
|
||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/models/input_model.dart';
|
import 'package:flutter_hbb/models/input_model.dart';
|
||||||
import 'package:flutter_hbb/models/state_model.dart';
|
import 'package:flutter_hbb/models/state_model.dart';
|
||||||
@@ -79,7 +80,15 @@ class _ViewCameraTabPageState extends State<ViewCameraTabPage> {
|
|||||||
label: peerId!,
|
label: peerId!,
|
||||||
selectedIcon: selectedIcon,
|
selectedIcon: selectedIcon,
|
||||||
unselectedIcon: unselectedIcon,
|
unselectedIcon: unselectedIcon,
|
||||||
onTabCloseButton: () => tabController.closeBy(peerId),
|
onTabCloseButton: () async {
|
||||||
|
if (await desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
id: peerId!,
|
||||||
|
tabController: tabController,
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tabController.closeBy(peerId!);
|
||||||
|
},
|
||||||
page: ViewCameraPage(
|
page: ViewCameraPage(
|
||||||
key: ValueKey(peerId),
|
key: ValueKey(peerId),
|
||||||
id: peerId!,
|
id: peerId!,
|
||||||
@@ -287,7 +296,13 @@ class _ViewCameraTabPageState extends State<ViewCameraTabPage> {
|
|||||||
translate('Close'),
|
translate('Close'),
|
||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
proc: () {
|
proc: () async {
|
||||||
|
if (await desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
id: key,
|
||||||
|
tabController: tabController,
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
tabController.closeBy(key);
|
tabController.closeBy(key);
|
||||||
cancelFunc();
|
cancelFunc();
|
||||||
},
|
},
|
||||||
@@ -340,6 +355,14 @@ class _ViewCameraTabPageState extends State<ViewCameraTabPage> {
|
|||||||
|
|
||||||
Future<bool> handleWindowCloseButton() async {
|
Future<bool> handleWindowCloseButton() async {
|
||||||
final connLength = tabController.length;
|
final connLength = tabController.length;
|
||||||
|
if (connLength == 1) {
|
||||||
|
if (await desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
id: tabController.state.value.tabs[0].key,
|
||||||
|
tabController: tabController,
|
||||||
|
)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (connLength <= 1) {
|
if (connLength <= 1) {
|
||||||
tabController.clear();
|
tabController.clear();
|
||||||
return true;
|
return true;
|
||||||
@@ -393,7 +416,15 @@ class _ViewCameraTabPageState extends State<ViewCameraTabPage> {
|
|||||||
label: id,
|
label: id,
|
||||||
selectedIcon: selectedIcon,
|
selectedIcon: selectedIcon,
|
||||||
unselectedIcon: unselectedIcon,
|
unselectedIcon: unselectedIcon,
|
||||||
onTabCloseButton: () => tabController.closeBy(id),
|
onTabCloseButton: () async {
|
||||||
|
if (await desktopTryShowTabAuditDialogCloseCancelled(
|
||||||
|
id: id,
|
||||||
|
tabController: tabController,
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tabController.closeBy(id);
|
||||||
|
},
|
||||||
page: ViewCameraPage(
|
page: ViewCameraPage(
|
||||||
key: ValueKey(id),
|
key: ValueKey(id),
|
||||||
id: id,
|
id: id,
|
||||||
|
|||||||
@@ -1122,23 +1122,25 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
closeOnActivate: groupValue != kRemoteScrollStyleEdge,
|
closeOnActivate: groupValue != kRemoteScrollStyleEdge,
|
||||||
ffi: widget.ffi,
|
ffi: widget.ffi,
|
||||||
),
|
),
|
||||||
RdoMenuButton<String>(
|
if (!isWeb) ...[
|
||||||
child: Text(translate('ScrollEdge')),
|
RdoMenuButton<String>(
|
||||||
value: kRemoteScrollStyleEdge,
|
child: Text(translate('ScrollEdge')),
|
||||||
groupValue: groupValue,
|
value: kRemoteScrollStyleEdge,
|
||||||
closeOnActivate: false,
|
groupValue: groupValue,
|
||||||
onChanged: widget.ffi.canvasModel.imageOverflow.value
|
closeOnActivate: false,
|
||||||
? (value) => onChangeScrollStyle(value)
|
onChanged: widget.ffi.canvasModel.imageOverflow.value
|
||||||
: null,
|
? (value) => onChangeScrollStyle(value)
|
||||||
ffi: widget.ffi,
|
: null,
|
||||||
),
|
ffi: widget.ffi,
|
||||||
Offstage(
|
),
|
||||||
offstage: groupValue != kRemoteScrollStyleEdge,
|
Offstage(
|
||||||
child: EdgeThicknessControl(
|
offstage: groupValue != kRemoteScrollStyleEdge,
|
||||||
value: edgeScrollEdgeThickness.toDouble(),
|
child: EdgeThicknessControl(
|
||||||
onChanged: onChangeEdgeScrollEdgeThickness,
|
value: edgeScrollEdgeThickness.toDouble(),
|
||||||
colorScheme: colorScheme,
|
onChanged: onChangeEdgeScrollEdgeThickness,
|
||||||
)),
|
colorScheme: colorScheme,
|
||||||
|
)),
|
||||||
|
],
|
||||||
Divider(),
|
Divider(),
|
||||||
]));
|
]));
|
||||||
});
|
});
|
||||||
@@ -2163,7 +2165,12 @@ class _CloseMenu extends StatelessWidget {
|
|||||||
return _IconMenuButton(
|
return _IconMenuButton(
|
||||||
assetName: 'assets/close.svg',
|
assetName: 'assets/close.svg',
|
||||||
tooltip: 'Close',
|
tooltip: 'Close',
|
||||||
onPressed: () => closeConnection(id: id),
|
onPressed: () async {
|
||||||
|
if (await showConnEndAuditDialogCloseCanceled(ffi: ffi)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
closeConnection(id: id);
|
||||||
|
},
|
||||||
color: _ToolbarTheme.redColor,
|
color: _ToolbarTheme.redColor,
|
||||||
hoverColor: _ToolbarTheme.hoverRedColor,
|
hoverColor: _ToolbarTheme.hoverRedColor,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,7 +12,11 @@ import '../../common/widgets/dialog.dart';
|
|||||||
|
|
||||||
class FileManagerPage extends StatefulWidget {
|
class FileManagerPage extends StatefulWidget {
|
||||||
FileManagerPage(
|
FileManagerPage(
|
||||||
{Key? key, required this.id, this.password, this.isSharedPassword, this.forceRelay})
|
{Key? key,
|
||||||
|
required this.id,
|
||||||
|
this.password,
|
||||||
|
this.isSharedPassword,
|
||||||
|
this.forceRelay})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
final String id;
|
final String id;
|
||||||
final String? password;
|
final String? password;
|
||||||
@@ -113,8 +117,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
|
|||||||
leading: Row(children: [
|
leading: Row(children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.close),
|
icon: Icon(Icons.close),
|
||||||
onPressed: () =>
|
onPressed: () => clientClose(gFFI.sessionId, gFFI)),
|
||||||
clientClose(gFFI.sessionId, gFFI.dialogManager)),
|
|
||||||
]),
|
]),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
title: ToggleSwitch(
|
title: ToggleSwitch(
|
||||||
@@ -591,67 +594,67 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
|
|
||||||
Widget headTools() => Container(
|
Widget headTools() => Container(
|
||||||
child: Row(
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: Obx(() {
|
||||||
|
final home = controller.options.value.home;
|
||||||
|
final isWindows = controller.options.value.isWindows;
|
||||||
|
return BreadCrumb(
|
||||||
|
items: getPathBreadCrumbItems(controller.shortPath, isWindows,
|
||||||
|
() => controller.goToHomeDirectory(), (list) {
|
||||||
|
var path = "";
|
||||||
|
if (home.startsWith(list[0])) {
|
||||||
|
// absolute path
|
||||||
|
for (var item in list) {
|
||||||
|
path = PathUtil.join(path, item, isWindows);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
path += home;
|
||||||
|
for (var item in list) {
|
||||||
|
path = PathUtil.join(path, item, isWindows);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
controller.openDirectory(path);
|
||||||
|
}),
|
||||||
|
divider: Icon(Icons.chevron_right),
|
||||||
|
overflow: ScrollableOverflow(controller: _breadCrumbScroller),
|
||||||
|
);
|
||||||
|
})),
|
||||||
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: Obx(() {
|
IconButton(
|
||||||
final home = controller.options.value.home;
|
icon: Icon(Icons.arrow_back),
|
||||||
final isWindows = controller.options.value.isWindows;
|
onPressed: controller.goBack,
|
||||||
return BreadCrumb(
|
),
|
||||||
items: getPathBreadCrumbItems(controller.shortPath, isWindows,
|
IconButton(
|
||||||
() => controller.goToHomeDirectory(), (list) {
|
icon: Icon(Icons.arrow_upward),
|
||||||
var path = "";
|
onPressed: controller.goToParentDirectory,
|
||||||
if (home.startsWith(list[0])) {
|
),
|
||||||
// absolute path
|
PopupMenuButton<SortBy>(
|
||||||
for (var item in list) {
|
tooltip: "",
|
||||||
path = PathUtil.join(path, item, isWindows);
|
icon: Icon(Icons.sort),
|
||||||
}
|
itemBuilder: (context) {
|
||||||
|
return SortBy.values
|
||||||
|
.map((e) => PopupMenuItem(
|
||||||
|
child: Text(translate(e.toString())),
|
||||||
|
value: e,
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
},
|
||||||
|
onSelected: (sortBy) {
|
||||||
|
// If selecting the same sort option, flip the order
|
||||||
|
// If selecting a different sort option, use ascending order
|
||||||
|
if (controller.sortBy.value == sortBy) {
|
||||||
|
ascending.value = !controller.sortAscending;
|
||||||
} else {
|
} else {
|
||||||
path += home;
|
ascending.value = true;
|
||||||
for (var item in list) {
|
|
||||||
path = PathUtil.join(path, item, isWindows);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
controller.openDirectory(path);
|
controller.changeSortStyle(sortBy,
|
||||||
|
ascending: ascending.value);
|
||||||
}),
|
}),
|
||||||
divider: Icon(Icons.chevron_right),
|
|
||||||
overflow: ScrollableOverflow(controller: _breadCrumbScroller),
|
|
||||||
);
|
|
||||||
})),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.arrow_back),
|
|
||||||
onPressed: controller.goBack,
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.arrow_upward),
|
|
||||||
onPressed: controller.goToParentDirectory,
|
|
||||||
),
|
|
||||||
PopupMenuButton<SortBy>(
|
|
||||||
tooltip: "",
|
|
||||||
icon: Icon(Icons.sort),
|
|
||||||
itemBuilder: (context) {
|
|
||||||
return SortBy.values
|
|
||||||
.map((e) => PopupMenuItem(
|
|
||||||
child: Text(translate(e.toString())),
|
|
||||||
value: e,
|
|
||||||
))
|
|
||||||
.toList();
|
|
||||||
},
|
|
||||||
onSelected: (sortBy) {
|
|
||||||
// If selecting the same sort option, flip the order
|
|
||||||
// If selecting a different sort option, use ascending order
|
|
||||||
if (controller.sortBy.value == sortBy) {
|
|
||||||
ascending.value = !controller.sortAscending;
|
|
||||||
} else {
|
|
||||||
ascending.value = true;
|
|
||||||
}
|
|
||||||
controller.changeSortStyle(sortBy, ascending: ascending.value);
|
|
||||||
}
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
));
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
|
||||||
Widget listTail() => Obx(() => Container(
|
Widget listTail() => Obx(() => Container(
|
||||||
height: 100,
|
height: 100,
|
||||||
|
|||||||
@@ -366,7 +366,7 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
|
|||||||
|
|
||||||
return WillPopScope(
|
return WillPopScope(
|
||||||
onWillPop: () async {
|
onWillPop: () async {
|
||||||
clientClose(sessionId, gFFI.dialogManager);
|
clientClose(sessionId, gFFI);
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
@@ -484,7 +484,7 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
icon: Icon(Icons.clear),
|
icon: Icon(Icons.clear),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
clientClose(sessionId, gFFI.dialogManager);
|
clientClose(sessionId, gFFI);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
var _disableUdp = false;
|
var _disableUdp = false;
|
||||||
var _enableIpv6Punch = false;
|
var _enableIpv6Punch = false;
|
||||||
var _isUsingPublicServer = false;
|
var _isUsingPublicServer = false;
|
||||||
|
var _allowAskForNoteAtEndOfConnection = false;
|
||||||
|
|
||||||
_SettingsState() {
|
_SettingsState() {
|
||||||
_enableAbr = option2bool(
|
_enableAbr = option2bool(
|
||||||
@@ -136,6 +137,8 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
_enableTrustedDevices = mainGetBoolOptionSync(kOptionEnableTrustedDevices);
|
_enableTrustedDevices = mainGetBoolOptionSync(kOptionEnableTrustedDevices);
|
||||||
_enableUdpPunch = mainGetLocalBoolOptionSync(kOptionEnableUdpPunch);
|
_enableUdpPunch = mainGetLocalBoolOptionSync(kOptionEnableUdpPunch);
|
||||||
_enableIpv6Punch = mainGetLocalBoolOptionSync(kOptionEnableIpv6Punch);
|
_enableIpv6Punch = mainGetLocalBoolOptionSync(kOptionEnableIpv6Punch);
|
||||||
|
_allowAskForNoteAtEndOfConnection =
|
||||||
|
mainGetLocalBoolOptionSync(kOptionAllowAskForNoteAtEndOfConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -782,6 +785,19 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
showThemeSettings(gFFI.dialogManager);
|
showThemeSettings(gFFI.dialogManager);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
|
SettingsTile.switchTile(
|
||||||
|
title: Text(translate('note-at-conn-end-tip')),
|
||||||
|
initialValue: _allowAskForNoteAtEndOfConnection,
|
||||||
|
onToggle: (v) async {
|
||||||
|
await mainSetLocalBoolOption(
|
||||||
|
kOptionAllowAskForNoteAtEndOfConnection, v);
|
||||||
|
final newValue = mainGetLocalBoolOptionSync(
|
||||||
|
kOptionAllowAskForNoteAtEndOfConnection);
|
||||||
|
setState(() {
|
||||||
|
_allowAskForNoteAtEndOfConnection = newValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
if (isAndroid)
|
if (isAndroid)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
|
import 'package:flutter_hbb/common/widgets/dialog.dart';
|
||||||
import 'package:flutter_hbb/models/model.dart';
|
import 'package:flutter_hbb/models/model.dart';
|
||||||
import 'package:flutter_hbb/models/terminal_model.dart';
|
import 'package:flutter_hbb/models/terminal_model.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
@@ -38,6 +39,8 @@ class _TerminalPageState extends State<TerminalPage>
|
|||||||
? (GoogleFonts.robotoMono().fontFamily ?? 'monospace')
|
? (GoogleFonts.robotoMono().fontFamily ?? 'monospace')
|
||||||
: 'monospace';
|
: 'monospace';
|
||||||
|
|
||||||
|
SessionID get sessionId => _ffi.sessionId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -82,6 +85,16 @@ class _TerminalPageState extends State<TerminalPage>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
|
return WillPopScope(
|
||||||
|
onWillPop: () async {
|
||||||
|
clientClose(sessionId, _ffi);
|
||||||
|
return false; // Prevent default back behavior
|
||||||
|
},
|
||||||
|
child: buildBody(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildBody() {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
body: TerminalView(
|
body: TerminalView(
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ class _ViewCameraPageState extends State<ViewCameraPage>
|
|||||||
|
|
||||||
return WillPopScope(
|
return WillPopScope(
|
||||||
onWillPop: () async {
|
onWillPop: () async {
|
||||||
clientClose(sessionId, gFFI.dialogManager);
|
clientClose(sessionId, gFFI);
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
@@ -310,7 +310,7 @@ class _ViewCameraPageState extends State<ViewCameraPage>
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
icon: Icon(Icons.clear),
|
icon: Icon(Icons.clear),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
clientClose(sessionId, gFFI.dialogManager);
|
clientClose(sessionId, gFFI);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import 'package:flutter_hbb/plugin/manager.dart';
|
|||||||
import 'package:flutter_hbb/plugin/widgets/desc_ui.dart';
|
import 'package:flutter_hbb/plugin/widgets/desc_ui.dart';
|
||||||
import 'package:flutter_hbb/common/shared_state.dart';
|
import 'package:flutter_hbb/common/shared_state.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
|
import 'package:flutter_hbb/utils/http_service.dart' as http;
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
import 'package:image/image.dart' as img2;
|
import 'package:image/image.dart' as img2;
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
@@ -933,11 +934,21 @@ class FfiModel with ChangeNotifier {
|
|||||||
/// Show a message box with [type], [title] and [text].
|
/// Show a message box with [type], [title] and [text].
|
||||||
showMsgBox(SessionID sessionId, String type, String title, String text,
|
showMsgBox(SessionID sessionId, String type, String title, String text,
|
||||||
String link, bool hasRetry, OverlayDialogManager dialogManager,
|
String link, bool hasRetry, OverlayDialogManager dialogManager,
|
||||||
{bool? hasCancel}) {
|
{bool? hasCancel}) async {
|
||||||
msgBox(sessionId, type, title, text, link, dialogManager,
|
final showNoteEdit = parent.target != null &&
|
||||||
hasCancel: hasCancel,
|
allowAskForNoteAtEndOfConnection(parent.target, false) &&
|
||||||
reconnect: hasRetry ? reconnect : null,
|
(title == "Connection Error" || type == "restarting") &&
|
||||||
reconnectTimeout: hasRetry ? _reconnects : null);
|
!hasRetry;
|
||||||
|
if (showNoteEdit) {
|
||||||
|
await showConnEndAuditDialogCloseCanceled(
|
||||||
|
ffi: parent.target!, type: type, title: title, text: text);
|
||||||
|
closeConnection();
|
||||||
|
} else {
|
||||||
|
msgBox(sessionId, type, title, text, link, dialogManager,
|
||||||
|
hasCancel: hasCancel,
|
||||||
|
reconnect: hasRetry ? reconnect : null,
|
||||||
|
reconnectTimeout: hasRetry ? _reconnects : null);
|
||||||
|
}
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
if (hasRetry) {
|
if (hasRetry) {
|
||||||
_timer = Timer(Duration(seconds: _reconnects), () {
|
_timer = Timer(Duration(seconds: _reconnects), () {
|
||||||
@@ -958,8 +969,30 @@ class FfiModel with ChangeNotifier {
|
|||||||
onCancel: closeConnection);
|
onCancel: closeConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showRelayHintDialog(SessionID sessionId, String type, String title,
|
Future<void> showRelayHintDialog(
|
||||||
String text, OverlayDialogManager dialogManager, String peerId) {
|
SessionID sessionId,
|
||||||
|
String type,
|
||||||
|
String title,
|
||||||
|
String text,
|
||||||
|
OverlayDialogManager dialogManager,
|
||||||
|
String peerId) async {
|
||||||
|
var hint = "\n\n${translate('relay_hint_tip')}";
|
||||||
|
if (text.contains("10054") || text.contains("104")) {
|
||||||
|
hint = "";
|
||||||
|
}
|
||||||
|
final text2 = "${translate(text)}$hint";
|
||||||
|
|
||||||
|
if (parent.target != null &&
|
||||||
|
allowAskForNoteAtEndOfConnection(parent.target, false) &&
|
||||||
|
pi.isSet.isTrue) {
|
||||||
|
if (await showConnEndAuditDialogCloseCanceled(
|
||||||
|
ffi: parent.target!, type: type, title: title, text: text2)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
closeConnection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dialogManager.show(tag: '$sessionId-$type', (setState, close, context) {
|
dialogManager.show(tag: '$sessionId-$type', (setState, close, context) {
|
||||||
onClose() {
|
onClose() {
|
||||||
closeConnection();
|
closeConnection();
|
||||||
@@ -968,13 +1001,10 @@ class FfiModel with ChangeNotifier {
|
|||||||
|
|
||||||
final style =
|
final style =
|
||||||
ElevatedButton.styleFrom(backgroundColor: Colors.green[700]);
|
ElevatedButton.styleFrom(backgroundColor: Colors.green[700]);
|
||||||
var hint = "\n\n${translate('relay_hint_tip')}";
|
|
||||||
if (text.contains("10054") || text.contains("104")) {
|
|
||||||
hint = "";
|
|
||||||
}
|
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
title: null,
|
title: null,
|
||||||
content: msgboxContent(type, title, "${translate(text)}$hint"),
|
content: msgboxContent(type, title, text2),
|
||||||
actions: [
|
actions: [
|
||||||
dialogButton('Close', onPressed: onClose, isOutline: true),
|
dialogButton('Close', onPressed: onClose, isOutline: true),
|
||||||
if (type == 'relay-hint')
|
if (type == 'relay-hint')
|
||||||
@@ -1064,10 +1094,91 @@ class FfiModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _queryAuditGuid(String peerId) async {
|
||||||
|
try {
|
||||||
|
if (!mainGetLocalBoolOptionSync(
|
||||||
|
kOptionAllowAskForNoteAtEndOfConnection)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bind.sessionGetAuditGuid(sessionId: sessionId).isNotEmpty) {
|
||||||
|
debugPrint('Get cached audit GUID');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final url = bind.sessionGetAuditServerSync(
|
||||||
|
sessionId: sessionId, typ: "conn/active");
|
||||||
|
if (url.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final initialConnSessionId =
|
||||||
|
bind.sessionGetConnSessionId(sessionId: sessionId);
|
||||||
|
final connType = switch (parent.target?.connType) {
|
||||||
|
ConnType.defaultConn => 0,
|
||||||
|
ConnType.fileTransfer => 1,
|
||||||
|
ConnType.portForward => 2,
|
||||||
|
ConnType.rdp => 2,
|
||||||
|
ConnType.viewCamera => 3,
|
||||||
|
ConnType.terminal => 4,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const retryIntervals = [1, 1, 2, 2, 3, 3];
|
||||||
|
|
||||||
|
for (int attempt = 1; attempt <= retryIntervals.length; attempt++) {
|
||||||
|
final currentConnSessionId =
|
||||||
|
bind.sessionGetConnSessionId(sessionId: sessionId);
|
||||||
|
if (currentConnSessionId != initialConnSessionId) {
|
||||||
|
debugPrint('connSessionId changed, stopping audit GUID query');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final fullUrl =
|
||||||
|
'$url?id=$peerId&session_id=$currentConnSessionId&conn_type=$connType';
|
||||||
|
|
||||||
|
debugPrint(
|
||||||
|
'Querying audit GUID, attempt $attempt/${retryIntervals.length}');
|
||||||
|
try {
|
||||||
|
var headers = getHttpHeaders();
|
||||||
|
headers['Content-Type'] = "application/json";
|
||||||
|
|
||||||
|
final response = await http.get(
|
||||||
|
Uri.parse(fullUrl),
|
||||||
|
headers: headers,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final guid = jsonDecode(response.body) as String?;
|
||||||
|
if (guid != null && guid.isNotEmpty) {
|
||||||
|
bind.sessionSetAuditGuid(sessionId: sessionId, guid: guid);
|
||||||
|
debugPrint('Successfully retrieved audit GUID');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debugPrint(
|
||||||
|
'Failed to query audit GUID. Status: ${response.statusCode}, Body: ${response.body}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error querying audit GUID (attempt $attempt): $e');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attempt < retryIntervals.length) {
|
||||||
|
await Future.delayed(Duration(seconds: retryIntervals[attempt - 1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debugPrint(
|
||||||
|
'Failed to retrieve audit GUID after ${retryIntervals.length} attempts');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error in _queryAuditGuid: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle the peer info event based on [evt].
|
/// Handle the peer info event based on [evt].
|
||||||
handlePeerInfo(Map<String, dynamic> evt, String peerId, bool isCache) async {
|
handlePeerInfo(Map<String, dynamic> evt, String peerId, bool isCache) async {
|
||||||
parent.target?.chatModel.voiceCallStatus.value = VoiceCallStatus.notStarted;
|
parent.target?.chatModel.voiceCallStatus.value = VoiceCallStatus.notStarted;
|
||||||
|
|
||||||
|
_queryAuditGuid(peerId);
|
||||||
|
|
||||||
// This call is to ensuer the keyboard mode is updated depending on the peer version.
|
// This call is to ensuer the keyboard mode is updated depending on the peer version.
|
||||||
parent.target?.inputModel.updateKeyboardMode();
|
parent.target?.inputModel.updateKeyboardMode();
|
||||||
|
|
||||||
@@ -2096,9 +2207,8 @@ class CanvasModel with ChangeNotifier {
|
|||||||
Future<void> updateScrollStyle() async {
|
Future<void> updateScrollStyle() async {
|
||||||
final style = await bind.sessionGetScrollStyle(sessionId: sessionId);
|
final style = await bind.sessionGetScrollStyle(sessionId: sessionId);
|
||||||
|
|
||||||
_scrollStyle = style != null
|
_scrollStyle =
|
||||||
? ScrollStyle.fromString(style)
|
style != null ? ScrollStyle.fromString(style) : ScrollStyle.scrollauto;
|
||||||
: ScrollStyle.scrollauto;
|
|
||||||
|
|
||||||
if (_scrollStyle != ScrollStyle.scrollauto) {
|
if (_scrollStyle != ScrollStyle.scrollauto) {
|
||||||
_resetScroll();
|
_resetScroll();
|
||||||
@@ -2108,7 +2218,8 @@ class CanvasModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initializeEdgeScrollEdgeThickness() async {
|
Future<void> initializeEdgeScrollEdgeThickness() async {
|
||||||
final savedValue = await bind.sessionGetEdgeScrollEdgeThickness(sessionId: sessionId);
|
final savedValue =
|
||||||
|
await bind.sessionGetEdgeScrollEdgeThickness(sessionId: sessionId);
|
||||||
|
|
||||||
if (savedValue != null) {
|
if (savedValue != null) {
|
||||||
_edgeScrollEdgeThickness = savedValue;
|
_edgeScrollEdgeThickness = savedValue;
|
||||||
@@ -2223,12 +2334,12 @@ class CanvasModel with ChangeNotifier {
|
|||||||
|
|
||||||
(Vector2, Vector2) getScrollInfo() {
|
(Vector2, Vector2) getScrollInfo() {
|
||||||
final scrollPixel = Vector2(
|
final scrollPixel = Vector2(
|
||||||
_horizontal.hasClients ? _horizontal.position.pixels : 0,
|
_horizontal.hasClients ? _horizontal.position.pixels : 0,
|
||||||
_vertical.hasClients ? _vertical.position.pixels : 0);
|
_vertical.hasClients ? _vertical.position.pixels : 0);
|
||||||
|
|
||||||
final max = Vector2(
|
final max = Vector2(
|
||||||
_horizontal.hasClients ? _horizontal.position.maxScrollExtent : 0,
|
_horizontal.hasClients ? _horizontal.position.maxScrollExtent : 0,
|
||||||
_vertical.hasClients ? _vertical.position.maxScrollExtent : 0);
|
_vertical.hasClients ? _vertical.position.maxScrollExtent : 0);
|
||||||
|
|
||||||
return (scrollPixel, max);
|
return (scrollPixel, max);
|
||||||
}
|
}
|
||||||
@@ -3310,7 +3421,6 @@ class FFI {
|
|||||||
var version = '';
|
var version = '';
|
||||||
var connType = ConnType.defaultConn;
|
var connType = ConnType.defaultConn;
|
||||||
var closed = false;
|
var closed = false;
|
||||||
var auditNote = '';
|
|
||||||
|
|
||||||
/// dialogManager use late to ensure init after main page binding [globalKey]
|
/// dialogManager use late to ensure init after main page binding [globalKey]
|
||||||
late final dialogManager = OverlayDialogManager();
|
late final dialogManager = OverlayDialogManager();
|
||||||
@@ -3401,7 +3511,6 @@ class FFI {
|
|||||||
List<int>? displays,
|
List<int>? displays,
|
||||||
}) {
|
}) {
|
||||||
closed = false;
|
closed = false;
|
||||||
auditNote = '';
|
|
||||||
if (isMobile) mobileReset();
|
if (isMobile) mobileReset();
|
||||||
assert(
|
assert(
|
||||||
(!(isPortForward && isViewCamera)) &&
|
(!(isPortForward && isViewCamera)) &&
|
||||||
|
|||||||
@@ -1979,5 +1979,41 @@ class RustdeskImpl {
|
|||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<int?> sessionGetEdgeScrollEdgeThickness(
|
||||||
|
{required UuidValue sessionId, dynamic hint}) {
|
||||||
|
final thickness = js.context.callMethod(
|
||||||
|
'getByName', ['option:session', 'edge-scroll-edge-thickness']);
|
||||||
|
return Future(() => int.tryParse(thickness) ?? 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> sessionSetEdgeScrollEdgeThickness(
|
||||||
|
{required UuidValue sessionId, required int value, dynamic hint}) {
|
||||||
|
return Future(() => js.context.callMethod('setByName',
|
||||||
|
['option:session', 'edge-scroll-edge-thickness', value.toString()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
String sessionGetConnSessionId({required UuidValue sessionId, dynamic hint}) {
|
||||||
|
return js.context.callMethod('getByName', ['conn_session_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool willSessionCloseCloseSession(
|
||||||
|
{required UuidValue sessionId, dynamic hint}) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String sessionGetLastAuditNote({required UuidValue sessionId, dynamic hint}) {
|
||||||
|
return js.context.callMethod('getByName', ['last_audit_note']);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> sessionSetAuditGuid(
|
||||||
|
{required UuidValue sessionId, required String guid, dynamic hint}) {
|
||||||
|
return Future(
|
||||||
|
() => js.context.callMethod('setByName', ['audit_guid', guid]));
|
||||||
|
}
|
||||||
|
|
||||||
|
String sessionGetAuditGuid({required UuidValue sessionId, dynamic hint}) {
|
||||||
|
return js.context.callMethod('getByName', ['audit_guid']);
|
||||||
|
}
|
||||||
|
|
||||||
void dispose() {}
|
void dispose() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2103,6 +2103,26 @@ pub mod sessions {
|
|||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if removing a session by session_id would result in removing the entire peer.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// - `true`: The session exists and removing it would leave the peer with no other sessions,
|
||||||
|
/// so the entire peer would be removed (equivalent to `remove_session_by_session_id` returning `Some`)
|
||||||
|
/// - `false`: The session doesn't exist, or it exists but the peer has other sessions,
|
||||||
|
/// so the peer would not be removed (equivalent to `remove_session_by_session_id` returning `None`)
|
||||||
|
#[inline]
|
||||||
|
pub fn would_remove_peer_by_session_id(id: &SessionID) -> bool {
|
||||||
|
for (_peer_key, s) in SESSIONS.read().unwrap().iter() {
|
||||||
|
let read_lock = s.ui_handler.session_handlers.read().unwrap();
|
||||||
|
if read_lock.contains_key(id) {
|
||||||
|
// Found the session, check if it's the only one for this peer
|
||||||
|
return read_lock.len() == 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Session not found
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn check_remove_unused_displays(
|
fn check_remove_unused_displays(
|
||||||
current: Option<usize>,
|
current: Option<usize>,
|
||||||
session_id: &SessionID,
|
session_id: &SessionID,
|
||||||
|
|||||||
@@ -254,6 +254,10 @@ pub fn session_get_enable_trusted_devices(session_id: SessionID) -> SyncReturn<b
|
|||||||
SyncReturn(v)
|
SyncReturn(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn will_session_close_close_session(session_id: SessionID) -> SyncReturn<bool> {
|
||||||
|
SyncReturn(sessions::would_remove_peer_by_session_id(&session_id))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session_close(session_id: SessionID) {
|
pub fn session_close(session_id: SessionID) {
|
||||||
if let Some(session) = sessions::remove_session_by_session_id(&session_id) {
|
if let Some(session) = sessions::remove_session_by_session_id(&session_id) {
|
||||||
// `release_remote_keys` is not required for mobile platforms in common cases.
|
// `release_remote_keys` is not required for mobile platforms in common cases.
|
||||||
@@ -1777,6 +1781,36 @@ pub fn session_send_note(session_id: SessionID, note: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn session_get_last_audit_note(session_id: SessionID) -> SyncReturn<String> {
|
||||||
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
|
SyncReturn(session.last_audit_note.lock().unwrap().clone())
|
||||||
|
} else {
|
||||||
|
SyncReturn("".to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn session_set_audit_guid(session_id: SessionID, guid: String) {
|
||||||
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
|
*session.audit_guid.lock().unwrap() = guid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn session_get_audit_guid(session_id: SessionID) -> SyncReturn<String> {
|
||||||
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
|
SyncReturn(session.audit_guid.lock().unwrap().clone())
|
||||||
|
} else {
|
||||||
|
SyncReturn("".to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn session_get_conn_session_id(session_id: SessionID) -> SyncReturn<String> {
|
||||||
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
|
SyncReturn(session.lc.read().unwrap().session_id.to_string())
|
||||||
|
} else {
|
||||||
|
SyncReturn("".to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session_alternative_codecs(session_id: SessionID) -> String {
|
pub fn session_alternative_codecs(session_id: SessionID) -> String {
|
||||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
let (vp8, av1, h264, h265) = session.alternative_codecs();
|
let (vp8, av1, h264, h265) = session.alternative_codecs();
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", "禁用 UDP"),
|
("Disable UDP", "禁用 UDP"),
|
||||||
("disable-udp-tip", "控制是否仅使用TCP。\n启用此选项后,RustDesk 将不再使用UDP 21116,而是使用TCP 21116。"),
|
("disable-udp-tip", "控制是否仅使用TCP。\n启用此选项后,RustDesk 将不再使用UDP 21116,而是使用TCP 21116。"),
|
||||||
("server-oss-not-support-tip", "注意:RustDesk 开源服务器(OSS server) 不包含此功能。"),
|
("server-oss-not-support-tip", "注意:RustDesk 开源服务器(OSS server) 不包含此功能。"),
|
||||||
|
("input note here", "输入备注"),
|
||||||
|
("note-at-conn-end-tip", "在连接结束时请求备注"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", "UDP deaktivieren"),
|
("Disable UDP", "UDP deaktivieren"),
|
||||||
("disable-udp-tip", "Legt fest, ob nur TCP verwendet werden soll. Wenn diese Option aktiviert ist, verwendet RustDesk nicht mehr UDP 21116, sondern stattdessen TCP 21116."),
|
("disable-udp-tip", "Legt fest, ob nur TCP verwendet werden soll. Wenn diese Option aktiviert ist, verwendet RustDesk nicht mehr UDP 21116, sondern stattdessen TCP 21116."),
|
||||||
("server-oss-not-support-tip", "HINWEIS: RustDesk Server OSS enthält diese Funktion nicht."),
|
("server-oss-not-support-tip", "HINWEIS: RustDesk Server OSS enthält diese Funktion nicht."),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,5 +261,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("allow-insecure-tls-fallback-tip", "By default, RustDesk verifies the server certificate for protocols using TLS.\nWith this option enabled, RustDesk will fall back to skipping the verification step and proceed in case of verification failure."),
|
("allow-insecure-tls-fallback-tip", "By default, RustDesk verifies the server certificate for protocols using TLS.\nWith this option enabled, RustDesk will fall back to skipping the verification step and proceed in case of verification failure."),
|
||||||
("disable-udp-tip", "Controls whether to use TCP only.\nWhen this option enabled, RustDesk will not use UDP 21116 any more, TCP 21116 will be used instead."),
|
("disable-udp-tip", "Controls whether to use TCP only.\nWhen this option enabled, RustDesk will not use UDP 21116 any more, TCP 21116 will be used instead."),
|
||||||
("server-oss-not-support-tip", "NOTE: RustDesk server OSS doesn't include this feature."),
|
("server-oss-not-support-tip", "NOTE: RustDesk server OSS doesn't include this feature."),
|
||||||
|
("note-at-conn-end-tip", "Ask for note at end of connection"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", "Désactiver UDP"),
|
("Disable UDP", "Désactiver UDP"),
|
||||||
("disable-udp-tip", "Contrôle l’utilisation exclusive du mode TCP.\nLorsque cette option est activée, RustDesk n’utilise plus le port UDP 21116 et utilise le port TCP 21116 à la place."),
|
("disable-udp-tip", "Contrôle l’utilisation exclusive du mode TCP.\nLorsque cette option est activée, RustDesk n’utilise plus le port UDP 21116 et utilise le port TCP 21116 à la place."),
|
||||||
("server-oss-not-support-tip", "Note : Cette fonctionnalité n’est pas disponible sous la version open-source du serveur RustDesk."),
|
("server-oss-not-support-tip", "Note : Cette fonctionnalité n’est pas disponible sous la version open-source du serveur RustDesk."),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", "Disabilita UDP"),
|
("Disable UDP", "Disabilita UDP"),
|
||||||
("disable-udp-tip", "Controlla se usare solo TCP.\nQuando questa opzione è abilitata, RustDesk non userà più UDP 21116, verrà invece usato TCP 21116."),
|
("disable-udp-tip", "Controlla se usare solo TCP.\nQuando questa opzione è abilitata, RustDesk non userà più UDP 21116, verrà invece usato TCP 21116."),
|
||||||
("server-oss-not-support-tip", "NOTA: il sistema operativo del server RustDesk non include questa funzionalità."),
|
("server-oss-not-support-tip", "NOTA: il sistema operativo del server RustDesk non include questa funzionalità."),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", "UDP uitschakelen"),
|
("Disable UDP", "UDP uitschakelen"),
|
||||||
("disable-udp-tip", "Controleert of alleen TCP moet worden gebruikt. Als deze optie is ingeschakeld, gebruikt RustDesk niet langer UDP 21116, maar TCP 21116."),
|
("disable-udp-tip", "Controleert of alleen TCP moet worden gebruikt. Als deze optie is ingeschakeld, gebruikt RustDesk niet langer UDP 21116, maar TCP 21116."),
|
||||||
("server-oss-not-support-tip", "Opmerking: Deze functie is niet beschikbaar in de open-sourceversie van de RustDesk-server."),
|
("server-oss-not-support-tip", "Opmerking: Deze functie is niet beschikbaar in de open-sourceversie van de RustDesk-server."),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,5 +727,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Disable UDP", ""),
|
("Disable UDP", ""),
|
||||||
("disable-udp-tip", ""),
|
("disable-udp-tip", ""),
|
||||||
("server-oss-not-support-tip", ""),
|
("server-oss-not-support-tip", ""),
|
||||||
|
("input note here", ""),
|
||||||
|
("note-at-conn-end-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ pub struct Session<T: InvokeUiSession> {
|
|||||||
// Indicate whether the session is reconnected.
|
// Indicate whether the session is reconnected.
|
||||||
// Used to auto start file transfer after reconnection.
|
// Used to auto start file transfer after reconnection.
|
||||||
pub reconnect_count: Arc<AtomicUsize>,
|
pub reconnect_count: Arc<AtomicUsize>,
|
||||||
|
pub last_audit_note: Arc<Mutex<String>>,
|
||||||
|
pub audit_guid: Arc<Mutex<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -355,7 +357,10 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_edge_scroll_edge_thickness(&self, value: i32) {
|
pub fn save_edge_scroll_edge_thickness(&self, value: i32) {
|
||||||
self.lc.write().unwrap().save_edge_scroll_edge_thickness(value);
|
self.lc
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.save_edge_scroll_edge_thickness(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_flutter_option(&self, k: String, v: String) {
|
pub fn save_flutter_option(&self, k: String, v: String) {
|
||||||
@@ -562,9 +567,6 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_audit_server(&self, typ: String) -> String {
|
pub fn get_audit_server(&self, typ: String) -> String {
|
||||||
if LocalConfig::get_option("access_token").is_empty() {
|
|
||||||
return "".to_owned();
|
|
||||||
}
|
|
||||||
crate::get_audit_server(
|
crate::get_audit_server(
|
||||||
Config::get_option("api-server"),
|
Config::get_option("api-server"),
|
||||||
Config::get_option("custom-rendezvous-server"),
|
Config::get_option("custom-rendezvous-server"),
|
||||||
@@ -576,6 +578,7 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
let url = self.get_audit_server("conn".to_string());
|
let url = self.get_audit_server("conn".to_string());
|
||||||
let id = self.get_id();
|
let id = self.get_id();
|
||||||
let session_id = self.lc.read().unwrap().session_id;
|
let session_id = self.lc.read().unwrap().session_id;
|
||||||
|
*self.last_audit_note.lock().unwrap() = note.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
send_note(url, id, session_id, note);
|
send_note(url, id, session_id, note);
|
||||||
});
|
});
|
||||||
@@ -1281,6 +1284,8 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
drop(connection_round_state_lock);
|
drop(connection_round_state_lock);
|
||||||
|
|
||||||
let cloned = self.clone();
|
let cloned = self.clone();
|
||||||
|
*cloned.audit_guid.lock().unwrap() = String::new();
|
||||||
|
*cloned.last_audit_note.lock().unwrap() = String::new();
|
||||||
// override only if true
|
// override only if true
|
||||||
if true == force_relay {
|
if true == force_relay {
|
||||||
self.lc.write().unwrap().force_relay = true;
|
self.lc.write().unwrap().force_relay = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user