commit
723a3dfb7b
@ -923,7 +923,8 @@ bool option2bool(String option, String value) {
|
||||
} else if (option.startsWith("allow-") ||
|
||||
option == "stop-service" ||
|
||||
option == "direct-server" ||
|
||||
option == "stop-rendezvous-service") {
|
||||
option == "stop-rendezvous-service" ||
|
||||
option == "force-always-relay") {
|
||||
res = value == "Y";
|
||||
} else {
|
||||
assert(false);
|
||||
@ -939,7 +940,8 @@ String bool2option(String option, bool b) {
|
||||
} else if (option.startsWith('allow-') ||
|
||||
option == "stop-service" ||
|
||||
option == "direct-server" ||
|
||||
option == "stop-rendezvous-service") {
|
||||
option == "stop-rendezvous-service" ||
|
||||
option == "force-always-relay") {
|
||||
res = b ? 'Y' : '';
|
||||
} else {
|
||||
assert(false);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common/formatter/id_formatter.dart';
|
||||
import 'package:flutter_hbb/common/widgets/peer_card.dart';
|
||||
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
|
||||
@ -237,29 +238,32 @@ class _AddressBookState extends State<AddressBook> {
|
||||
}
|
||||
|
||||
void abAddId() async {
|
||||
var field = "";
|
||||
var msg = "";
|
||||
var isInProgress = false;
|
||||
TextEditingController controller = TextEditingController(text: field);
|
||||
IDTextEditingController idController = IDTextEditingController(text: '');
|
||||
TextEditingController aliasController = TextEditingController(text: '');
|
||||
final tags = List.of(gFFI.abModel.tags);
|
||||
var selectedTag = List<dynamic>.empty(growable: true).obs;
|
||||
final style = TextStyle(fontSize: 14.0);
|
||||
String? errorMsg;
|
||||
|
||||
gFFI.dialogManager.show((setState, close) {
|
||||
submit() async {
|
||||
setState(() {
|
||||
msg = "";
|
||||
isInProgress = true;
|
||||
errorMsg = null;
|
||||
});
|
||||
field = controller.text.trim();
|
||||
if (field.isEmpty) {
|
||||
String id = idController.id;
|
||||
if (id.isEmpty) {
|
||||
// pass
|
||||
} else {
|
||||
final ids = field.trim().split(RegExp(r"[\s,;\n]+"));
|
||||
field = ids.join(',');
|
||||
for (final newId in ids) {
|
||||
if (gFFI.abModel.idContainBy(newId)) {
|
||||
continue;
|
||||
}
|
||||
gFFI.abModel.addId(newId);
|
||||
if (gFFI.abModel.idContainBy(id)) {
|
||||
setState(() {
|
||||
isInProgress = false;
|
||||
errorMsg = translate('ID already exists');
|
||||
});
|
||||
return;
|
||||
}
|
||||
gFFI.abModel.addId(id, aliasController.text.trim(), selectedTag);
|
||||
await gFFI.abModel.pushAb();
|
||||
this.setState(() {});
|
||||
// final currentPeers
|
||||
@ -272,21 +276,70 @@ class _AddressBookState extends State<AddressBook> {
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(translate("whitelist_sep")),
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
Row(
|
||||
Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
maxLines: null,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
errorText: msg.isEmpty ? null : translate(msg),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'*',
|
||||
style: TextStyle(color: Colors.red, fontSize: 14),
|
||||
),
|
||||
controller: controller,
|
||||
focusNode: FocusNode()..requestFocus()),
|
||||
Text(
|
||||
'ID',
|
||||
style: style,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
TextField(
|
||||
controller: idController,
|
||||
inputFormatters: [IDTextInputFormatter()],
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(),
|
||||
errorText: errorMsg),
|
||||
style: style,
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
translate('Alias'),
|
||||
style: style,
|
||||
),
|
||||
).marginOnly(top: 8, bottom: 2),
|
||||
TextField(
|
||||
controller: aliasController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
isDense: true,
|
||||
),
|
||||
style: style,
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
translate('Tags'),
|
||||
style: style,
|
||||
),
|
||||
).marginOnly(top: 8),
|
||||
Container(
|
||||
child: Wrap(
|
||||
children: tags
|
||||
.map((e) => AddressBookTag(
|
||||
name: e,
|
||||
tags: selectedTag,
|
||||
onTap: () {
|
||||
if (selectedTag.contains(e)) {
|
||||
selectedTag.remove(e);
|
||||
} else {
|
||||
selectedTag.add(e);
|
||||
}
|
||||
},
|
||||
showActionMenu: false))
|
||||
.toList(growable: false),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -56,6 +56,9 @@ class _PeerCardState extends State<_PeerCard>
|
||||
|
||||
Widget _buildMobile() {
|
||||
final peer = super.widget.peer;
|
||||
final name =
|
||||
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
||||
|
||||
return Card(
|
||||
margin: EdgeInsets.symmetric(horizontal: 2),
|
||||
child: GestureDetector(
|
||||
@ -90,7 +93,7 @@ class _PeerCardState extends State<_PeerCard>
|
||||
? formatID(peer.id)
|
||||
: peer.alias)
|
||||
]),
|
||||
Text('${peer.username}@${peer.hostname}')
|
||||
Text(name)
|
||||
],
|
||||
).paddingOnly(left: 8.0),
|
||||
),
|
||||
@ -145,6 +148,8 @@ class _PeerCardState extends State<_PeerCard>
|
||||
|
||||
Widget _buildPeerTile(
|
||||
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
||||
final name =
|
||||
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
||||
final greyStyle = TextStyle(
|
||||
fontSize: 11,
|
||||
color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6));
|
||||
@ -184,7 +189,7 @@ class _PeerCardState extends State<_PeerCard>
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
'${peer.username}@${peer.hostname}',
|
||||
name,
|
||||
style: greyStyle,
|
||||
textAlign: TextAlign.start,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
@ -206,7 +211,8 @@ class _PeerCardState extends State<_PeerCard>
|
||||
|
||||
Widget _buildPeerCard(
|
||||
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
||||
final name = '${peer.username}@${peer.hostname}';
|
||||
final name =
|
||||
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
||||
return Card(
|
||||
color: Colors.transparent,
|
||||
elevation: 0,
|
||||
@ -310,11 +316,20 @@ class _PeerCardState extends State<_PeerCard>
|
||||
bool get wantKeepAlive => true;
|
||||
}
|
||||
|
||||
enum CardType {
|
||||
recent,
|
||||
fav,
|
||||
lan,
|
||||
ab,
|
||||
}
|
||||
|
||||
abstract class BasePeerCard extends StatelessWidget {
|
||||
final Peer peer;
|
||||
final EdgeInsets? menuPadding;
|
||||
final CardType cardType;
|
||||
|
||||
BasePeerCard({required this.peer, this.menuPadding, Key? key})
|
||||
BasePeerCard(
|
||||
{required this.peer, required this.cardType, this.menuPadding, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -419,7 +434,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
if (Navigator.canPop(context)) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
_rdpDialog(id);
|
||||
_rdpDialog(id, cardType);
|
||||
},
|
||||
)),
|
||||
))
|
||||
@ -471,17 +486,16 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
switchType: SwitchType.scheckbox,
|
||||
text: translate('Always connect via relay'),
|
||||
getter: () async {
|
||||
return (await bind.mainGetPeerOption(id: id, key: option)).isNotEmpty;
|
||||
if (cardType == CardType.ab) {
|
||||
return gFFI.abModel.find(id)?.forceAlwaysRelay ?? false;
|
||||
} else {
|
||||
return (await bind.mainGetPeerOption(id: id, key: option)).isNotEmpty;
|
||||
}
|
||||
},
|
||||
setter: (bool v) async {
|
||||
String value;
|
||||
String oldValue = await bind.mainGetPeerOption(id: id, key: option);
|
||||
if (oldValue.isEmpty) {
|
||||
value = 'Y';
|
||||
} else {
|
||||
value = '';
|
||||
}
|
||||
await bind.mainSetPeerOption(id: id, key: option, value: value);
|
||||
gFFI.abModel.setPeerForceAlwaysRelay(id, v);
|
||||
await bind.mainSetPeerOption(
|
||||
id: id, key: option, value: bool2option('force-always-relay', v));
|
||||
},
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
@ -489,14 +503,14 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
}
|
||||
|
||||
@protected
|
||||
MenuEntryBase<String> _renameAction(String id, bool isAddressBook) {
|
||||
MenuEntryBase<String> _renameAction(String id) {
|
||||
return MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
translate('Rename'),
|
||||
style: style,
|
||||
),
|
||||
proc: () {
|
||||
_rename(id, isAddressBook);
|
||||
_rename(id);
|
||||
},
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
@ -586,33 +600,42 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void _rename(String id, bool isAddressBook) async {
|
||||
@protected
|
||||
MenuEntryBase<String> _addToAb(Peer peer) {
|
||||
return MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
translate('Add to Address Book'),
|
||||
style: style,
|
||||
),
|
||||
proc: () {
|
||||
() async {
|
||||
if (!gFFI.abModel.idContainBy(peer.id)) {
|
||||
gFFI.abModel.addPeer(peer);
|
||||
await gFFI.abModel.pushAb();
|
||||
}
|
||||
}();
|
||||
},
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
}
|
||||
|
||||
void _rename(String id) async {
|
||||
RxBool isInProgress = false.obs;
|
||||
var name = peer.alias;
|
||||
var controller = TextEditingController(text: name);
|
||||
if (isAddressBook) {
|
||||
final peer = gFFI.abModel.peers.firstWhereOrNull((p) => id == p.id);
|
||||
if (peer == null) {
|
||||
// this should not happen
|
||||
} else {
|
||||
name = peer.alias;
|
||||
}
|
||||
String name;
|
||||
if (cardType == CardType.ab) {
|
||||
name = gFFI.abModel.find(id)?.alias ?? "";
|
||||
} else {
|
||||
name = await bind.mainGetPeerOption(id: id, key: 'alias');
|
||||
}
|
||||
var controller = TextEditingController(text: name);
|
||||
gFFI.dialogManager.show((setState, close) {
|
||||
submit() async {
|
||||
isInProgress.value = true;
|
||||
name = controller.text;
|
||||
String name = controller.text.trim();
|
||||
await bind.mainSetPeerAlias(id: id, alias: name);
|
||||
if (isAddressBook) {
|
||||
gFFI.abModel.setPeerAlias(id, name);
|
||||
await gFFI.abModel.pushAb();
|
||||
}
|
||||
if (isAddressBook) {
|
||||
gFFI.abModel.pullAb();
|
||||
} else {
|
||||
bind.mainLoadRecentPeers();
|
||||
bind.mainLoadFavPeers();
|
||||
}
|
||||
gFFI.abModel.setPeerAlias(id, name);
|
||||
update();
|
||||
close();
|
||||
isInProgress.value = false;
|
||||
}
|
||||
@ -646,11 +669,32 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void update() {
|
||||
switch (cardType) {
|
||||
case CardType.recent:
|
||||
bind.mainLoadRecentPeers();
|
||||
break;
|
||||
case CardType.fav:
|
||||
bind.mainLoadFavPeers();
|
||||
break;
|
||||
case CardType.lan:
|
||||
bind.mainLoadLanPeers();
|
||||
break;
|
||||
case CardType.ab:
|
||||
gFFI.abModel.pullAb();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RecentPeerCard extends BasePeerCard {
|
||||
RecentPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
||||
: super(peer: peer, menuPadding: menuPadding, key: key);
|
||||
: super(
|
||||
peer: peer,
|
||||
cardType: CardType.recent,
|
||||
menuPadding: menuPadding,
|
||||
key: key);
|
||||
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
@ -671,7 +715,7 @@ class RecentPeerCard extends BasePeerCard {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
}
|
||||
menuItems.add(MenuEntryDivider());
|
||||
menuItems.add(_renameAction(peer.id, false));
|
||||
menuItems.add(_renameAction(peer.id));
|
||||
menuItems.add(_removeAction(peer.id, () async {
|
||||
await bind.mainLoadRecentPeers();
|
||||
}));
|
||||
@ -679,13 +723,20 @@ class RecentPeerCard extends BasePeerCard {
|
||||
menuItems.add(_unrememberPasswordAction(peer.id));
|
||||
}
|
||||
menuItems.add(_addFavAction(peer.id));
|
||||
if (!gFFI.abModel.idContainBy(peer.id)) {
|
||||
menuItems.add(_addToAb(peer));
|
||||
}
|
||||
return menuItems;
|
||||
}
|
||||
}
|
||||
|
||||
class FavoritePeerCard extends BasePeerCard {
|
||||
FavoritePeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
||||
: super(peer: peer, menuPadding: menuPadding, key: key);
|
||||
: super(
|
||||
peer: peer,
|
||||
cardType: CardType.fav,
|
||||
menuPadding: menuPadding,
|
||||
key: key);
|
||||
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
@ -706,7 +757,7 @@ class FavoritePeerCard extends BasePeerCard {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
}
|
||||
menuItems.add(MenuEntryDivider());
|
||||
menuItems.add(_renameAction(peer.id, false));
|
||||
menuItems.add(_renameAction(peer.id));
|
||||
menuItems.add(_removeAction(peer.id, () async {
|
||||
await bind.mainLoadFavPeers();
|
||||
}));
|
||||
@ -716,13 +767,20 @@ class FavoritePeerCard extends BasePeerCard {
|
||||
menuItems.add(_rmFavAction(peer.id, () async {
|
||||
await bind.mainLoadFavPeers();
|
||||
}));
|
||||
if (!gFFI.abModel.idContainBy(peer.id)) {
|
||||
menuItems.add(_addToAb(peer));
|
||||
}
|
||||
return menuItems;
|
||||
}
|
||||
}
|
||||
|
||||
class DiscoveredPeerCard extends BasePeerCard {
|
||||
DiscoveredPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
||||
: super(peer: peer, menuPadding: menuPadding, key: key);
|
||||
: super(
|
||||
peer: peer,
|
||||
cardType: CardType.lan,
|
||||
menuPadding: menuPadding,
|
||||
key: key);
|
||||
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
@ -744,13 +802,20 @@ class DiscoveredPeerCard extends BasePeerCard {
|
||||
}
|
||||
menuItems.add(MenuEntryDivider());
|
||||
menuItems.add(_removeAction(peer.id, () async {}));
|
||||
if (!gFFI.abModel.idContainBy(peer.id)) {
|
||||
menuItems.add(_addToAb(peer));
|
||||
}
|
||||
return menuItems;
|
||||
}
|
||||
}
|
||||
|
||||
class AddressBookPeerCard extends BasePeerCard {
|
||||
AddressBookPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
||||
: super(peer: peer, menuPadding: menuPadding, key: key);
|
||||
: super(
|
||||
peer: peer,
|
||||
cardType: CardType.ab,
|
||||
menuPadding: menuPadding,
|
||||
key: key);
|
||||
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
@ -771,7 +836,7 @@ class AddressBookPeerCard extends BasePeerCard {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
}
|
||||
menuItems.add(MenuEntryDivider());
|
||||
menuItems.add(_renameAction(peer.id, false));
|
||||
menuItems.add(_renameAction(peer.id));
|
||||
menuItems.add(_removeAction(peer.id, () async {}));
|
||||
if (await bind.mainPeerHasPassword(id: peer.id)) {
|
||||
menuItems.add(_unrememberPasswordAction(peer.id));
|
||||
@ -872,23 +937,33 @@ class AddressBookPeerCard extends BasePeerCard {
|
||||
}
|
||||
}
|
||||
|
||||
void _rdpDialog(String id) async {
|
||||
final portController = TextEditingController(
|
||||
text: await bind.mainGetPeerOption(id: id, key: 'rdp_port'));
|
||||
final userController = TextEditingController(
|
||||
text: await bind.mainGetPeerOption(id: id, key: 'rdp_username'));
|
||||
void _rdpDialog(String id, CardType card) async {
|
||||
String port, username;
|
||||
if (card == CardType.ab) {
|
||||
port = gFFI.abModel.find(id)?.rdpPort ?? '';
|
||||
username = gFFI.abModel.find(id)?.rdpUsername ?? '';
|
||||
} else {
|
||||
port = await bind.mainGetPeerOption(id: id, key: 'rdp_port');
|
||||
username = await bind.mainGetPeerOption(id: id, key: 'rdp_username');
|
||||
}
|
||||
|
||||
final portController = TextEditingController(text: port);
|
||||
final userController = TextEditingController(text: username);
|
||||
final passwordController = TextEditingController(
|
||||
text: await bind.mainGetPeerOption(id: id, key: 'rdp_password'));
|
||||
RxBool secure = true.obs;
|
||||
|
||||
gFFI.dialogManager.show((setState, close) {
|
||||
submit() async {
|
||||
String port = portController.text.trim();
|
||||
String username = userController.text;
|
||||
String password = passwordController.text;
|
||||
await bind.mainSetPeerOption(id: id, key: 'rdp_port', value: port);
|
||||
await bind.mainSetPeerOption(
|
||||
id: id, key: 'rdp_port', value: portController.text.trim());
|
||||
id: id, key: 'rdp_username', value: username);
|
||||
await bind.mainSetPeerOption(
|
||||
id: id, key: 'rdp_username', value: userController.text);
|
||||
await bind.mainSetPeerOption(
|
||||
id: id, key: 'rdp_password', value: passwordController.text);
|
||||
id: id, key: 'rdp_password', value: password);
|
||||
gFFI.abModel.setRdp(id, port, username);
|
||||
close();
|
||||
}
|
||||
|
||||
|
@ -68,11 +68,21 @@ class AbModel {
|
||||
peers.clear();
|
||||
}
|
||||
|
||||
void addId(String id) async {
|
||||
void addId(String id, String alias, List<dynamic> tags) {
|
||||
if (idContainBy(id)) {
|
||||
return;
|
||||
}
|
||||
peers.add(Peer.fromJson({"id": id}));
|
||||
final peer = Peer.fromJson({
|
||||
'id': id,
|
||||
'alias': alias,
|
||||
'tags': tags,
|
||||
});
|
||||
peers.add(peer);
|
||||
}
|
||||
|
||||
void addPeer(Peer peer) {
|
||||
peers.removeWhere((e) => e.id == peer.id);
|
||||
peers.add(peer);
|
||||
}
|
||||
|
||||
void addTag(String tag) async {
|
||||
@ -112,6 +122,10 @@ class AbModel {
|
||||
}
|
||||
}
|
||||
|
||||
Peer? find(String id) {
|
||||
return peers.firstWhereOrNull((e) => e.id == id);
|
||||
}
|
||||
|
||||
bool idContainBy(String id) {
|
||||
return peers.where((element) => element.id == id).isNotEmpty;
|
||||
}
|
||||
@ -150,13 +164,28 @@ class AbModel {
|
||||
}
|
||||
}
|
||||
|
||||
void setPeerAlias(String id, String value) {
|
||||
Future<void> setPeerAlias(String id, String value) async {
|
||||
final it = peers.where((p0) => p0.id == id);
|
||||
if (it.isEmpty) {
|
||||
debugPrint("$id is not exists");
|
||||
return;
|
||||
} else {
|
||||
if (it.isNotEmpty) {
|
||||
it.first.alias = value;
|
||||
await pushAb();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setPeerForceAlwaysRelay(String id, bool value) async {
|
||||
final it = peers.where((p0) => p0.id == id);
|
||||
if (it.isNotEmpty) {
|
||||
it.first.forceAlwaysRelay = value;
|
||||
await pushAb();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setRdp(String id, String port, String username) async {
|
||||
final it = peers.where((p0) => p0.id == id);
|
||||
if (it.isNotEmpty) {
|
||||
it.first.rdpPort = port;
|
||||
it.first.rdpUsername = username;
|
||||
await pushAb();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,9 @@ class Peer {
|
||||
final String platform;
|
||||
String alias;
|
||||
List<dynamic> tags;
|
||||
bool forceAlwaysRelay = false;
|
||||
String rdpPort;
|
||||
String rdpUsername;
|
||||
bool online = false;
|
||||
|
||||
Peer.fromJson(Map<String, dynamic> json)
|
||||
@ -17,7 +20,10 @@ class Peer {
|
||||
hostname = json['hostname'] ?? '',
|
||||
platform = json['platform'] ?? '',
|
||||
alias = json['alias'] ?? '',
|
||||
tags = json['tags'] ?? [];
|
||||
tags = json['tags'] ?? [],
|
||||
forceAlwaysRelay = json['forceAlwaysRelay'] == 'true',
|
||||
rdpPort = json['rdpPort'] ?? '',
|
||||
rdpUsername = json['rdpUsername'] ?? '';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
@ -27,6 +33,9 @@ class Peer {
|
||||
"platform": platform,
|
||||
"alias": alias,
|
||||
"tags": tags,
|
||||
"forceAlwaysRelay": forceAlwaysRelay.toString(),
|
||||
"rdpPort": rdpPort,
|
||||
"rdpUsername": rdpUsername,
|
||||
};
|
||||
}
|
||||
|
||||
@ -37,16 +46,23 @@ class Peer {
|
||||
required this.platform,
|
||||
required this.alias,
|
||||
required this.tags,
|
||||
required this.forceAlwaysRelay,
|
||||
required this.rdpPort,
|
||||
required this.rdpUsername,
|
||||
});
|
||||
|
||||
Peer.loading()
|
||||
: this(
|
||||
id: '...',
|
||||
username: '...',
|
||||
hostname: '...',
|
||||
platform: '...',
|
||||
alias: '',
|
||||
tags: []);
|
||||
id: '...',
|
||||
username: '...',
|
||||
hostname: '...',
|
||||
platform: '...',
|
||||
alias: '',
|
||||
tags: [],
|
||||
forceAlwaysRelay: false,
|
||||
rdpPort: '',
|
||||
rdpUsername: '',
|
||||
);
|
||||
}
|
||||
|
||||
class Peers extends ChangeNotifier {
|
||||
|
@ -214,7 +214,11 @@ pub fn core_main() -> Option<Vec<String>> {
|
||||
return None;
|
||||
} else if args[0] == "--password" {
|
||||
if args.len() == 2 {
|
||||
crate::ipc::set_permanent_password(args[1].to_owned()).unwrap();
|
||||
if crate::platform::is_root() {
|
||||
crate::ipc::set_permanent_password(args[1].to_owned()).unwrap();
|
||||
} else {
|
||||
log::info!("Permission denied!");
|
||||
}
|
||||
}
|
||||
return None;
|
||||
} else if args[0] == "--check-hwcodec-config" {
|
||||
|
@ -641,45 +641,11 @@ pub fn main_peer_has_password(id: String) -> bool {
|
||||
peer_has_password(id)
|
||||
}
|
||||
|
||||
pub fn main_get_recent_peers() -> String {
|
||||
if !config::APP_DIR.read().unwrap().is_empty() {
|
||||
let peers: Vec<HashMap<&str, String>> = PeerConfig::peers()
|
||||
.drain(..)
|
||||
.map(|(id, _, p)| {
|
||||
HashMap::<&str, String>::from_iter([
|
||||
("id", id),
|
||||
("username", p.info.username.clone()),
|
||||
("hostname", p.info.hostname.clone()),
|
||||
("platform", p.info.platform.clone()),
|
||||
(
|
||||
"alias",
|
||||
p.options.get("alias").unwrap_or(&"".to_owned()).to_owned(),
|
||||
),
|
||||
])
|
||||
})
|
||||
.collect();
|
||||
serde_json::ser::to_string(&peers).unwrap_or("".to_owned())
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main_load_recent_peers() {
|
||||
if !config::APP_DIR.read().unwrap().is_empty() {
|
||||
let peers: Vec<HashMap<&str, String>> = PeerConfig::peers()
|
||||
.drain(..)
|
||||
.map(|(id, _, p)| {
|
||||
HashMap::<&str, String>::from_iter([
|
||||
("id", id),
|
||||
("username", p.info.username.clone()),
|
||||
("hostname", p.info.hostname.clone()),
|
||||
("platform", p.info.platform.clone()),
|
||||
(
|
||||
"alias",
|
||||
p.options.get("alias").unwrap_or(&"".to_owned()).to_owned(),
|
||||
),
|
||||
])
|
||||
})
|
||||
.map(|(id, _, p)| peer_to_map(id, p))
|
||||
.collect();
|
||||
if let Some(s) = flutter::GLOBAL_EVENT_STREAM
|
||||
.read()
|
||||
@ -705,16 +671,7 @@ pub fn main_load_fav_peers() {
|
||||
.into_iter()
|
||||
.filter_map(|(id, _, p)| {
|
||||
if favs.contains(&id) {
|
||||
Some(HashMap::<&str, String>::from_iter([
|
||||
("id", id),
|
||||
("username", p.info.username.clone()),
|
||||
("hostname", p.info.hostname.clone()),
|
||||
("platform", p.info.platform.clone()),
|
||||
(
|
||||
"alias",
|
||||
p.options.get("alias").unwrap_or(&"".to_owned()).to_owned(),
|
||||
),
|
||||
]))
|
||||
Some(peer_to_map(id, p))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -400,5 +400,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", "右键选择选项卡"),
|
||||
("Skipped", "已跳过"),
|
||||
("Add to Address Book", "添加到地址簿"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", "Dies ist nur möglich, wenn der Zugriff nur über ein permanentes Passwort erfolgt."), // Sehr unklar. Muss noch angepasst werden. Original: Allow hiding only if accepting sessions via password and using pernament passw"),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", "Permitir ocultar solo si se aceptan sesiones a través de contraseña y usando contraseña permanente"),
|
||||
("wayland_experiment_tip", "El soporte para Wayland está en fase experimental, por favor, use X11 si necesita acceso desatendido."),
|
||||
("Right click to select tabs", "Clic derecho para seleccionar pestañas"),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", "فقط در صورت پذیرفتن جلسات از طریق رمز عبور و استفاده از رمز عبور دائمی، مخفی شدن مجاز است"),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", "Autoriser le masquage uniquement si vous acceptez des sessions via un mot de passe et utilisez un mot de passe permanent"),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", "Να επιτρέπεται η απόκρυψη, μόνο εάν αποδέχεστε συνδέσεις μέσω κωδικού πρόσβασης και χρησιμοποιείτε μόνιμο κωδικό πρόσβασης"),
|
||||
("wayland_experiment_tip", "Η υποστήριξη Wayland βρίσκεται σε πειραματικό στάδιο, χρησιμοποιήστε το X11 εάν χρειάζεστε πρόσβαση χωρίς επίβλεψη."),
|
||||
("Right click to select tabs", "Κάντε δεξί κλικ για να επιλέξετε καρτέλες"),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", "Permetti di nascondere solo se si accettano sessioni con password permanente"),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", "Разрешать скрытие случае, если принимаются сеансы по паролю или используется постоянный пароль"),
|
||||
("wayland_experiment_tip", "Поддержка Wayland находится на экспериментальной стадии, используйте X11, если вам требуется автоматический доступ."),
|
||||
("Right click to select tabs", "Выбор вкладок щелчком правой кнопки мыши"),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", "Kjo është e mundur vetëm nëse aksesi bëhet nëpërmjet një fjalëkalimi të përhershëm"),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", "Tillåt att gömma endast om accepterande sessioner med lösenord och permanenta lösenord"),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", "在只允許密碼連接並且只用固定密碼的情況下才允許隱藏"),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", "右鍵選擇選項卡"),
|
||||
("Add to Address Book", "添加到地址簿"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -399,5 +399,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("hide_cm_tip", ""),
|
||||
("wayland_experiment_tip", ""),
|
||||
("Right click to select tabs", ""),
|
||||
("Add to Address Book", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -685,6 +685,19 @@ pub fn discover() {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn peer_to_map(id: String, p: PeerConfig) -> HashMap<&'static str, String> {
|
||||
HashMap::<&str, String>::from_iter([
|
||||
("id", id),
|
||||
("username", p.info.username.clone()),
|
||||
("hostname", p.info.hostname.clone()),
|
||||
("platform", p.info.platform.clone()),
|
||||
(
|
||||
"alias",
|
||||
p.options.get("alias").unwrap_or(&"".to_owned()).to_owned(),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_lan_peers() -> Vec<HashMap<&'static str, String>> {
|
||||
config::LanPeers::load()
|
||||
|
Loading…
x
Reference in New Issue
Block a user