mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-11-04 00:33:12 +00:00
refactor: unify request approval menus and naming
This commit is contained in:
1 parent
417dcdf7bb
commit
c43443b1a1
67 files changed
+535
-559
No files matched your search
@@ -2330,8 +2330,8 @@ bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
|
||||
password = args[i + 1];
|
||||
i++;
|
||||
break;
|
||||
case '--without-password':
|
||||
loginMethod = 'consent';
|
||||
case '--request-approval':
|
||||
loginMethod = 'request-approval';
|
||||
break;
|
||||
case '--switch_uuid':
|
||||
switchUuid = args[i + 1];
|
||||
@@ -2344,7 +2344,7 @@ bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (loginMethod == 'consent') {
|
||||
if (loginMethod == 'request-approval') {
|
||||
password = null;
|
||||
}
|
||||
if (type != null && id != null) {
|
||||
@@ -2486,8 +2486,8 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
|
||||
|
||||
var queryParameters =
|
||||
uri.queryParameters.map((k, v) => MapEntry(k.toLowerCase(), v));
|
||||
final withoutPassword = queryParameters['without-password'] == 'true';
|
||||
if (withoutPassword) {
|
||||
final requestApproval = queryParameters['request-approval'] == 'true';
|
||||
if (requestApproval) {
|
||||
queryParameters.remove('password');
|
||||
}
|
||||
|
||||
@@ -2501,7 +2501,7 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
|
||||
if (isMobile && id != null) {
|
||||
final forceRelay = queryParameters["relay"] != null;
|
||||
final password = queryParameters["password"];
|
||||
final loginMethod = withoutPassword ? 'consent' : '';
|
||||
final loginMethod = requestApproval ? 'request-approval' : '';
|
||||
|
||||
// Determine connection type based on command
|
||||
if (command == '--file-transfer') {
|
||||
@@ -2536,7 +2536,7 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
|
||||
var param = queryParameters;
|
||||
String? password = param["password"];
|
||||
if (password != null) args.addAll(['--password', password]);
|
||||
if (withoutPassword) args.add('--without-password');
|
||||
if (requestApproval) args.add('--request-approval');
|
||||
String? switch_uuid = param["switch_uuid"];
|
||||
if (switch_uuid != null) args.addAll(['--switch_uuid', switch_uuid]);
|
||||
if (param["relay"] != null) args.add("--relay");
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
||||
import '../../desktop/widgets/popup_menu.dart';
|
||||
|
||||
class LoginMethodMenu extends MenuEntryBase<String> {
|
||||
final MenuEntryButton<String> defaultEntry;
|
||||
final List<MenuEntryButton<String>> methods;
|
||||
|
||||
LoginMethodMenu({required this.defaultEntry, required this.methods});
|
||||
|
||||
@override
|
||||
List<mod_menu.PopupMenuEntry<String>> build(
|
||||
BuildContext context, MenuConfig conf) {
|
||||
return [
|
||||
_LoginMethodMenuItem(
|
||||
height: conf.height,
|
||||
child: IntrinsicHeight(
|
||||
child:
|
||||
Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
|
||||
Expanded(
|
||||
child: MenuEntryButton<String>(
|
||||
padding: defaultEntry.padding,
|
||||
enabled: defaultEntry.enabled,
|
||||
dismissOnClicked: true,
|
||||
proc: defaultEntry.proc,
|
||||
childBuilder: defaultEntry.childBuilder,
|
||||
).build(context, conf).single),
|
||||
PopupMenuChildrenItem<String>(
|
||||
height: conf.height,
|
||||
padding: EdgeInsets.zero,
|
||||
enabled: defaultEntry.enabled,
|
||||
itemBuilder: (submenuContext) => methods
|
||||
.expand((entry) => MenuEntryButton<String>(
|
||||
childBuilder: entry.childBuilder,
|
||||
proc: entry.proc,
|
||||
padding: entry.padding,
|
||||
enabled: entry.enabled,
|
||||
dismissOnClicked: true,
|
||||
dismissCallback: () {
|
||||
if (Navigator.canPop(context)) Navigator.pop(context);
|
||||
},
|
||||
).build(submenuContext, conf))
|
||||
.toList(),
|
||||
child: SizedBox(
|
||||
width: 56,
|
||||
child: Icon(Icons.keyboard_arrow_right, color: conf.commonColor),
|
||||
),
|
||||
),
|
||||
])),
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// PopupMenuItem adds a TextButton around its child; these actions need separate hit targets.
|
||||
class _LoginMethodMenuItem extends mod_menu.PopupMenuEntry<String> {
|
||||
const _LoginMethodMenuItem({required this.height, required this.child});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
final double height;
|
||||
|
||||
@override
|
||||
bool represents(String? value) => false;
|
||||
|
||||
@override
|
||||
State<_LoginMethodMenuItem> createState() => _LoginMethodMenuItemState();
|
||||
}
|
||||
|
||||
class _LoginMethodMenuItemState extends State<_LoginMethodMenuItem> {
|
||||
@override
|
||||
Widget build(BuildContext context) => widget.child;
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import '../../models/peer_model.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
||||
import '../../desktop/widgets/popup_menu.dart';
|
||||
import 'login_method_menu.dart';
|
||||
import 'request_approval_menu.dart';
|
||||
import 'dart:math' as math;
|
||||
|
||||
typedef PopupMenuEntryBuilder = Future<List<mod_menu.PopupMenuEntry<String>>>
|
||||
@@ -573,7 +573,36 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
@protected
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(BuildContext context);
|
||||
|
||||
MenuEntryBase<String> _connectCommonAction(
|
||||
@protected
|
||||
Future<List<MenuEntryBase<String>>> _buildConnectionMenuItems(
|
||||
BuildContext context) async {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
_terminalAction(context),
|
||||
];
|
||||
|
||||
if (peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_terminalRunAsAdminAction(context));
|
||||
}
|
||||
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
}
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_rdpAction(context, peer.id));
|
||||
}
|
||||
menuItems.add(MenuEntryDivider<String>());
|
||||
menuItems.add(_requestApprovalAction(context));
|
||||
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
||||
if (!isWeb) {
|
||||
menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
||||
}
|
||||
return menuItems;
|
||||
}
|
||||
|
||||
MenuEntryButton<String> _connectCommonAction(
|
||||
BuildContext context,
|
||||
String title, {
|
||||
bool isFileTransfer = false,
|
||||
@@ -582,16 +611,10 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
bool isRDP = false,
|
||||
bool isTerminal = false,
|
||||
bool isTerminalRunAsAdmin = false,
|
||||
MenuEntryButton<String>? defaultEntry,
|
||||
String loginMethod = '',
|
||||
}) {
|
||||
action(String text, String loginMethod) => MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => loginMethod == 'consent'
|
||||
? Tooltip(
|
||||
message: translate(
|
||||
'Skip saved passwords and ask the remote user to accept the connection.'),
|
||||
child: Text(text, style: style),
|
||||
)
|
||||
: Text(text, style: style),
|
||||
return MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(title, style: style),
|
||||
proc: () {
|
||||
if (isTerminalRunAsAdmin) {
|
||||
setEnvTerminalAdmin();
|
||||
@@ -611,10 +634,44 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
return LoginMethodMenu(
|
||||
defaultEntry: defaultEntry ?? action(title, ''),
|
||||
methods: [
|
||||
action(translate('Without password'), 'consent'),
|
||||
}
|
||||
|
||||
MenuEntryBase<String> _requestApprovalAction(BuildContext context) {
|
||||
MenuEntryButton<String> action(String title,
|
||||
{bool isFileTransfer = false,
|
||||
bool isViewCamera = false,
|
||||
bool isTcpTunneling = false,
|
||||
bool isRDP = false,
|
||||
bool isTerminal = false,
|
||||
bool isTerminalRunAsAdmin = false}) =>
|
||||
_connectCommonAction(
|
||||
context,
|
||||
isTerminal || isTerminalRunAsAdmin
|
||||
? '${translate(title)} (beta)'
|
||||
: translate(title),
|
||||
isFileTransfer: isFileTransfer,
|
||||
isViewCamera: isViewCamera,
|
||||
isTcpTunneling: isTcpTunneling,
|
||||
isRDP: isRDP,
|
||||
isTerminal: isTerminal,
|
||||
isTerminalRunAsAdmin: isTerminalRunAsAdmin,
|
||||
loginMethod: 'request-approval',
|
||||
);
|
||||
|
||||
return RequestApprovalMenu(
|
||||
title: translate('Request approval'),
|
||||
padding: menuPadding,
|
||||
entries: [
|
||||
action('Connect'),
|
||||
action('Transfer file', isFileTransfer: true),
|
||||
action('View camera', isViewCamera: true),
|
||||
action('Terminal', isTerminal: true),
|
||||
if (peer.platform == kPeerPlatformWindows)
|
||||
action('Terminal (Run as administrator)', isTerminalRunAsAdmin: true),
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid)
|
||||
action('TCP tunneling', isTcpTunneling: true),
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows)
|
||||
action('RDP', isRDP: true),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -676,7 +733,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
|
||||
@protected
|
||||
MenuEntryBase<String> _rdpAction(BuildContext context, String id) {
|
||||
final action = MenuEntryButton<String>(
|
||||
return MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Container(
|
||||
alignment: AlignmentDirectional.center,
|
||||
height: CustomPopupMenuTheme.height,
|
||||
@@ -710,8 +767,6 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
return _connectCommonAction(context, translate('RDP'),
|
||||
isRDP: true, defaultEntry: action);
|
||||
}
|
||||
|
||||
@protected
|
||||
@@ -1014,33 +1069,13 @@ class RecentPeerCard extends BasePeerCard {
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
BuildContext context) async {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
_terminalAction(context),
|
||||
];
|
||||
|
||||
if (peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_terminalRunAsAdminAction(context));
|
||||
}
|
||||
|
||||
final List favs = (await bind.mainGetFav()).toList();
|
||||
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
}
|
||||
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
||||
if (!isWeb) {
|
||||
menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
||||
}
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_rdpAction(context, peer.id));
|
||||
}
|
||||
final menuItems = await _buildConnectionMenuItems(context);
|
||||
menuItems.add(MenuEntryDivider());
|
||||
if (isWindows) {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
}
|
||||
menuItems.add(MenuEntryDivider());
|
||||
if (isMobile || isDesktop || isWebDesktop) {
|
||||
menuItems.add(_renameAction(peer.id));
|
||||
}
|
||||
@@ -1079,31 +1114,11 @@ class FavoritePeerCard extends BasePeerCard {
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
BuildContext context) async {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
_terminalAction(context),
|
||||
];
|
||||
|
||||
if (peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_terminalRunAsAdminAction(context));
|
||||
}
|
||||
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
}
|
||||
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
||||
if (!isWeb) {
|
||||
menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
||||
}
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_rdpAction(context, peer.id));
|
||||
}
|
||||
final menuItems = await _buildConnectionMenuItems(context);
|
||||
menuItems.add(MenuEntryDivider());
|
||||
if (isWindows) {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
}
|
||||
menuItems.add(MenuEntryDivider());
|
||||
if (isMobile || isDesktop || isWebDesktop) {
|
||||
menuItems.add(_renameAction(peer.id));
|
||||
}
|
||||
@@ -1139,29 +1154,10 @@ class DiscoveredPeerCard extends BasePeerCard {
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
BuildContext context) async {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
_terminalAction(context),
|
||||
];
|
||||
|
||||
if (peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_terminalRunAsAdminAction(context));
|
||||
}
|
||||
|
||||
final List favs = (await bind.mainGetFav()).toList();
|
||||
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
}
|
||||
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
||||
if (!isWeb) {
|
||||
menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
||||
}
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_rdpAction(context, peer.id));
|
||||
}
|
||||
final menuItems = await _buildConnectionMenuItems(context);
|
||||
menuItems.add(MenuEntryDivider());
|
||||
menuItems.add(_wolAction(peer.id));
|
||||
if (isWindows) {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
@@ -1198,32 +1194,12 @@ class AddressBookPeerCard extends BasePeerCard {
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
BuildContext context) async {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
_terminalAction(context),
|
||||
];
|
||||
|
||||
if (peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_terminalRunAsAdminAction(context));
|
||||
}
|
||||
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
}
|
||||
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
||||
if (!isWeb) {
|
||||
menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
||||
}
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_rdpAction(context, peer.id));
|
||||
}
|
||||
final menuItems = await _buildConnectionMenuItems(context);
|
||||
menuItems.add(MenuEntryDivider());
|
||||
if (isWindows) {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
}
|
||||
if (gFFI.abModel.current.canWrite()) {
|
||||
menuItems.add(MenuEntryDivider());
|
||||
if (isMobile || isDesktop || isWebDesktop) {
|
||||
menuItems.add(_renameAction(peer.id));
|
||||
}
|
||||
@@ -1355,26 +1331,9 @@ class MyGroupPeerCard extends BasePeerCard {
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
BuildContext context) async {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
_terminalAction(context),
|
||||
];
|
||||
|
||||
if (peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_terminalRunAsAdminAction(context));
|
||||
}
|
||||
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
}
|
||||
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
||||
if (!isWeb) {
|
||||
menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
||||
}
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_rdpAction(context, peer.id));
|
||||
final menuItems = await _buildConnectionMenuItems(context);
|
||||
if (isWindows || gFFI.userModel.userName.isNotEmpty) {
|
||||
menuItems.add(MenuEntryDivider());
|
||||
}
|
||||
if (isWindows) {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
||||
import '../../desktop/widgets/popup_menu.dart';
|
||||
|
||||
class RequestApprovalMenu extends MenuEntryBase<String> {
|
||||
final String title;
|
||||
final List<MenuEntryButton<String>> entries;
|
||||
final EdgeInsets? padding;
|
||||
|
||||
RequestApprovalMenu(
|
||||
{required this.title, required this.entries, this.padding});
|
||||
|
||||
@override
|
||||
List<mod_menu.PopupMenuEntry<String>> build(
|
||||
BuildContext context, MenuConfig conf) {
|
||||
final theme = Theme.of(context);
|
||||
final buttonPadding =
|
||||
(TextButtonTheme.of(context).style?.padding?.resolve({}) ??
|
||||
ButtonStyleButton.scaledPadding(
|
||||
theme.useMaterial3
|
||||
? const EdgeInsets.symmetric(horizontal: 12, vertical: 8)
|
||||
: const EdgeInsets.all(8),
|
||||
const EdgeInsets.symmetric(horizontal: 8),
|
||||
const EdgeInsets.symmetric(horizontal: 4),
|
||||
MediaQuery.textScalerOf(context)
|
||||
.scale(theme.textTheme.labelLarge?.fontSize ?? 14) /
|
||||
14,
|
||||
))
|
||||
.resolve(Directionality.of(context));
|
||||
final checkboxTheme = CheckboxTheme.of(context);
|
||||
final checkboxWidth =
|
||||
((checkboxTheme.materialTapTargetSize ?? theme.materialTapTargetSize) ==
|
||||
MaterialTapTargetSize.padded
|
||||
? kMinInteractiveDimension
|
||||
: kMinInteractiveDimension - 8) +
|
||||
(checkboxTheme.visualDensity ?? theme.visualDensity)
|
||||
.baseSizeAdjustment
|
||||
.dx;
|
||||
final arrowInset = (checkboxWidth - (IconTheme.of(context).size ?? 24)) / 2;
|
||||
return MenuEntrySubMenu<String>(
|
||||
text: title,
|
||||
dismissParentOnCanceled: true,
|
||||
// Normal entries contain two TextButtons; the submenu adds a leading gap.
|
||||
padding: (padding ?? EdgeInsets.zero)
|
||||
.add(EdgeInsets.only(
|
||||
left: buttonPadding.left * 2,
|
||||
right: buttonPadding.right * 2 + arrowInset))
|
||||
.subtract(
|
||||
const EdgeInsetsDirectional.only(start: MenuConfig.midPadding))
|
||||
.resolve(Directionality.of(context)),
|
||||
entries: entries
|
||||
.map((entry) => MenuEntryButton<String>(
|
||||
childBuilder: entry.childBuilder,
|
||||
proc: entry.proc,
|
||||
padding: entry.padding,
|
||||
enabled: entry.enabled,
|
||||
dismissOnClicked: true,
|
||||
dismissCallback: () {
|
||||
if (Navigator.canPop(context)) Navigator.pop(context);
|
||||
},
|
||||
))
|
||||
.toList(),
|
||||
).build(context, conf);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import '../../common.dart';
|
||||
import '../../common/formatter/id_formatter.dart';
|
||||
import '../../common/widgets/peer_tab_page.dart';
|
||||
import '../../common/widgets/autocomplete.dart';
|
||||
import '../../common/widgets/request_approval_menu.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
||||
|
||||
@@ -340,6 +341,37 @@ class _ConnectionPageState extends State<ConnectionPage>
|
||||
isTcpTunneling: isTcpTunneling);
|
||||
}
|
||||
|
||||
RequestApprovalMenu _requestApprovalMenu(BuildContext context) {
|
||||
MenuEntryButton<String> action(String title,
|
||||
{bool isFileTransfer = false,
|
||||
bool isViewCamera = false,
|
||||
bool isTerminal = false,
|
||||
bool isTcpTunneling = false}) =>
|
||||
MenuEntryButton<String>(
|
||||
childBuilder: (style) => Text(translate(title), style: style),
|
||||
proc: () => connect(context, _idController.id,
|
||||
loginMethod: 'request-approval',
|
||||
isFileTransfer: isFileTransfer,
|
||||
isViewCamera: isViewCamera,
|
||||
isTerminal: isTerminal,
|
||||
isTcpTunneling: isTcpTunneling),
|
||||
padding: EdgeInsets.symmetric(horizontal: kDesktopMenuPadding.left),
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
|
||||
return RequestApprovalMenu(
|
||||
title: translate('Request approval'),
|
||||
padding: EdgeInsets.symmetric(horizontal: kDesktopMenuPadding.left),
|
||||
entries: [
|
||||
action('Connect'),
|
||||
action('Transfer file', isFileTransfer: true),
|
||||
action('View camera', isViewCamera: true),
|
||||
action('${translate('Terminal')} (beta)', isTerminal: true),
|
||||
if (isDesktop) action('TCP tunneling', isTcpTunneling: true),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// UI for the remote ID TextField.
|
||||
/// Search for a peer.
|
||||
Widget _buildRemoteIDTextField(BuildContext context) {
|
||||
@@ -558,11 +590,6 @@ class _ConnectionPageState extends State<ConnectionPage>
|
||||
context: context,
|
||||
position: RelativeRect.fromLTRB(x, y, x, y),
|
||||
items: [
|
||||
(
|
||||
'Connect without password',
|
||||
() => connect(context, _idController.id,
|
||||
loginMethod: 'consent')
|
||||
),
|
||||
(
|
||||
'Transfer file',
|
||||
() => onConnect(isFileTransfer: true)
|
||||
@@ -584,7 +611,8 @@ class _ConnectionPageState extends State<ConnectionPage>
|
||||
() => onConnect(isTcpTunneling: true)
|
||||
),
|
||||
]
|
||||
.map((e) => MenuEntryButton<String>(
|
||||
.map<MenuEntryBase<String>>(
|
||||
(e) => MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) =>
|
||||
Text(
|
||||
translate(e.$1),
|
||||
@@ -596,6 +624,10 @@ class _ConnectionPageState extends State<ConnectionPage>
|
||||
kDesktopMenuPadding.left),
|
||||
dismissOnClicked: true,
|
||||
))
|
||||
.followedBy([
|
||||
MenuEntryDivider<String>(),
|
||||
_requestApprovalMenu(context),
|
||||
])
|
||||
.map((e) => e.build(
|
||||
context,
|
||||
const MenuConfig(
|
||||
|
||||
@@ -15,6 +15,7 @@ class PopupMenuChildrenItem<T> extends mod_menu.PopupMenuEntry<T> {
|
||||
this.enabled,
|
||||
this.textStyle,
|
||||
this.onTap,
|
||||
this.dismissParentOnCanceled = false,
|
||||
this.position = mod_menu.PopupMenuPosition.overSide,
|
||||
this.offset = Offset.zero,
|
||||
required this.itemBuilder,
|
||||
@@ -27,6 +28,7 @@ class PopupMenuChildrenItem<T> extends mod_menu.PopupMenuEntry<T> {
|
||||
final EdgeInsets? padding;
|
||||
final RxBool? enabled;
|
||||
final void Function()? onTap;
|
||||
final bool dismissParentOnCanceled;
|
||||
final List<mod_menu.PopupMenuEntry<T>> Function(BuildContext) itemBuilder;
|
||||
final Widget child;
|
||||
|
||||
@@ -69,6 +71,13 @@ class MyPopupMenuItemState<T, W extends PopupMenuChildrenItem<T>>
|
||||
position: widget.position,
|
||||
offset: widget.offset,
|
||||
onSelected: handleTap,
|
||||
onCanceled: widget.dismissParentOnCanceled
|
||||
? () {
|
||||
if (ModalRoute.of(context)?.isCurrent == true) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
itemBuilder: widget.itemBuilder,
|
||||
padding: EdgeInsets.zero,
|
||||
child: AnimatedDefaultTextStyle(
|
||||
@@ -645,11 +654,13 @@ class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
|
||||
final String text;
|
||||
final List<MenuEntryBase<T>> entries;
|
||||
final EdgeInsets? padding;
|
||||
final bool dismissParentOnCanceled;
|
||||
|
||||
MenuEntrySubMenu({
|
||||
required this.text,
|
||||
required this.entries,
|
||||
this.padding,
|
||||
this.dismissParentOnCanceled = false,
|
||||
RxBool? enabled,
|
||||
}) : super(enabled: enabled);
|
||||
|
||||
@@ -662,6 +673,7 @@ class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
|
||||
enabled: super.enabled,
|
||||
height: conf.height,
|
||||
padding: padding,
|
||||
dismissParentOnCanceled: dismissParentOnCanceled,
|
||||
position: mod_menu.PopupMenuPosition.overSide,
|
||||
itemBuilder: (BuildContext context) => entries
|
||||
.map((entry) => entry.build(context, conf))
|
||||
@@ -678,10 +690,15 @@ class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Obx(() => Icon(
|
||||
Icons.keyboard_arrow_right,
|
||||
color: super.enabled!.value ? conf.commonColor : Colors.grey,
|
||||
)),
|
||||
child: Transform.scale(
|
||||
scale: MenuConfig.iconScale,
|
||||
child: Obx(() => Icon(
|
||||
Icons.keyboard_arrow_right,
|
||||
color: super.enabled!.value
|
||||
? Theme.of(context).unselectedWidgetColor
|
||||
: disabledStyle().color,
|
||||
)),
|
||||
),
|
||||
))
|
||||
]),
|
||||
)
|
||||
|
||||
@@ -241,8 +241,8 @@ class WebHomePage extends StatelessWidget {
|
||||
password = args[i + 1];
|
||||
i++;
|
||||
break;
|
||||
case '--without-password':
|
||||
loginMethod = 'consent';
|
||||
case '--request-approval':
|
||||
loginMethod = 'request-approval';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -921,9 +921,9 @@ class FfiModel with ChangeNotifier {
|
||||
} else if (type == 'input-password') {
|
||||
enterPasswordDialog(sessionId, dialogManager);
|
||||
} else if (type == 'terminal-admin-login' ||
|
||||
type == 'terminal-admin-login-consent') {
|
||||
type == 'terminal-admin-login-request-approval') {
|
||||
enterUserLoginDialog(sessionId, dialogManager, 'terminal-admin-login-tip',
|
||||
waitForAccept: type == 'terminal-admin-login-consent');
|
||||
waitForAccept: type == 'terminal-admin-login-request-approval');
|
||||
} else if (type == 'terminal-admin-login-password') {
|
||||
enterUserLoginAndPasswordDialog(
|
||||
sessionId, dialogManager, 'terminal-admin-login-tip');
|
||||
|
||||
Reference in new issue
Block a user