enable group, show accessible users and peers
Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
parent
09d380ba8f
commit
b2a4f11e0b
@ -2480,3 +2480,59 @@ String toCapitalized(String s) {
|
|||||||
}
|
}
|
||||||
return s.substring(0, 1).toUpperCase() + s.substring(1);
|
return s.substring(0, 1).toUpperCase() + s.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildErrorBanner(BuildContext context,
|
||||||
|
{required RxBool loading,
|
||||||
|
required RxString err,
|
||||||
|
required Function? retry,
|
||||||
|
required Function close}) {
|
||||||
|
const double height = 25;
|
||||||
|
return Obx(() => Offstage(
|
||||||
|
offstage: !(!loading.value && err.value.isNotEmpty),
|
||||||
|
child: Center(
|
||||||
|
child: Container(
|
||||||
|
height: height,
|
||||||
|
color: MyTheme.color(context).errorBannerBg,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
FittedBox(
|
||||||
|
child: Icon(
|
||||||
|
Icons.info,
|
||||||
|
color: Color.fromARGB(255, 249, 81, 81),
|
||||||
|
),
|
||||||
|
).marginAll(4),
|
||||||
|
Flexible(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Tooltip(
|
||||||
|
message: translate(err.value),
|
||||||
|
child: Text(
|
||||||
|
translate(err.value),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
)).marginSymmetric(vertical: 2),
|
||||||
|
),
|
||||||
|
if (retry != null)
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
retry.call();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
translate("Retry"),
|
||||||
|
style: TextStyle(color: MyTheme.accent),
|
||||||
|
)).marginSymmetric(horizontal: 5),
|
||||||
|
FittedBox(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
close.call();
|
||||||
|
},
|
||||||
|
child: Icon(Icons.close).marginSymmetric(horizontal: 5),
|
||||||
|
),
|
||||||
|
).marginAll(4)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)).marginOnly(bottom: 14),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hbb/consts.dart';
|
||||||
|
|
||||||
import 'package:flutter_hbb/models/peer_model.dart';
|
import 'package:flutter_hbb/models/peer_model.dart';
|
||||||
|
|
||||||
@ -48,11 +49,18 @@ class UserPayload {
|
|||||||
};
|
};
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toGroupCacheJson() {
|
||||||
|
final Map<String, dynamic> map = {
|
||||||
|
'name': name,
|
||||||
|
};
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PeerPayload {
|
class PeerPayload {
|
||||||
String id = '';
|
String id = '';
|
||||||
String info = '';
|
Map<String, dynamic> info = {};
|
||||||
int? status;
|
int? status;
|
||||||
String user = '';
|
String user = '';
|
||||||
String user_name = '';
|
String user_name = '';
|
||||||
@ -67,7 +75,38 @@ class PeerPayload {
|
|||||||
note = json['note'] ?? '';
|
note = json['note'] ?? '';
|
||||||
|
|
||||||
static Peer toPeer(PeerPayload p) {
|
static Peer toPeer(PeerPayload p) {
|
||||||
return Peer.fromJson({"id": p.id, "username": p.user_name});
|
return Peer.fromJson({
|
||||||
|
"id": p.id,
|
||||||
|
'loginName': p.user_name,
|
||||||
|
"username": p.info['username'] ?? '',
|
||||||
|
"platform": _platform(p.info['os']),
|
||||||
|
"hostname": p.info['device_name'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static String? _platform(dynamic field) {
|
||||||
|
if (field == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final fieldStr = field.toString();
|
||||||
|
List<String> list = fieldStr.split(' / ');
|
||||||
|
if (list.isEmpty) return null;
|
||||||
|
final os = list[0];
|
||||||
|
switch (os.toLowerCase()) {
|
||||||
|
case 'windows':
|
||||||
|
return kPeerPlatformWindows;
|
||||||
|
case 'linux':
|
||||||
|
return kPeerPlatformLinux;
|
||||||
|
case 'macos':
|
||||||
|
return kPeerPlatformMacOS;
|
||||||
|
case 'android':
|
||||||
|
return kPeerPlatformAndroid;
|
||||||
|
default:
|
||||||
|
if (fieldStr.toLowerCase().contains('linux')) {
|
||||||
|
return kPeerPlatformLinux;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class _AddressBookState extends State<AddressBook> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Obx(() {
|
Widget build(BuildContext context) => Obx(() {
|
||||||
if (gFFI.userModel.userName.value.isEmpty) {
|
if (!gFFI.userModel.isLogin) {
|
||||||
return Center(
|
return Center(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: loginDialog, child: Text(translate("Login"))));
|
onPressed: loginDialog, child: Text(translate("Login"))));
|
||||||
@ -49,11 +49,13 @@ class _AddressBookState extends State<AddressBook> {
|
|||||||
children: [
|
children: [
|
||||||
// NOT use Offstage to wrap LinearProgressIndicator
|
// NOT use Offstage to wrap LinearProgressIndicator
|
||||||
if (gFFI.abModel.retrying.value) LinearProgressIndicator(),
|
if (gFFI.abModel.retrying.value) LinearProgressIndicator(),
|
||||||
_buildErrorBanner(
|
buildErrorBanner(context,
|
||||||
|
loading: gFFI.abModel.abLoading,
|
||||||
err: gFFI.abModel.pullError,
|
err: gFFI.abModel.pullError,
|
||||||
retry: null,
|
retry: null,
|
||||||
close: () => gFFI.abModel.pullError.value = ''),
|
close: () => gFFI.abModel.pullError.value = ''),
|
||||||
_buildErrorBanner(
|
buildErrorBanner(context,
|
||||||
|
loading: gFFI.abModel.abLoading,
|
||||||
err: gFFI.abModel.pushError,
|
err: gFFI.abModel.pushError,
|
||||||
retry: () => gFFI.abModel.pushAb(isRetry: true),
|
retry: () => gFFI.abModel.pushAb(isRetry: true),
|
||||||
close: () => gFFI.abModel.pushError.value = ''),
|
close: () => gFFI.abModel.pushError.value = ''),
|
||||||
@ -66,61 +68,6 @@ class _AddressBookState extends State<AddressBook> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Widget _buildErrorBanner(
|
|
||||||
{required RxString err,
|
|
||||||
required Function? retry,
|
|
||||||
required Function close}) {
|
|
||||||
const double height = 25;
|
|
||||||
return Obx(() => Offstage(
|
|
||||||
offstage: !(!gFFI.abModel.abLoading.value && err.value.isNotEmpty),
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
height: height,
|
|
||||||
color: MyTheme.color(context).errorBannerBg,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
FittedBox(
|
|
||||||
child: Icon(
|
|
||||||
Icons.info,
|
|
||||||
color: Color.fromARGB(255, 249, 81, 81),
|
|
||||||
),
|
|
||||||
).marginAll(4),
|
|
||||||
Flexible(
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Tooltip(
|
|
||||||
message: translate(err.value),
|
|
||||||
child: Text(
|
|
||||||
translate(err.value),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
)).marginSymmetric(vertical: 2),
|
|
||||||
),
|
|
||||||
if (retry != null)
|
|
||||||
InkWell(
|
|
||||||
onTap: () {
|
|
||||||
retry.call();
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
translate("Retry"),
|
|
||||||
style: TextStyle(color: MyTheme.accent),
|
|
||||||
)).marginSymmetric(horizontal: 5),
|
|
||||||
FittedBox(
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
close.call();
|
|
||||||
},
|
|
||||||
child: Icon(Icons.close).marginSymmetric(horizontal: 5),
|
|
||||||
),
|
|
||||||
).marginAll(4)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)).marginOnly(bottom: 14),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildAddressBookDesktop() {
|
Widget _buildAddressBookDesktop() {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
@ -230,11 +177,10 @@ class _AddressBookState extends State<AddressBook> {
|
|||||||
return Expanded(
|
return Expanded(
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: Obx(() => AddressBookPeersView(
|
child: AddressBookPeersView(
|
||||||
menuPadding: widget.menuPadding,
|
menuPadding: widget.menuPadding,
|
||||||
// ignore: invalid_use_of_protected_member
|
initPeers: gFFI.abModel.peers,
|
||||||
initPeers: gFFI.abModel.peers.value,
|
)),
|
||||||
))),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,49 +29,28 @@ class _MyGroupState extends State<MyGroup> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
// use username to be same with ab
|
if (!gFFI.userModel.isLogin) {
|
||||||
if (gFFI.userModel.userName.value.isEmpty) {
|
|
||||||
return Center(
|
return Center(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: loginDialog, child: Text(translate("Login"))));
|
onPressed: loginDialog, child: Text(translate("Login"))));
|
||||||
}
|
} else if (gFFI.groupModel.groupLoading.value && gFFI.groupModel.emtpy) {
|
||||||
return buildBody(context);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildBody(BuildContext context) {
|
|
||||||
return Obx(() {
|
|
||||||
if (gFFI.groupModel.groupLoading.value) {
|
|
||||||
return const Center(
|
return const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (gFFI.groupModel.groupLoadError.isNotEmpty) {
|
return Column(
|
||||||
return _buildShowError(gFFI.groupModel.groupLoadError.value);
|
children: [
|
||||||
}
|
buildErrorBanner(context,
|
||||||
if (isDesktop) {
|
loading: gFFI.groupModel.groupLoading,
|
||||||
return _buildDesktop();
|
err: gFFI.groupModel.groupLoadError,
|
||||||
} else {
|
retry: null,
|
||||||
return _buildMobile();
|
close: () => gFFI.groupModel.groupLoadError.value = ''),
|
||||||
}
|
Expanded(child: isDesktop ? _buildDesktop() : _buildMobile())
|
||||||
|
],
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildShowError(String error) {
|
|
||||||
return Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(translate(error)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
gFFI.groupModel.pull();
|
|
||||||
},
|
|
||||||
child: Text(translate("Retry")))
|
|
||||||
],
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildDesktop() {
|
Widget _buildDesktop() {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
@ -100,10 +79,9 @@ class _MyGroupState extends State<MyGroup> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: Obx(() => MyGroupPeerView(
|
child: MyGroupPeerView(
|
||||||
menuPadding: widget.menuPadding,
|
menuPadding: widget.menuPadding,
|
||||||
// ignore: invalid_use_of_protected_member
|
initPeers: gFFI.groupModel.peers)),
|
||||||
initPeers: gFFI.groupModel.peersShow.value))),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -133,10 +111,9 @@ class _MyGroupState extends State<MyGroup> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: Obx(() => MyGroupPeerView(
|
child: MyGroupPeerView(
|
||||||
menuPadding: widget.menuPadding,
|
menuPadding: widget.menuPadding,
|
||||||
// ignore: invalid_use_of_protected_member
|
initPeers: gFFI.groupModel.peers)),
|
||||||
initPeers: gFFI.groupModel.peersShow.value))),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -195,6 +172,7 @@ class _MyGroupState extends State<MyGroup> {
|
|||||||
}, child: Obx(
|
}, child: Obx(
|
||||||
() {
|
() {
|
||||||
bool selected = selectedUser.value == username;
|
bool selected = selectedUser.value == username;
|
||||||
|
final isMe = username == gFFI.userModel.userName.value;
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: selected ? MyTheme.color(context).highlight : null,
|
color: selected ? MyTheme.color(context).highlight : null,
|
||||||
@ -208,7 +186,7 @@ class _MyGroupState extends State<MyGroup> {
|
|||||||
children: [
|
children: [
|
||||||
Icon(Icons.person_rounded, color: Colors.grey, size: 16)
|
Icon(Icons.person_rounded, color: Colors.grey, size: 16)
|
||||||
.marginOnly(right: 4),
|
.marginOnly(right: 4),
|
||||||
Expanded(child: Text(username)),
|
Expanded(child: Text(isMe ? translate('Me') : username)),
|
||||||
],
|
],
|
||||||
).paddingSymmetric(vertical: 4),
|
).paddingSymmetric(vertical: 4),
|
||||||
),
|
),
|
||||||
|
@ -1093,7 +1093,7 @@ class MyGroupPeerCard extends BasePeerCard {
|
|||||||
menuItems.add(_tcpTunnelingAction(context, peer.id));
|
menuItems.add(_tcpTunnelingAction(context, peer.id));
|
||||||
}
|
}
|
||||||
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
||||||
menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
// menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
||||||
if (peer.platform == 'Windows') {
|
if (peer.platform == 'Windows') {
|
||||||
menuItems.add(_rdpAction(context, peer.id));
|
menuItems.add(_rdpAction(context, peer.id));
|
||||||
}
|
}
|
||||||
@ -1101,9 +1101,14 @@ class MyGroupPeerCard extends BasePeerCard {
|
|||||||
menuItems.add(_createShortCutAction(peer.id));
|
menuItems.add(_createShortCutAction(peer.id));
|
||||||
}
|
}
|
||||||
menuItems.add(MenuEntryDivider());
|
menuItems.add(MenuEntryDivider());
|
||||||
menuItems.add(_renameAction(peer.id));
|
// menuItems.add(_renameAction(peer.id));
|
||||||
if (await bind.mainPeerHasPassword(id: peer.id)) {
|
// if (await bind.mainPeerHasPassword(id: peer.id)) {
|
||||||
menuItems.add(_unrememberPasswordAction(peer.id));
|
// menuItems.add(_unrememberPasswordAction(peer.id));
|
||||||
|
// }
|
||||||
|
if (gFFI.userModel.userName.isNotEmpty) {
|
||||||
|
if (!gFFI.abModel.idContainBy(peer.id)) {
|
||||||
|
menuItems.add(_addToAb(peer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return menuItems;
|
return menuItems;
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,11 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
child:
|
child:
|
||||||
visibleContextMenuListener(_createSwitchBar(context))),
|
visibleContextMenuListener(_createSwitchBar(context))),
|
||||||
const PeerSearchBar().marginOnly(right: isMobile ? 0 : 13),
|
const PeerSearchBar().marginOnly(right: isMobile ? 0 : 13),
|
||||||
_createRefresh(),
|
_createRefresh(
|
||||||
|
index: PeerTabIndex.ab, loading: gFFI.abModel.abLoading),
|
||||||
|
_createRefresh(
|
||||||
|
index: PeerTabIndex.group,
|
||||||
|
loading: gFFI.groupModel.groupLoading),
|
||||||
_createMultiSelection(),
|
_createMultiSelection(),
|
||||||
Offstage(
|
Offstage(
|
||||||
offstage: !isDesktop,
|
offstage: !isDesktop,
|
||||||
@ -170,12 +174,12 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
));
|
));
|
||||||
return Obx(() => InkWell(
|
return Obx(() => InkWell(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration:
|
decoration: (hover.value
|
||||||
selected ? decoBorder : (hover.value ? deco : null),
|
? (selected ? decoBorder : deco)
|
||||||
|
: (selected ? decoBorder : null)),
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
preferBelow: false,
|
preferBelow: false,
|
||||||
message:
|
message: model.tabTooltip(t),
|
||||||
model.tabTooltip(t, gFFI.groupModel.groupName.value),
|
|
||||||
onTriggered: isMobile ? mobileShowTabVisibilityMenu : null,
|
onTriggered: isMobile ? mobileShowTabVisibilityMenu : null,
|
||||||
child: Icon(model.tabIcon(t), color: color),
|
child: Icon(model.tabIcon(t), color: color),
|
||||||
).paddingSymmetric(horizontal: 4),
|
).paddingSymmetric(horizontal: 4),
|
||||||
@ -212,17 +216,19 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
child: child.marginSymmetric(vertical: isDesktop ? 12.0 : 6.0));
|
child: child.marginSymmetric(vertical: isDesktop ? 12.0 : 6.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _createRefresh() {
|
Widget _createRefresh(
|
||||||
|
{required PeerTabIndex index, required RxBool loading}) {
|
||||||
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
final textColor = Theme.of(context).textTheme.titleLarge?.color;
|
final textColor = Theme.of(context).textTheme.titleLarge?.color;
|
||||||
return Offstage(
|
return Offstage(
|
||||||
offstage: gFFI.peerTabModel.currentTab != PeerTabIndex.ab.index,
|
offstage: model.currentTab != index.index,
|
||||||
child: RefreshWidget(
|
child: RefreshWidget(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (gFFI.peerTabModel.currentTab < entries.length) {
|
if (gFFI.peerTabModel.currentTab < entries.length) {
|
||||||
entries[gFFI.peerTabModel.currentTab].load();
|
entries[gFFI.peerTabModel.currentTab].load();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
spinning: gFFI.abModel.abLoading,
|
spinning: loading,
|
||||||
child: RotatedBox(
|
child: RotatedBox(
|
||||||
quarterTurns: 2,
|
quarterTurns: 2,
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
@ -297,9 +303,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
Expanded(
|
Expanded(child: Text(model.tabTooltip(i))),
|
||||||
child:
|
|
||||||
Text(model.tabTooltip(i, gFFI.groupModel.groupName.value))),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
@ -348,7 +352,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
for (int i = 0; i < model.tabNames.length; i++) {
|
for (int i = 0; i < model.tabNames.length; i++) {
|
||||||
menu.add(MenuEntrySwitch(
|
menu.add(MenuEntrySwitch(
|
||||||
switchType: SwitchType.scheckbox,
|
switchType: SwitchType.scheckbox,
|
||||||
text: model.tabTooltip(i, gFFI.groupModel.groupName.value),
|
text: model.tabTooltip(i),
|
||||||
getter: () async {
|
getter: () async {
|
||||||
return model.isVisible[i];
|
return model.isVisible[i];
|
||||||
},
|
},
|
||||||
@ -388,6 +392,9 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
|
|
||||||
Widget deleteSelection() {
|
Widget deleteSelection() {
|
||||||
final model = Provider.of<PeerTabModel>(context);
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
|
if (model.currentTab == PeerTabIndex.group.index) {
|
||||||
|
return Offstage();
|
||||||
|
}
|
||||||
return _hoverAction(
|
return _hoverAction(
|
||||||
context: context,
|
context: context,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -35,6 +35,7 @@ class LoadEvent {
|
|||||||
static const String favorite = 'load_fav_peers';
|
static const String favorite = 'load_fav_peers';
|
||||||
static const String lan = 'load_lan_peers';
|
static const String lan = 'load_lan_peers';
|
||||||
static const String addressBook = 'load_address_book_peers';
|
static const String addressBook = 'load_address_book_peers';
|
||||||
|
static const String group = 'load_group_peers';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// for peer search text, global obs value
|
/// for peer search text, global obs value
|
||||||
@ -312,7 +313,7 @@ abstract class BasePeersView extends StatelessWidget {
|
|||||||
final String loadEvent;
|
final String loadEvent;
|
||||||
final PeerFilter? peerFilter;
|
final PeerFilter? peerFilter;
|
||||||
final PeerCardBuilder peerCardBuilder;
|
final PeerCardBuilder peerCardBuilder;
|
||||||
final List<Peer> initPeers;
|
final RxList<Peer>? initPeers;
|
||||||
|
|
||||||
const BasePeersView({
|
const BasePeersView({
|
||||||
Key? key,
|
Key? key,
|
||||||
@ -326,7 +327,7 @@ abstract class BasePeersView extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return _PeersView(
|
return _PeersView(
|
||||||
peers: Peers(name: name, loadEvent: loadEvent, peers: initPeers),
|
peers: Peers(name: name, loadEvent: loadEvent, initPeers: initPeers),
|
||||||
peerFilter: peerFilter,
|
peerFilter: peerFilter,
|
||||||
peerCardBuilder: peerCardBuilder);
|
peerCardBuilder: peerCardBuilder);
|
||||||
}
|
}
|
||||||
@ -343,7 +344,7 @@ class RecentPeersView extends BasePeersView {
|
|||||||
peer: peer,
|
peer: peer,
|
||||||
menuPadding: menuPadding,
|
menuPadding: menuPadding,
|
||||||
),
|
),
|
||||||
initPeers: [],
|
initPeers: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -365,7 +366,7 @@ class FavoritePeersView extends BasePeersView {
|
|||||||
peer: peer,
|
peer: peer,
|
||||||
menuPadding: menuPadding,
|
menuPadding: menuPadding,
|
||||||
),
|
),
|
||||||
initPeers: [],
|
initPeers: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -387,7 +388,7 @@ class DiscoveredPeersView extends BasePeersView {
|
|||||||
peer: peer,
|
peer: peer,
|
||||||
menuPadding: menuPadding,
|
menuPadding: menuPadding,
|
||||||
),
|
),
|
||||||
initPeers: [],
|
initPeers: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -403,7 +404,7 @@ class AddressBookPeersView extends BasePeersView {
|
|||||||
{Key? key,
|
{Key? key,
|
||||||
EdgeInsets? menuPadding,
|
EdgeInsets? menuPadding,
|
||||||
ScrollController? scrollController,
|
ScrollController? scrollController,
|
||||||
required List<Peer> initPeers})
|
required RxList<Peer> initPeers})
|
||||||
: super(
|
: super(
|
||||||
key: key,
|
key: key,
|
||||||
name: 'address book peer',
|
name: 'address book peer',
|
||||||
@ -435,11 +436,11 @@ class MyGroupPeerView extends BasePeersView {
|
|||||||
{Key? key,
|
{Key? key,
|
||||||
EdgeInsets? menuPadding,
|
EdgeInsets? menuPadding,
|
||||||
ScrollController? scrollController,
|
ScrollController? scrollController,
|
||||||
required List<Peer> initPeers})
|
required RxList<Peer> initPeers})
|
||||||
: super(
|
: super(
|
||||||
key: key,
|
key: key,
|
||||||
name: 'my group peer',
|
name: 'group peer',
|
||||||
loadEvent: 'load_my_group_peers',
|
loadEvent: LoadEvent.group,
|
||||||
peerFilter: filter,
|
peerFilter: filter,
|
||||||
peerCardBuilder: (Peer peer) => MyGroupPeerCard(
|
peerCardBuilder: (Peer peer) => MyGroupPeerCard(
|
||||||
peer: peer,
|
peer: peer,
|
||||||
@ -450,12 +451,12 @@ class MyGroupPeerView extends BasePeersView {
|
|||||||
|
|
||||||
static bool filter(Peer peer) {
|
static bool filter(Peer peer) {
|
||||||
if (gFFI.groupModel.searchUserText.isNotEmpty) {
|
if (gFFI.groupModel.searchUserText.isNotEmpty) {
|
||||||
if (!peer.username.contains(gFFI.groupModel.searchUserText)) {
|
if (!peer.loginName.contains(gFFI.groupModel.searchUserText)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (gFFI.groupModel.selectedUser.isNotEmpty) {
|
if (gFFI.groupModel.selectedUser.isNotEmpty) {
|
||||||
if (gFFI.groupModel.selectedUser.value != peer.username) {
|
if (gFFI.groupModel.selectedUser.value != peer.loginName) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +126,7 @@ void runMainApp(bool startService) async {
|
|||||||
bind.pluginListReload();
|
bind.pluginListReload();
|
||||||
}
|
}
|
||||||
gFFI.abModel.loadCache();
|
gFFI.abModel.loadCache();
|
||||||
|
gFFI.groupModel.loadCache();
|
||||||
gFFI.userModel.refreshCurrentUser();
|
gFFI.userModel.refreshCurrentUser();
|
||||||
runApp(App());
|
runApp(App());
|
||||||
// Set window option.
|
// Set window option.
|
||||||
@ -154,6 +155,7 @@ void runMobileApp() async {
|
|||||||
if (isAndroid) androidChannelInit();
|
if (isAndroid) androidChannelInit();
|
||||||
platformFFI.syncAndroidServiceAppDirConfigPath();
|
platformFFI.syncAndroidServiceAppDirConfigPath();
|
||||||
gFFI.abModel.loadCache();
|
gFFI.abModel.loadCache();
|
||||||
|
gFFI.groupModel.loadCache();
|
||||||
gFFI.userModel.refreshCurrentUser();
|
gFFI.userModel.refreshCurrentUser();
|
||||||
runApp(App());
|
runApp(App());
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
||||||
import 'package:flutter_hbb/models/model.dart';
|
import 'package:flutter_hbb/models/model.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';
|
||||||
@ -115,9 +116,10 @@ class AbModel {
|
|||||||
_timerCounter = 0;
|
_timerCounter = 0;
|
||||||
if (pullError.isNotEmpty) {
|
if (pullError.isNotEmpty) {
|
||||||
if (statusCode == 401) {
|
if (statusCode == 401) {
|
||||||
gFFI.userModel.reset(clearAbCache: true);
|
gFFI.userModel.reset(resetOther: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
platformFFI.tryHandle({'name': LoadEvent.addressBook});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +243,8 @@ class AbModel {
|
|||||||
ret = true;
|
ret = true;
|
||||||
_saveCache();
|
_saveCache();
|
||||||
} else {
|
} else {
|
||||||
Map<String, dynamic> json = _jsonDecodeResp(resp.body, resp.statusCode);
|
Map<String, dynamic> json =
|
||||||
|
_jsonDecodeResp(utf8.decode(resp.bodyBytes), resp.statusCode);
|
||||||
if (json.containsKey('error')) {
|
if (json.containsKey('error')) {
|
||||||
throw json['error'];
|
throw json['error'];
|
||||||
} else if (resp.statusCode == 200) {
|
} else if (resp.statusCode == 200) {
|
||||||
@ -479,11 +482,12 @@ class AbModel {
|
|||||||
|
|
||||||
loadCache() async {
|
loadCache() async {
|
||||||
try {
|
try {
|
||||||
if (_cacheLoadOnceFlag || abLoading.value) return;
|
if (_cacheLoadOnceFlag || abLoading.value || initialized) return;
|
||||||
_cacheLoadOnceFlag = true;
|
_cacheLoadOnceFlag = true;
|
||||||
final access_token = bind.mainGetLocalOption(key: 'access_token');
|
final access_token = bind.mainGetLocalOption(key: 'access_token');
|
||||||
if (access_token.isEmpty) return;
|
if (access_token.isEmpty) return;
|
||||||
final cache = await bind.mainLoadAb();
|
final cache = await bind.mainLoadAb();
|
||||||
|
if (abLoading.value) return;
|
||||||
final data = jsonDecode(cache);
|
final data = jsonDecode(cache);
|
||||||
if (data == null || data['access_token'] != access_token) return;
|
if (data == null || data['access_token'] != access_token) return;
|
||||||
_deserialize(data);
|
_deserialize(data);
|
||||||
@ -561,4 +565,12 @@ class AbModel {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset() async {
|
||||||
|
pullError.value = '';
|
||||||
|
pushError.value = '';
|
||||||
|
tags.clear();
|
||||||
|
peers.clear();
|
||||||
|
await bind.mainClearAb();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:flutter_hbb/common/hbbs/hbbs.dart';
|
import 'package:flutter_hbb/common/hbbs/hbbs.dart';
|
||||||
|
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
||||||
import 'package:flutter_hbb/models/model.dart';
|
import 'package:flutter_hbb/models/model.dart';
|
||||||
import 'package:flutter_hbb/models/peer_model.dart';
|
import 'package:flutter_hbb/models/peer_model.dart';
|
||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
@ -11,57 +12,74 @@ import 'package:http/http.dart' as http;
|
|||||||
class GroupModel {
|
class GroupModel {
|
||||||
final RxBool groupLoading = false.obs;
|
final RxBool groupLoading = false.obs;
|
||||||
final RxString groupLoadError = "".obs;
|
final RxString groupLoadError = "".obs;
|
||||||
final RxString groupId = ''.obs;
|
|
||||||
RxString groupName = ''.obs;
|
|
||||||
final RxList<UserPayload> users = RxList.empty(growable: true);
|
final RxList<UserPayload> users = RxList.empty(growable: true);
|
||||||
final RxList<Peer> peersShow = RxList.empty(growable: true);
|
final RxList<Peer> peers = RxList.empty(growable: true);
|
||||||
final RxString selectedUser = ''.obs;
|
final RxString selectedUser = ''.obs;
|
||||||
final RxString searchUserText = ''.obs;
|
final RxString searchUserText = ''.obs;
|
||||||
WeakReference<FFI> parent;
|
WeakReference<FFI> parent;
|
||||||
var initialized = false;
|
var initialized = false;
|
||||||
|
var _cacheLoadOnceFlag = false;
|
||||||
|
var _statusCode = 200;
|
||||||
|
|
||||||
|
bool get emtpy => users.isEmpty && peers.isEmpty;
|
||||||
|
|
||||||
GroupModel(this.parent);
|
GroupModel(this.parent);
|
||||||
|
|
||||||
reset() {
|
|
||||||
groupName.value = '';
|
|
||||||
groupId.value = '';
|
|
||||||
users.clear();
|
|
||||||
peersShow.clear();
|
|
||||||
initialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> pull({force = true, quiet = false}) async {
|
Future<void> pull({force = true, quiet = false}) async {
|
||||||
/*
|
if (!gFFI.userModel.isLogin || groupLoading.value) return;
|
||||||
if (!force && initialized) return;
|
if (!force && initialized) return;
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
groupLoading.value = true;
|
groupLoading.value = true;
|
||||||
groupLoadError.value = "";
|
groupLoadError.value = "";
|
||||||
}
|
}
|
||||||
await _pull();
|
try {
|
||||||
|
await _pull();
|
||||||
|
} catch (_) {}
|
||||||
groupLoading.value = false;
|
groupLoading.value = false;
|
||||||
initialized = true;
|
initialized = true;
|
||||||
*/
|
platformFFI.tryHandle({'name': LoadEvent.group});
|
||||||
|
if (_statusCode == 401) {
|
||||||
|
gFFI.userModel.reset(resetOther: true);
|
||||||
|
} else {
|
||||||
|
_saveCache();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _pull() async {
|
Future<void> _pull() async {
|
||||||
reset();
|
List<UserPayload> tmpUsers = List.empty(growable: true);
|
||||||
if (bind.mainGetLocalOption(key: 'access_token') == '') {
|
if (!await _getUsers(tmpUsers)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
List<Peer> tmpPeers = List.empty(growable: true);
|
||||||
if (!await _getGroup()) {
|
if (!await _getPeers(tmpPeers)) {
|
||||||
reset();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('$e');
|
|
||||||
reset();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// me first
|
||||||
|
var index = tmpUsers
|
||||||
|
.indexWhere((user) => user.name == gFFI.userModel.userName.value);
|
||||||
|
if (index != -1) {
|
||||||
|
var user = tmpUsers.removeAt(index);
|
||||||
|
tmpUsers.insert(0, user);
|
||||||
|
}
|
||||||
|
users.value = tmpUsers;
|
||||||
|
if (!users.any((u) => u.name == selectedUser.value)) {
|
||||||
|
selectedUser.value = '';
|
||||||
|
}
|
||||||
|
// recover online
|
||||||
|
final oldOnlineIDs = peers.where((e) => e.online).map((e) => e.id).toList();
|
||||||
|
peers.value = tmpPeers;
|
||||||
|
peers
|
||||||
|
.where((e) => oldOnlineIDs.contains(e.id))
|
||||||
|
.map((e) => e.online = true)
|
||||||
|
.toList();
|
||||||
|
groupLoadError.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _getUsers(List<UserPayload> tmpUsers) async {
|
||||||
final api = "${await bind.mainGetApiServer()}/api/users";
|
final api = "${await bind.mainGetApiServer()}/api/users";
|
||||||
try {
|
try {
|
||||||
var uri0 = Uri.parse(api);
|
var uri0 = Uri.parse(api);
|
||||||
final pageSize = 20;
|
final pageSize = 100;
|
||||||
var total = 0;
|
var total = 0;
|
||||||
int current = 0;
|
int current = 0;
|
||||||
do {
|
do {
|
||||||
@ -74,84 +92,63 @@ class GroupModel {
|
|||||||
queryParameters: {
|
queryParameters: {
|
||||||
'current': current.toString(),
|
'current': current.toString(),
|
||||||
'pageSize': pageSize.toString(),
|
'pageSize': pageSize.toString(),
|
||||||
if (gFFI.userModel.isAdmin.isFalse) 'grp': groupId.value,
|
'accessible': '',
|
||||||
|
'status': '1',
|
||||||
});
|
});
|
||||||
final resp = await http.get(uri, headers: getHttpHeaders());
|
final resp = await http.get(uri, headers: getHttpHeaders());
|
||||||
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
_statusCode = resp.statusCode;
|
||||||
Map<String, dynamic> json = jsonDecode(utf8.decode(resp.bodyBytes));
|
Map<String, dynamic> json =
|
||||||
if (json.containsKey('error')) {
|
_jsonDecodeResp(utf8.decode(resp.bodyBytes), resp.statusCode);
|
||||||
throw json['error'];
|
if (json.containsKey('error')) {
|
||||||
|
if (json['error'] == 'Admin required!') {
|
||||||
|
throw translate('upgrade_rustdesk_server_pro_to_{1.1.10}_tip');
|
||||||
} else {
|
} else {
|
||||||
if (json.containsKey('total')) {
|
throw json['error'];
|
||||||
if (total == 0) total = json['total'];
|
}
|
||||||
if (json.containsKey('data')) {
|
}
|
||||||
final data = json['data'];
|
if (resp.statusCode != 200) {
|
||||||
if (data is List) {
|
throw 'HTTP ${resp.statusCode}';
|
||||||
for (final user in data) {
|
}
|
||||||
final u = UserPayload.fromJson(user);
|
if (json.containsKey('total')) {
|
||||||
if (!users.any((e) => e.name == u.name)) {
|
if (total == 0) total = json['total'];
|
||||||
users.add(u);
|
if (json.containsKey('data')) {
|
||||||
}
|
final data = json['data'];
|
||||||
}
|
if (data is List) {
|
||||||
|
for (final user in data) {
|
||||||
|
final u = UserPayload.fromJson(user);
|
||||||
|
int index = tmpUsers.indexWhere((e) => e.name == u.name);
|
||||||
|
if (index < 0) {
|
||||||
|
tmpUsers.add(u);
|
||||||
|
} else {
|
||||||
|
tmpUsers[index] = u;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (current * pageSize < total);
|
} while (current * pageSize < total);
|
||||||
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
debugPrint('$err');
|
debugPrint('get accessible users: $err');
|
||||||
groupLoadError.value = err.toString();
|
groupLoadError.value = err.toString();
|
||||||
} finally {
|
|
||||||
_pullUserPeers();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> _getGroup() async {
|
|
||||||
final url = await bind.mainGetApiServer();
|
|
||||||
final body = {
|
|
||||||
'id': await bind.mainGetMyId(),
|
|
||||||
'uuid': await bind.mainGetUuid()
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
final response = await http.post(Uri.parse('$url/api/currentGroup'),
|
|
||||||
headers: getHttpHeaders(), body: json.encode(body));
|
|
||||||
final status = response.statusCode;
|
|
||||||
if (status == 401 || status == 400) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final data = json.decode(utf8.decode(response.bodyBytes));
|
|
||||||
final error = data['error'];
|
|
||||||
if (error != null) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
groupName.value = data['name'] ?? '';
|
|
||||||
groupId.value = data['guid'] ?? '';
|
|
||||||
return groupId.value.isNotEmpty && groupName.isNotEmpty;
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('$e');
|
|
||||||
groupLoadError.value = e.toString();
|
|
||||||
} finally {}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _pullUserPeers() async {
|
Future<bool> _getPeers(List<Peer> tmpPeers) async {
|
||||||
peersShow.clear();
|
|
||||||
final api = "${await bind.mainGetApiServer()}/api/peers";
|
|
||||||
try {
|
try {
|
||||||
|
final api = "${await bind.mainGetApiServer()}/api/peers";
|
||||||
var uri0 = Uri.parse(api);
|
var uri0 = Uri.parse(api);
|
||||||
final pageSize =
|
final pageSize = 100;
|
||||||
20; // ????????????????????????????????????????????????????? stupid stupis, how about >20 peers
|
|
||||||
var total = 0;
|
var total = 0;
|
||||||
int current = 0;
|
int current = 0;
|
||||||
var queryParameters = {
|
var queryParameters = {
|
||||||
'current': current.toString(),
|
'current': current.toString(),
|
||||||
'pageSize': pageSize.toString(),
|
'pageSize': pageSize.toString(),
|
||||||
|
'accessible': '',
|
||||||
|
'status': '1',
|
||||||
|
'user_status': '1',
|
||||||
};
|
};
|
||||||
if (!gFFI.userModel.isAdmin.value) {
|
|
||||||
queryParameters.addAll({'grp': groupId.value});
|
|
||||||
}
|
|
||||||
do {
|
do {
|
||||||
current += 1;
|
current += 1;
|
||||||
var uri = Uri(
|
var uri = Uri(
|
||||||
@ -161,32 +158,107 @@ class GroupModel {
|
|||||||
port: uri0.port,
|
port: uri0.port,
|
||||||
queryParameters: queryParameters);
|
queryParameters: queryParameters);
|
||||||
final resp = await http.get(uri, headers: getHttpHeaders());
|
final resp = await http.get(uri, headers: getHttpHeaders());
|
||||||
if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") {
|
_statusCode = resp.statusCode;
|
||||||
Map<String, dynamic> json = jsonDecode(utf8.decode(resp.bodyBytes));
|
|
||||||
if (json.containsKey('error')) {
|
Map<String, dynamic> json =
|
||||||
throw json['error'];
|
_jsonDecodeResp(utf8.decode(resp.bodyBytes), resp.statusCode);
|
||||||
} else {
|
if (json.containsKey('error')) {
|
||||||
if (json.containsKey('total')) {
|
throw json['error'];
|
||||||
if (total == 0) total = json['total'];
|
}
|
||||||
if (json.containsKey('data')) {
|
if (resp.statusCode != 200) {
|
||||||
final data = json['data'];
|
throw 'HTTP ${resp.statusCode}';
|
||||||
if (data is List) {
|
}
|
||||||
for (final p in data) {
|
if (json.containsKey('total')) {
|
||||||
final peerPayload = PeerPayload.fromJson(p);
|
if (total == 0) total = json['total'];
|
||||||
final peer = PeerPayload.toPeer(peerPayload);
|
if (total > 1000) {
|
||||||
if (!peersShow.any((e) => e.id == peer.id)) {
|
total = 1000;
|
||||||
peersShow.add(peer);
|
}
|
||||||
}
|
if (json.containsKey('data')) {
|
||||||
}
|
final data = json['data'];
|
||||||
|
if (data is List) {
|
||||||
|
for (final p in data) {
|
||||||
|
final peerPayload = PeerPayload.fromJson(p);
|
||||||
|
final peer = PeerPayload.toPeer(peerPayload);
|
||||||
|
int index = tmpPeers.indexWhere((e) => e.id == peer.id);
|
||||||
|
if (index < 0) {
|
||||||
|
tmpPeers.add(peer);
|
||||||
|
} else {
|
||||||
|
tmpPeers[index] = peer;
|
||||||
|
}
|
||||||
|
if (tmpPeers.length >= 1000) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (current * pageSize < total);
|
} while (current * pageSize < total);
|
||||||
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
debugPrint('$err');
|
debugPrint('get accessible peers: $err');
|
||||||
groupLoadError.value = err.toString();
|
groupLoadError.value = err.toString();
|
||||||
} finally {}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _jsonDecodeResp(String body, int statusCode) {
|
||||||
|
try {
|
||||||
|
Map<String, dynamic> json = jsonDecode(body);
|
||||||
|
return json;
|
||||||
|
} catch (e) {
|
||||||
|
final err = body.isNotEmpty && body.length < 128 ? body : e.toString();
|
||||||
|
if (statusCode != 200) {
|
||||||
|
throw 'HTTP $statusCode, $err';
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _saveCache() {
|
||||||
|
try {
|
||||||
|
final map = (<String, dynamic>{
|
||||||
|
"access_token": bind.mainGetLocalOption(key: 'access_token'),
|
||||||
|
"users": users.map((e) => e.toGroupCacheJson()).toList(),
|
||||||
|
'peers': peers.map((e) => e.toGroupCacheJson()).toList()
|
||||||
|
});
|
||||||
|
bind.mainSaveGroup(json: jsonEncode(map));
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('group save:$e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCache() async {
|
||||||
|
try {
|
||||||
|
if (_cacheLoadOnceFlag || groupLoading.value || initialized) return;
|
||||||
|
_cacheLoadOnceFlag = true;
|
||||||
|
final access_token = bind.mainGetLocalOption(key: 'access_token');
|
||||||
|
if (access_token.isEmpty) return;
|
||||||
|
final cache = await bind.mainLoadGroup();
|
||||||
|
if (groupLoading.value) return;
|
||||||
|
final data = jsonDecode(cache);
|
||||||
|
if (data == null || data['access_token'] != access_token) return;
|
||||||
|
users.clear();
|
||||||
|
peers.clear();
|
||||||
|
if (data['users'] is List) {
|
||||||
|
for (var u in data['users']) {
|
||||||
|
users.add(UserPayload.fromJson(u));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data['peers'] is List) {
|
||||||
|
for (final peer in data['peers']) {
|
||||||
|
peers.add(Peer.fromJson(peer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("load group cache: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() async {
|
||||||
|
groupLoadError.value = '';
|
||||||
|
users.clear();
|
||||||
|
peers.clear();
|
||||||
|
selectedUser.value = '';
|
||||||
|
await bind.mainClearGroup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,8 @@ class PlatformFFI {
|
|||||||
|
|
||||||
int getRgbaSize(SessionID sessionId) =>
|
int getRgbaSize(SessionID sessionId) =>
|
||||||
_ffiBind.sessionGetRgbaSize(sessionId: sessionId);
|
_ffiBind.sessionGetRgbaSize(sessionId: sessionId);
|
||||||
void nextRgba(SessionID sessionId) => _ffiBind.sessionNextRgba(sessionId: sessionId);
|
void nextRgba(SessionID sessionId) =>
|
||||||
|
_ffiBind.sessionNextRgba(sessionId: sessionId);
|
||||||
void registerTexture(SessionID sessionId, int ptr) =>
|
void registerTexture(SessionID sessionId, int ptr) =>
|
||||||
_ffiBind.sessionRegisterTexture(sessionId: sessionId, ptr: ptr);
|
_ffiBind.sessionRegisterTexture(sessionId: sessionId, ptr: ptr);
|
||||||
|
|
||||||
@ -198,7 +199,7 @@ class PlatformFFI {
|
|||||||
version = await getVersion();
|
version = await getVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _tryHandle(Map<String, dynamic> evt) async {
|
Future<bool> tryHandle(Map<String, dynamic> evt) async {
|
||||||
final name = evt['name'];
|
final name = evt['name'];
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
final handlers = _eventHandlers[name];
|
final handlers = _eventHandlers[name];
|
||||||
@ -216,14 +217,15 @@ class PlatformFFI {
|
|||||||
|
|
||||||
/// Start listening to the Rust core's events and frames.
|
/// Start listening to the Rust core's events and frames.
|
||||||
void _startListenEvent(RustdeskImpl rustdeskImpl) {
|
void _startListenEvent(RustdeskImpl rustdeskImpl) {
|
||||||
final appType = _appType == kAppTypeDesktopRemote ? '$_appType,$kWindowId' : _appType;
|
final appType =
|
||||||
|
_appType == kAppTypeDesktopRemote ? '$_appType,$kWindowId' : _appType;
|
||||||
var sink = rustdeskImpl.startGlobalEventStream(appType: appType);
|
var sink = rustdeskImpl.startGlobalEventStream(appType: appType);
|
||||||
sink.listen((message) {
|
sink.listen((message) {
|
||||||
() async {
|
() async {
|
||||||
try {
|
try {
|
||||||
Map<String, dynamic> event = json.decode(message);
|
Map<String, dynamic> event = json.decode(message);
|
||||||
// _tryHandle here may be more flexible than _eventCallback
|
// _tryHandle here may be more flexible than _eventCallback
|
||||||
if (!await _tryHandle(event)) {
|
if (!await tryHandle(event)) {
|
||||||
if (_eventCallback != null) {
|
if (_eventCallback != null) {
|
||||||
await _eventCallback!(event);
|
await _eventCallback!(event);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'platform_model.dart';
|
import 'platform_model.dart';
|
||||||
// ignore: depend_on_referenced_packages
|
// ignore: depend_on_referenced_packages
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
@ -7,7 +8,7 @@ import 'package:collection/collection.dart';
|
|||||||
class Peer {
|
class Peer {
|
||||||
final String id;
|
final String id;
|
||||||
String hash;
|
String hash;
|
||||||
String username;
|
String username; // pc username
|
||||||
String hostname;
|
String hostname;
|
||||||
String platform;
|
String platform;
|
||||||
String alias;
|
String alias;
|
||||||
@ -16,6 +17,7 @@ class Peer {
|
|||||||
String rdpPort;
|
String rdpPort;
|
||||||
String rdpUsername;
|
String rdpUsername;
|
||||||
bool online = false;
|
bool online = false;
|
||||||
|
String loginName; //login username
|
||||||
|
|
||||||
String getId() {
|
String getId() {
|
||||||
if (alias != '') {
|
if (alias != '') {
|
||||||
@ -34,7 +36,8 @@ class Peer {
|
|||||||
tags = json['tags'] ?? [],
|
tags = json['tags'] ?? [],
|
||||||
forceAlwaysRelay = json['forceAlwaysRelay'] == 'true',
|
forceAlwaysRelay = json['forceAlwaysRelay'] == 'true',
|
||||||
rdpPort = json['rdpPort'] ?? '',
|
rdpPort = json['rdpPort'] ?? '',
|
||||||
rdpUsername = json['rdpUsername'] ?? '';
|
rdpUsername = json['rdpUsername'] ?? '',
|
||||||
|
loginName = json['loginName'] ?? '';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return <String, dynamic>{
|
return <String, dynamic>{
|
||||||
@ -48,6 +51,7 @@ class Peer {
|
|||||||
"forceAlwaysRelay": forceAlwaysRelay.toString(),
|
"forceAlwaysRelay": forceAlwaysRelay.toString(),
|
||||||
"rdpPort": rdpPort,
|
"rdpPort": rdpPort,
|
||||||
"rdpUsername": rdpUsername,
|
"rdpUsername": rdpUsername,
|
||||||
|
'loginName': loginName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +67,16 @@ class Peer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toGroupCacheJson() {
|
||||||
|
return <String, dynamic>{
|
||||||
|
"id": id,
|
||||||
|
"username": username,
|
||||||
|
"hostname": hostname,
|
||||||
|
"platform": platform,
|
||||||
|
"login_name": loginName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Peer({
|
Peer({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.hash,
|
required this.hash,
|
||||||
@ -74,6 +88,7 @@ class Peer {
|
|||||||
required this.forceAlwaysRelay,
|
required this.forceAlwaysRelay,
|
||||||
required this.rdpPort,
|
required this.rdpPort,
|
||||||
required this.rdpUsername,
|
required this.rdpUsername,
|
||||||
|
required this.loginName,
|
||||||
});
|
});
|
||||||
|
|
||||||
Peer.loading()
|
Peer.loading()
|
||||||
@ -88,6 +103,7 @@ class Peer {
|
|||||||
forceAlwaysRelay: false,
|
forceAlwaysRelay: false,
|
||||||
rdpPort: '',
|
rdpPort: '',
|
||||||
rdpUsername: '',
|
rdpUsername: '',
|
||||||
|
loginName: '',
|
||||||
);
|
);
|
||||||
bool equal(Peer other) {
|
bool equal(Peer other) {
|
||||||
return id == other.id &&
|
return id == other.id &&
|
||||||
@ -99,21 +115,24 @@ class Peer {
|
|||||||
tags.equals(other.tags) &&
|
tags.equals(other.tags) &&
|
||||||
forceAlwaysRelay == other.forceAlwaysRelay &&
|
forceAlwaysRelay == other.forceAlwaysRelay &&
|
||||||
rdpPort == other.rdpPort &&
|
rdpPort == other.rdpPort &&
|
||||||
rdpUsername == other.rdpUsername;
|
rdpUsername == other.rdpUsername &&
|
||||||
|
loginName == other.loginName;
|
||||||
}
|
}
|
||||||
|
|
||||||
Peer.copy(Peer other)
|
Peer.copy(Peer other)
|
||||||
: this(
|
: this(
|
||||||
id: other.id,
|
id: other.id,
|
||||||
hash: other.hash,
|
hash: other.hash,
|
||||||
username: other.username,
|
username: other.username,
|
||||||
hostname: other.hostname,
|
hostname: other.hostname,
|
||||||
platform: other.platform,
|
platform: other.platform,
|
||||||
alias: other.alias,
|
alias: other.alias,
|
||||||
tags: other.tags.toList(),
|
tags: other.tags.toList(),
|
||||||
forceAlwaysRelay: other.forceAlwaysRelay,
|
forceAlwaysRelay: other.forceAlwaysRelay,
|
||||||
rdpPort: other.rdpPort,
|
rdpPort: other.rdpPort,
|
||||||
rdpUsername: other.rdpUsername);
|
rdpUsername: other.rdpUsername,
|
||||||
|
loginName: other.loginName,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum UpdateEvent { online, load }
|
enum UpdateEvent { online, load }
|
||||||
@ -121,11 +140,14 @@ enum UpdateEvent { online, load }
|
|||||||
class Peers extends ChangeNotifier {
|
class Peers extends ChangeNotifier {
|
||||||
final String name;
|
final String name;
|
||||||
final String loadEvent;
|
final String loadEvent;
|
||||||
List<Peer> peers;
|
List<Peer> peers = List.empty(growable: true);
|
||||||
|
final RxList<Peer>? initPeers;
|
||||||
UpdateEvent event = UpdateEvent.load;
|
UpdateEvent event = UpdateEvent.load;
|
||||||
static const _cbQueryOnlines = 'callback_query_onlines';
|
static const _cbQueryOnlines = 'callback_query_onlines';
|
||||||
|
|
||||||
Peers({required this.name, required this.peers, required this.loadEvent}) {
|
Peers(
|
||||||
|
{required this.name, required this.initPeers, required this.loadEvent}) {
|
||||||
|
peers = initPeers ?? [];
|
||||||
platformFFI.registerEventHandler(_cbQueryOnlines, name, (evt) async {
|
platformFFI.registerEventHandler(_cbQueryOnlines, name, (evt) async {
|
||||||
_updateOnlineState(evt);
|
_updateOnlineState(evt);
|
||||||
});
|
});
|
||||||
@ -176,7 +198,11 @@ class Peers extends ChangeNotifier {
|
|||||||
|
|
||||||
void _updatePeers(Map<String, dynamic> evt) {
|
void _updatePeers(Map<String, dynamic> evt) {
|
||||||
final onlineStates = _getOnlineStates();
|
final onlineStates = _getOnlineStates();
|
||||||
peers = _decodePeers(evt['peers']);
|
if (initPeers != null) {
|
||||||
|
peers = initPeers!;
|
||||||
|
} else {
|
||||||
|
peers = _decodePeers(evt['peers']);
|
||||||
|
}
|
||||||
for (var peer in peers) {
|
for (var peer in peers) {
|
||||||
final state = onlineStates[peer.id];
|
final state = onlineStates[peer.id];
|
||||||
peer.online = state != null && state != false;
|
peer.online = state != null && state != false;
|
||||||
|
@ -17,8 +17,6 @@ enum PeerTabIndex {
|
|||||||
group,
|
group,
|
||||||
}
|
}
|
||||||
|
|
||||||
const String defaultGroupTabname = 'Group';
|
|
||||||
|
|
||||||
class PeerTabModel with ChangeNotifier {
|
class PeerTabModel with ChangeNotifier {
|
||||||
WeakReference<FFI> parent;
|
WeakReference<FFI> parent;
|
||||||
int get currentTab => _currentTab;
|
int get currentTab => _currentTab;
|
||||||
@ -28,7 +26,7 @@ class PeerTabModel with ChangeNotifier {
|
|||||||
'Favorites',
|
'Favorites',
|
||||||
'Discovered',
|
'Discovered',
|
||||||
'Address Book',
|
'Address Book',
|
||||||
//defaultGroupTabname,
|
'Group',
|
||||||
];
|
];
|
||||||
final List<IconData> icons = [
|
final List<IconData> icons = [
|
||||||
Icons.access_time_filled,
|
Icons.access_time_filled,
|
||||||
@ -37,7 +35,7 @@ class PeerTabModel with ChangeNotifier {
|
|||||||
IconFont.addressBook,
|
IconFont.addressBook,
|
||||||
Icons.group,
|
Icons.group,
|
||||||
];
|
];
|
||||||
final List<bool> _isVisible = List.filled(4, true, growable: false);
|
final List<bool> _isVisible = List.filled(5, true, growable: false);
|
||||||
List<bool> get isVisible => _isVisible;
|
List<bool> get isVisible => _isVisible;
|
||||||
List<int> get indexs => List.generate(tabNames.length, (index) => index);
|
List<int> get indexs => List.generate(tabNames.length, (index) => index);
|
||||||
List<int> get visibleIndexs => indexs.where((e) => _isVisible[e]).toList();
|
List<int> get visibleIndexs => indexs.where((e) => _isVisible[e]).toList();
|
||||||
@ -85,17 +83,9 @@ class PeerTabModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String tabTooltip(int index, String groupName) {
|
String tabTooltip(int index) {
|
||||||
if (index >= 0 && index < tabNames.length) {
|
if (index >= 0 && index < tabNames.length) {
|
||||||
if (index == PeerTabIndex.group.index) {
|
return translate(tabNames[index]);
|
||||||
if (gFFI.userModel.isAdmin.value || groupName.isEmpty) {
|
|
||||||
return translate(defaultGroupTabname);
|
|
||||||
} else {
|
|
||||||
return '${translate('Group')}: $groupName';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return translate(tabNames[index]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
assert(false);
|
assert(false);
|
||||||
return index.toString();
|
return index.toString();
|
||||||
|
@ -45,7 +45,7 @@ class UserModel {
|
|||||||
refreshingUser = false;
|
refreshingUser = false;
|
||||||
final status = response.statusCode;
|
final status = response.statusCode;
|
||||||
if (status == 401 || status == 400) {
|
if (status == 401 || status == 400) {
|
||||||
reset(clearAbCache: status == 401);
|
reset(resetOther: status == 401);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final data = json.decode(utf8.decode(response.bodyBytes));
|
final data = json.decode(utf8.decode(response.bodyBytes));
|
||||||
@ -84,11 +84,13 @@ class UserModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> reset({bool clearAbCache = false}) async {
|
Future<void> reset({bool resetOther = false}) async {
|
||||||
await bind.mainSetLocalOption(key: 'access_token', value: '');
|
await bind.mainSetLocalOption(key: 'access_token', value: '');
|
||||||
await bind.mainSetLocalOption(key: 'user_info', value: '');
|
await bind.mainSetLocalOption(key: 'user_info', value: '');
|
||||||
if (clearAbCache) await bind.mainClearAb();
|
if (resetOther) {
|
||||||
await gFFI.groupModel.reset();
|
await gFFI.abModel.reset();
|
||||||
|
await gFFI.groupModel.reset();
|
||||||
|
}
|
||||||
userName.value = '';
|
userName.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +122,7 @@ class UserModel {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("request /api/logout failed: err=$e");
|
debugPrint("request /api/logout failed: err=$e");
|
||||||
} finally {
|
} finally {
|
||||||
await reset(clearAbCache: true);
|
await reset(resetOther: true);
|
||||||
gFFI.dialogManager.dismissByTag(tag);
|
gFFI.dialogManager.dismissByTag(tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1650,6 +1650,106 @@ macro_rules! deserialize_default {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct GroupPeer {
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
deserialize_with = "deserialize_string",
|
||||||
|
skip_serializing_if = "String::is_empty"
|
||||||
|
)]
|
||||||
|
pub id: String,
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
deserialize_with = "deserialize_string",
|
||||||
|
skip_serializing_if = "String::is_empty"
|
||||||
|
)]
|
||||||
|
pub username: String,
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
deserialize_with = "deserialize_string",
|
||||||
|
skip_serializing_if = "String::is_empty"
|
||||||
|
)]
|
||||||
|
pub hostname: String,
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
deserialize_with = "deserialize_string",
|
||||||
|
skip_serializing_if = "String::is_empty"
|
||||||
|
)]
|
||||||
|
pub platform: String,
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
deserialize_with = "deserialize_string",
|
||||||
|
skip_serializing_if = "String::is_empty"
|
||||||
|
)]
|
||||||
|
pub login_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct GroupUser {
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
deserialize_with = "deserialize_string",
|
||||||
|
skip_serializing_if = "String::is_empty"
|
||||||
|
)]
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Group {
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
deserialize_with = "deserialize_string",
|
||||||
|
skip_serializing_if = "String::is_empty"
|
||||||
|
)]
|
||||||
|
pub access_token: String,
|
||||||
|
#[serde(default, deserialize_with = "deserialize_vec_groupuser")]
|
||||||
|
pub users: Vec<GroupUser>,
|
||||||
|
#[serde(default, deserialize_with = "deserialize_vec_grouppeer")]
|
||||||
|
pub peers: Vec<GroupPeer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Group {
|
||||||
|
fn path() -> PathBuf {
|
||||||
|
let filename = format!("{}_group", APP_NAME.read().unwrap().clone());
|
||||||
|
Config::path(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store(json: String) {
|
||||||
|
if let Ok(mut file) = std::fs::File::create(Self::path()) {
|
||||||
|
let data = compress(json.as_bytes());
|
||||||
|
let max_len = 64 * 1024 * 1024;
|
||||||
|
if data.len() > max_len {
|
||||||
|
// maxlen of function decompress
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Ok(data) = symmetric_crypt(&data, true) {
|
||||||
|
file.write_all(&data).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load() -> Self {
|
||||||
|
if let Ok(mut file) = std::fs::File::open(Self::path()) {
|
||||||
|
let mut data = vec![];
|
||||||
|
if file.read_to_end(&mut data).is_ok() {
|
||||||
|
if let Ok(data) = symmetric_crypt(&data, false) {
|
||||||
|
let data = decompress(&data);
|
||||||
|
if let Ok(group) = serde_json::from_str::<Self>(&String::from_utf8_lossy(&data))
|
||||||
|
{
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Self::remove();
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove() {
|
||||||
|
std::fs::remove_file(Self::path()).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
deserialize_default!(deserialize_string, String);
|
deserialize_default!(deserialize_string, String);
|
||||||
deserialize_default!(deserialize_bool, bool);
|
deserialize_default!(deserialize_bool, bool);
|
||||||
deserialize_default!(deserialize_i32, i32);
|
deserialize_default!(deserialize_i32, i32);
|
||||||
@ -1658,6 +1758,8 @@ deserialize_default!(deserialize_vec_string, Vec<String>);
|
|||||||
deserialize_default!(deserialize_vec_i32_string_i32, Vec<(i32, String, i32)>);
|
deserialize_default!(deserialize_vec_i32_string_i32, Vec<(i32, String, i32)>);
|
||||||
deserialize_default!(deserialize_vec_discoverypeer, Vec<DiscoveryPeer>);
|
deserialize_default!(deserialize_vec_discoverypeer, Vec<DiscoveryPeer>);
|
||||||
deserialize_default!(deserialize_vec_abpeer, Vec<AbPeer>);
|
deserialize_default!(deserialize_vec_abpeer, Vec<AbPeer>);
|
||||||
|
deserialize_default!(deserialize_vec_groupuser, Vec<GroupUser>);
|
||||||
|
deserialize_default!(deserialize_vec_grouppeer, Vec<GroupPeer>);
|
||||||
deserialize_default!(deserialize_keypair, KeyPair);
|
deserialize_default!(deserialize_keypair, KeyPair);
|
||||||
deserialize_default!(deserialize_size, Size);
|
deserialize_default!(deserialize_size, Size);
|
||||||
deserialize_default!(deserialize_hashmap_string_string, HashMap<String, String>);
|
deserialize_default!(deserialize_hashmap_string_string, HashMap<String, String>);
|
||||||
|
@ -1187,6 +1187,24 @@ pub fn main_load_ab() -> String {
|
|||||||
serde_json::to_string(&config::Ab::load()).unwrap_or_default()
|
serde_json::to_string(&config::Ab::load()).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn main_save_group(json: String) {
|
||||||
|
if json.len() > 1024 {
|
||||||
|
std::thread::spawn(|| {
|
||||||
|
config::Group::store(json);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
config::Group::store(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_clear_group() {
|
||||||
|
config::Group::remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_load_group() -> String {
|
||||||
|
serde_json::to_string(&config::Group::load()).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session_send_pointer(session_id: SessionID, msg: String) {
|
pub fn session_send_pointer(session_id: SessionID, msg: String) {
|
||||||
super::flutter::session_send_pointer(session_id, msg);
|
super::flutter::session_send_pointer(session_id, msg);
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", "自动关闭不活跃的会话"),
|
("auto_disconnect_option_tip", "自动关闭不活跃的会话"),
|
||||||
("Connection failed due to inactivity", "由于长时间无操作, 连接被自动断开"),
|
("Connection failed due to inactivity", "由于长时间无操作, 连接被自动断开"),
|
||||||
("Check for software update on startup", "启动时检查软件更新"),
|
("Check for software update on startup", "启动时检查软件更新"),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", "请升级专业版服务器到{}或更高版本!"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", "Automatisches Schließen eingehender Sitzungen bei Inaktivität des Benutzers"),
|
("auto_disconnect_option_tip", "Automatisches Schließen eingehender Sitzungen bei Inaktivität des Benutzers"),
|
||||||
("Connection failed due to inactivity", "Automatische Trennung der Verbindung aufgrund von Inaktivität"),
|
("Connection failed due to inactivity", "Automatische Trennung der Verbindung aufgrund von Inaktivität"),
|
||||||
("Check for software update on startup", "Beim Start auf Softwareaktualisierung prüfen"),
|
("Check for software update on startup", "Beim Start auf Softwareaktualisierung prüfen"),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -90,5 +90,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Decline", "Decline"),
|
("Decline", "Decline"),
|
||||||
("auto_disconnect_option_tip", "Automatically close incoming sessions on user inactivity"),
|
("auto_disconnect_option_tip", "Automatically close incoming sessions on user inactivity"),
|
||||||
("Connection failed due to inactivity", "Automatically disconnected due to inactivity"),
|
("Connection failed due to inactivity", "Automatically disconnected due to inactivity"),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", "Please upgrade RustDesk Server Pro to version {} or newer!")
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", "Secara otomatis akan menutup sesi ketika pengguna tidak beraktivitas"),
|
("auto_disconnect_option_tip", "Secara otomatis akan menutup sesi ketika pengguna tidak beraktivitas"),
|
||||||
("Connection failed due to inactivity", "Secara otomatis akan terputus ketik tidak ada aktivitas."),
|
("Connection failed due to inactivity", "Secara otomatis akan terputus ketik tidak ada aktivitas."),
|
||||||
("Check for software update on startup", "Periksa pembaruan aplikasi saat sistem dinyalakan."),
|
("Check for software update on startup", "Periksa pembaruan aplikasi saat sistem dinyalakan."),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", "Connessione non riuscita a causa di inattività"),
|
("Connection failed due to inactivity", "Connessione non riuscita a causa di inattività"),
|
||||||
("Check for software update on startup", "All'avvio programma verifica presenza di aggiornamenti"),
|
("Check for software update on startup", "All'avvio programma verifica presenza di aggiornamenti"),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", "Automātiski aizvērt ienākošās sesijas lietotāja neaktivitātes gadījumā"),
|
("auto_disconnect_option_tip", "Automātiski aizvērt ienākošās sesijas lietotāja neaktivitātes gadījumā"),
|
||||||
("Connection failed due to inactivity", "Automātiski atvienots neaktivitātes dēļ"),
|
("Connection failed due to inactivity", "Automātiski atvienots neaktivitātes dēļ"),
|
||||||
("Check for software update on startup", "Startējot pārbaudīt, vai nav programmatūras atjauninājumu"),
|
("Check for software update on startup", "Startējot pārbaudīt, vai nav programmatūras atjauninājumu"),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", "Automatycznie rozłącz sesje przychodzące przy braku aktywności użytkownika"),
|
("auto_disconnect_option_tip", "Automatycznie rozłącz sesje przychodzące przy braku aktywności użytkownika"),
|
||||||
("Connection failed due to inactivity", "Automatycznie rozłącz przy bezczynności"),
|
("Connection failed due to inactivity", "Automatycznie rozłącz przy bezczynności"),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", "Автоматически закрывать входящие сеансы при неактивности пользователя"),
|
("auto_disconnect_option_tip", "Автоматически закрывать входящие сеансы при неактивности пользователя"),
|
||||||
("Connection failed due to inactivity", "Подключение не выполнено из-за неактивности"),
|
("Connection failed due to inactivity", "Подключение не выполнено из-за неактивности"),
|
||||||
("Check for software update on startup", "Проверять обновления программы при запуске"),
|
("Check for software update on startup", "Проверять обновления программы при запуске"),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -555,5 +555,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("auto_disconnect_option_tip", ""),
|
("auto_disconnect_option_tip", ""),
|
||||||
("Connection failed due to inactivity", ""),
|
("Connection failed due to inactivity", ""),
|
||||||
("Check for software update on startup", ""),
|
("Check for software update on startup", ""),
|
||||||
|
("upgrade_rustdesk_server_pro_to_{}_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user