peer card batch operation
Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
parent
bdc5cded22
commit
5a0865559c
@ -9,6 +9,7 @@ import 'package:get/get.dart';
|
|||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
|
import 'address_book.dart';
|
||||||
|
|
||||||
void clientClose(SessionID sessionId, OverlayDialogManager dialogManager) {
|
void clientClose(SessionID sessionId, OverlayDialogManager dialogManager) {
|
||||||
msgBox(sessionId, 'info', 'Close', 'Are you sure to close the connection?',
|
msgBox(sessionId, 'info', 'Close', 'Are you sure to close the connection?',
|
||||||
@ -1350,3 +1351,98 @@ customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async {
|
|||||||
);
|
);
|
||||||
msgBoxCommon(ffi.dialogManager, 'Custom Image Quality', content, [btnClose]);
|
msgBoxCommon(ffi.dialogManager, 'Custom Image Quality', content, [btnClose]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deletePeerConfirmDialog(Function onSubmit) async {
|
||||||
|
gFFI.dialogManager.show(
|
||||||
|
(setState, close, context) {
|
||||||
|
submit() async {
|
||||||
|
await onSubmit();
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CustomAlertDialog(
|
||||||
|
title: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.delete_rounded,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
Text(translate('Delete')).paddingOnly(
|
||||||
|
left: 10,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
content: SizedBox.shrink(),
|
||||||
|
actions: [
|
||||||
|
dialogButton(
|
||||||
|
"Cancel",
|
||||||
|
icon: Icon(Icons.close_rounded),
|
||||||
|
onPressed: close,
|
||||||
|
isOutline: true,
|
||||||
|
),
|
||||||
|
dialogButton(
|
||||||
|
"OK",
|
||||||
|
icon: Icon(Icons.done_rounded),
|
||||||
|
onPressed: submit,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onSubmit: submit,
|
||||||
|
onCancel: close,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void editAbTagDialog(
|
||||||
|
List<dynamic> currentTags, Function(List<dynamic>) onSubmit) {
|
||||||
|
var isInProgress = false;
|
||||||
|
|
||||||
|
final tags = List.of(gFFI.abModel.tags);
|
||||||
|
var selectedTag = currentTags.obs;
|
||||||
|
|
||||||
|
gFFI.dialogManager.show((setState, close, context) {
|
||||||
|
submit() async {
|
||||||
|
setState(() {
|
||||||
|
isInProgress = true;
|
||||||
|
});
|
||||||
|
await onSubmit(selectedTag);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CustomAlertDialog(
|
||||||
|
title: Text(translate("Edit Tag")),
|
||||||
|
content: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Offstage(
|
||||||
|
offstage: !isInProgress, child: const LinearProgressIndicator())
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
dialogButton("Cancel", onPressed: close, isOutline: true),
|
||||||
|
dialogButton("OK", onPressed: submit),
|
||||||
|
],
|
||||||
|
onSubmit: submit,
|
||||||
|
onCancel: close,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -3,8 +3,11 @@ import 'dart:io';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/address_book.dart';
|
import 'package:flutter_hbb/common/widgets/address_book.dart';
|
||||||
|
import 'package:flutter_hbb/common/widgets/dialog.dart';
|
||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
|
import 'package:flutter_hbb/models/peer_tab_model.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../common/formatter/id_formatter.dart';
|
import '../../common/formatter/id_formatter.dart';
|
||||||
@ -58,17 +61,21 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
final peer = super.widget.peer;
|
final peer = super.widget.peer;
|
||||||
final name =
|
final name =
|
||||||
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
||||||
|
final PeerTabModel peerTabModel = Provider.of(context);
|
||||||
|
final selected = peerTabModel.isPeerSelected(peer.id);
|
||||||
return Card(
|
return Card(
|
||||||
margin: EdgeInsets.symmetric(horizontal: 2),
|
margin: EdgeInsets.symmetric(horizontal: 2),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: !isWebDesktop ? () => connect(context, peer.id) : null,
|
onTap: () {
|
||||||
|
if (peerTabModel.multiSelectionMode) {
|
||||||
|
peerTabModel.onPeerCardTap(peer);
|
||||||
|
} else {
|
||||||
|
if (!isWebDesktop) connect(context, peer.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
onDoubleTap: isWebDesktop ? () => connect(context, peer.id) : null,
|
onDoubleTap: isWebDesktop ? () => connect(context, peer.id) : null,
|
||||||
onLongPressStart: (details) {
|
onLongPress: () {
|
||||||
final x = details.globalPosition.dx;
|
peerTabModel.togglePeerSelect(peer);
|
||||||
final y = details.globalPosition.dy;
|
|
||||||
_menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
|
||||||
_showPeerMenu(peer.id);
|
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.only(left: 12, top: 8, bottom: 8),
|
padding: EdgeInsets.only(left: 12, top: 8, bottom: 8),
|
||||||
@ -97,24 +104,30 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
],
|
],
|
||||||
).paddingOnly(left: 8.0),
|
).paddingOnly(left: 8.0),
|
||||||
),
|
),
|
||||||
InkWell(
|
selected
|
||||||
child: const Padding(
|
? Padding(
|
||||||
padding: EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
child: Icon(Icons.more_vert)),
|
child: checkBox(),
|
||||||
onTapDown: (e) {
|
)
|
||||||
final x = e.globalPosition.dx;
|
: InkWell(
|
||||||
final y = e.globalPosition.dy;
|
child: const Padding(
|
||||||
_menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
padding: EdgeInsets.all(12),
|
||||||
},
|
child: Icon(Icons.more_vert)),
|
||||||
onTap: () {
|
onTapDown: (e) {
|
||||||
_showPeerMenu(peer.id);
|
final x = e.globalPosition.dx;
|
||||||
})
|
final y = e.globalPosition.dy;
|
||||||
|
_menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
||||||
|
},
|
||||||
|
onTap: () {
|
||||||
|
_showPeerMenu(peer.id);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDesktop() {
|
Widget _buildDesktop() {
|
||||||
|
final PeerTabModel peerTabModel = Provider.of(context);
|
||||||
final peer = super.widget.peer;
|
final peer = super.widget.peer;
|
||||||
var deco = Rx<BoxDecoration?>(
|
var deco = Rx<BoxDecoration?>(
|
||||||
BoxDecoration(
|
BoxDecoration(
|
||||||
@ -144,7 +157,18 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onDoubleTap: () => widget.connect(context, peer.id),
|
onDoubleTap: peerTabModel.multiSelectionMode
|
||||||
|
? null
|
||||||
|
: () => widget.connect(context, peer.id),
|
||||||
|
onLongPress: () {
|
||||||
|
peerTabModel.togglePeerSelect(peer);
|
||||||
|
},
|
||||||
|
onSecondaryTapDown: (_) {
|
||||||
|
peerTabModel.togglePeerSelect(peer);
|
||||||
|
},
|
||||||
|
onTap: peerTabModel.multiSelectionMode
|
||||||
|
? () => peerTabModel.onPeerCardTap(peer)
|
||||||
|
: null,
|
||||||
child: Obx(() => peerCardUiType.value == PeerUiType.grid
|
child: Obx(() => peerCardUiType.value == PeerUiType.grid
|
||||||
? _buildPeerCard(context, peer, deco)
|
? _buildPeerCard(context, peer, deco)
|
||||||
: _buildPeerTile(context, peer, deco))),
|
: _buildPeerTile(context, peer, deco))),
|
||||||
@ -153,6 +177,8 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
|
|
||||||
Widget _buildPeerTile(
|
Widget _buildPeerTile(
|
||||||
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
||||||
|
final PeerTabModel peerTabModel = Provider.of(context);
|
||||||
|
final selected = peerTabModel.isPeerSelected(peer.id);
|
||||||
final name =
|
final name =
|
||||||
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
||||||
final greyStyle = TextStyle(
|
final greyStyle = TextStyle(
|
||||||
@ -212,7 +238,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
],
|
],
|
||||||
).marginOnly(top: 2),
|
).marginOnly(top: 2),
|
||||||
),
|
),
|
||||||
_actionMore(peer),
|
selected ? checkBox() : _actionMore(peer),
|
||||||
],
|
],
|
||||||
).paddingOnly(left: 10.0, top: 3.0),
|
).paddingOnly(left: 10.0, top: 3.0),
|
||||||
),
|
),
|
||||||
@ -225,6 +251,8 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
|
|
||||||
Widget _buildPeerCard(
|
Widget _buildPeerCard(
|
||||||
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
||||||
|
final PeerTabModel peerTabModel = Provider.of(context);
|
||||||
|
final selected = peerTabModel.isPeerSelected(peer.id);
|
||||||
final name =
|
final name =
|
||||||
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
||||||
return Card(
|
return Card(
|
||||||
@ -294,7 +322,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
)),
|
)),
|
||||||
]).paddingSymmetric(vertical: 8)),
|
]).paddingSymmetric(vertical: 8)),
|
||||||
_actionMore(peer),
|
selected ? checkBox() : _actionMore(peer),
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: 12.0),
|
).paddingSymmetric(horizontal: 12.0),
|
||||||
)
|
)
|
||||||
@ -306,6 +334,13 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget checkBox() {
|
||||||
|
return Icon(
|
||||||
|
Icons.check_box,
|
||||||
|
color: MyTheme.accent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _actionMore(Peer peer) => Listener(
|
Widget _actionMore(Peer peer) => Listener(
|
||||||
onPointerDown: (e) {
|
onPointerDown: (e) {
|
||||||
final x = e.position.dx;
|
final x = e.position.dx;
|
||||||
@ -332,9 +367,11 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
|
|
||||||
abstract class BasePeerCard extends StatelessWidget {
|
abstract class BasePeerCard extends StatelessWidget {
|
||||||
final Peer peer;
|
final Peer peer;
|
||||||
|
final PeerTabIndex tab;
|
||||||
final EdgeInsets? menuPadding;
|
final EdgeInsets? menuPadding;
|
||||||
|
|
||||||
BasePeerCard({required this.peer, this.menuPadding, Key? key})
|
BasePeerCard(
|
||||||
|
{required this.peer, required this.tab, this.menuPadding, Key? key})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -524,9 +561,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
MenuEntryBase<String> _removeAction(
|
MenuEntryBase<String> _removeAction(String id) {
|
||||||
String id, Future<void> Function() reloadFunc,
|
|
||||||
{bool isLan = false}) {
|
|
||||||
return MenuEntryButton<String>(
|
return MenuEntryButton<String>(
|
||||||
childBuilder: (TextStyle? style) => Row(
|
childBuilder: (TextStyle? style) => Row(
|
||||||
children: [
|
children: [
|
||||||
@ -545,7 +580,33 @@ abstract class BasePeerCard extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
proc: () {
|
proc: () {
|
||||||
_delete(id, isLan, reloadFunc);
|
onSubmit() async {
|
||||||
|
switch (tab) {
|
||||||
|
case PeerTabIndex.recent:
|
||||||
|
await bind.mainRemovePeer(id: id);
|
||||||
|
await bind.mainLoadRecentPeers();
|
||||||
|
break;
|
||||||
|
case PeerTabIndex.fav:
|
||||||
|
final favs = (await bind.mainGetFav()).toList();
|
||||||
|
if (favs.remove(id)) {
|
||||||
|
await bind.mainStoreFav(favs: favs);
|
||||||
|
await bind.mainLoadFavPeers();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PeerTabIndex.lan:
|
||||||
|
await bind.mainRemoveDiscovered(id: id);
|
||||||
|
await bind.mainLoadLanPeers();
|
||||||
|
break;
|
||||||
|
case PeerTabIndex.ab:
|
||||||
|
gFFI.abModel.deletePeer(id);
|
||||||
|
await gFFI.abModel.pushAb();
|
||||||
|
break;
|
||||||
|
case PeerTabIndex.group:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deletePeerConfirmDialog(onSubmit);
|
||||||
},
|
},
|
||||||
padding: menuPadding,
|
padding: menuPadding,
|
||||||
dismissOnClicked: true,
|
dismissOnClicked: true,
|
||||||
@ -721,62 +782,15 @@ abstract class BasePeerCard extends StatelessWidget {
|
|||||||
|
|
||||||
@protected
|
@protected
|
||||||
void _update();
|
void _update();
|
||||||
|
|
||||||
void _delete(String id, bool isLan, Function reloadFunc) async {
|
|
||||||
gFFI.dialogManager.show(
|
|
||||||
(setState, close, context) {
|
|
||||||
submit() async {
|
|
||||||
if (isLan) {
|
|
||||||
await bind.mainRemoveDiscovered(id: id);
|
|
||||||
} else {
|
|
||||||
final favs = (await bind.mainGetFav()).toList();
|
|
||||||
if (favs.remove(id)) {
|
|
||||||
await bind.mainStoreFav(favs: favs);
|
|
||||||
}
|
|
||||||
await bind.mainRemovePeer(id: id);
|
|
||||||
}
|
|
||||||
await reloadFunc();
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return CustomAlertDialog(
|
|
||||||
title: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.delete_rounded,
|
|
||||||
color: Colors.red,
|
|
||||||
),
|
|
||||||
Text(translate('Delete')).paddingOnly(
|
|
||||||
left: 10,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
content: SizedBox.shrink(),
|
|
||||||
actions: [
|
|
||||||
dialogButton(
|
|
||||||
"Cancel",
|
|
||||||
icon: Icon(Icons.close_rounded),
|
|
||||||
onPressed: close,
|
|
||||||
isOutline: true,
|
|
||||||
),
|
|
||||||
dialogButton(
|
|
||||||
"OK",
|
|
||||||
icon: Icon(Icons.done_rounded),
|
|
||||||
onPressed: submit,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onSubmit: submit,
|
|
||||||
onCancel: close,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class RecentPeerCard extends BasePeerCard {
|
class RecentPeerCard extends BasePeerCard {
|
||||||
RecentPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
RecentPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
||||||
: super(peer: peer, menuPadding: menuPadding, key: key);
|
: super(
|
||||||
|
peer: peer,
|
||||||
|
tab: PeerTabIndex.recent,
|
||||||
|
menuPadding: menuPadding,
|
||||||
|
key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||||
@ -817,9 +831,7 @@ class RecentPeerCard extends BasePeerCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
menuItems.add(MenuEntryDivider());
|
menuItems.add(MenuEntryDivider());
|
||||||
menuItems.add(_removeAction(peer.id, () async {
|
menuItems.add(_removeAction(peer.id));
|
||||||
await bind.mainLoadRecentPeers();
|
|
||||||
}));
|
|
||||||
return menuItems;
|
return menuItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -830,7 +842,11 @@ class RecentPeerCard extends BasePeerCard {
|
|||||||
|
|
||||||
class FavoritePeerCard extends BasePeerCard {
|
class FavoritePeerCard extends BasePeerCard {
|
||||||
FavoritePeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
FavoritePeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
||||||
: super(peer: peer, menuPadding: menuPadding, key: key);
|
: super(
|
||||||
|
peer: peer,
|
||||||
|
tab: PeerTabIndex.fav,
|
||||||
|
menuPadding: menuPadding,
|
||||||
|
key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||||
@ -865,9 +881,7 @@ class FavoritePeerCard extends BasePeerCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
menuItems.add(MenuEntryDivider());
|
menuItems.add(MenuEntryDivider());
|
||||||
menuItems.add(_removeAction(peer.id, () async {
|
menuItems.add(_removeAction(peer.id));
|
||||||
await bind.mainLoadFavPeers();
|
|
||||||
}));
|
|
||||||
return menuItems;
|
return menuItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -878,7 +892,11 @@ class FavoritePeerCard extends BasePeerCard {
|
|||||||
|
|
||||||
class DiscoveredPeerCard extends BasePeerCard {
|
class DiscoveredPeerCard extends BasePeerCard {
|
||||||
DiscoveredPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
DiscoveredPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
||||||
: super(peer: peer, menuPadding: menuPadding, key: key);
|
: super(
|
||||||
|
peer: peer,
|
||||||
|
tab: PeerTabIndex.lan,
|
||||||
|
menuPadding: menuPadding,
|
||||||
|
key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||||
@ -915,11 +933,7 @@ class DiscoveredPeerCard extends BasePeerCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
menuItems.add(MenuEntryDivider());
|
menuItems.add(MenuEntryDivider());
|
||||||
menuItems.add(
|
menuItems.add(_removeAction(peer.id));
|
||||||
_removeAction(peer.id, () async {
|
|
||||||
await bind.mainLoadLanPeers();
|
|
||||||
}, isLan: true),
|
|
||||||
);
|
|
||||||
return menuItems;
|
return menuItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -930,7 +944,11 @@ class DiscoveredPeerCard extends BasePeerCard {
|
|||||||
|
|
||||||
class AddressBookPeerCard extends BasePeerCard {
|
class AddressBookPeerCard extends BasePeerCard {
|
||||||
AddressBookPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
AddressBookPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
||||||
: super(peer: peer, menuPadding: menuPadding, key: key);
|
: super(
|
||||||
|
peer: peer,
|
||||||
|
tab: PeerTabIndex.ab,
|
||||||
|
menuPadding: menuPadding,
|
||||||
|
key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||||
@ -959,7 +977,7 @@ class AddressBookPeerCard extends BasePeerCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
menuItems.add(MenuEntryDivider());
|
menuItems.add(MenuEntryDivider());
|
||||||
menuItems.add(_removeAction(peer.id, () async {}));
|
menuItems.add(_removeAction(peer.id));
|
||||||
return menuItems;
|
return menuItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -967,27 +985,6 @@ class AddressBookPeerCard extends BasePeerCard {
|
|||||||
@override
|
@override
|
||||||
void _update() => gFFI.abModel.pullAb();
|
void _update() => gFFI.abModel.pullAb();
|
||||||
|
|
||||||
@protected
|
|
||||||
@override
|
|
||||||
MenuEntryBase<String> _removeAction(
|
|
||||||
String id, Future<void> Function() reloadFunc,
|
|
||||||
{bool isLan = false}) {
|
|
||||||
return MenuEntryButton<String>(
|
|
||||||
childBuilder: (TextStyle? style) => Text(
|
|
||||||
translate('Remove'),
|
|
||||||
style: style,
|
|
||||||
),
|
|
||||||
proc: () {
|
|
||||||
() async {
|
|
||||||
gFFI.abModel.deletePeer(id);
|
|
||||||
await gFFI.abModel.pushAb();
|
|
||||||
}();
|
|
||||||
},
|
|
||||||
padding: super.menuPadding,
|
|
||||||
dismissOnClicked: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
MenuEntryBase<String> _editTagAction(String id) {
|
MenuEntryBase<String> _editTagAction(String id) {
|
||||||
return MenuEntryButton<String>(
|
return MenuEntryButton<String>(
|
||||||
@ -996,70 +993,24 @@ class AddressBookPeerCard extends BasePeerCard {
|
|||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
proc: () {
|
proc: () {
|
||||||
_abEditTag(id);
|
editAbTagDialog(gFFI.abModel.getPeerTags(id), (selectedTag) async {
|
||||||
|
gFFI.abModel.changeTagForPeer(id, selectedTag);
|
||||||
|
await gFFI.abModel.pushAb();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
padding: super.menuPadding,
|
padding: super.menuPadding,
|
||||||
dismissOnClicked: true,
|
dismissOnClicked: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _abEditTag(String id) {
|
|
||||||
var isInProgress = false;
|
|
||||||
|
|
||||||
final tags = List.of(gFFI.abModel.tags);
|
|
||||||
var selectedTag = gFFI.abModel.getPeerTags(id).obs;
|
|
||||||
|
|
||||||
gFFI.dialogManager.show((setState, close, context) {
|
|
||||||
submit() async {
|
|
||||||
setState(() {
|
|
||||||
isInProgress = true;
|
|
||||||
});
|
|
||||||
gFFI.abModel.changeTagForPeer(id, selectedTag);
|
|
||||||
await gFFI.abModel.pushAb();
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return CustomAlertDialog(
|
|
||||||
title: Text(translate("Edit Tag")),
|
|
||||||
content: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
|
||||||
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),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Offstage(
|
|
||||||
offstage: !isInProgress, child: const LinearProgressIndicator())
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
dialogButton("Cancel", onPressed: close, isOutline: true),
|
|
||||||
dialogButton("OK", onPressed: submit),
|
|
||||||
],
|
|
||||||
onSubmit: submit,
|
|
||||||
onCancel: close,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyGroupPeerCard extends BasePeerCard {
|
class MyGroupPeerCard extends BasePeerCard {
|
||||||
MyGroupPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
MyGroupPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key})
|
||||||
: super(peer: peer, menuPadding: menuPadding, key: key);
|
: super(
|
||||||
|
peer: peer,
|
||||||
|
tab: PeerTabIndex.group,
|
||||||
|
menuPadding: menuPadding,
|
||||||
|
key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/address_book.dart';
|
import 'package:flutter_hbb/common/widgets/address_book.dart';
|
||||||
|
import 'package:flutter_hbb/common/widgets/dialog.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/my_group.dart';
|
import 'package:flutter_hbb/common/widgets/my_group.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/peer_card.dart';
|
import 'package:flutter_hbb/common/widgets/peer_card.dart';
|
||||||
@ -83,6 +84,11 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
|
Widget selectionWrap(Widget widget) {
|
||||||
|
return model.multiSelectionMode ? createMultiSelectionBar() : widget;
|
||||||
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
textBaseline: TextBaseline.ideographic,
|
textBaseline: TextBaseline.ideographic,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -91,7 +97,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
height: 32,
|
height: 32,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: isDesktop ? null : EdgeInsets.symmetric(horizontal: 2),
|
padding: isDesktop ? null : EdgeInsets.symmetric(horizontal: 2),
|
||||||
child: Row(
|
child: selectionWrap(Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: _createSwitchBar(context)),
|
Expanded(child: _createSwitchBar(context)),
|
||||||
@ -127,7 +133,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
).marginOnly(left: 8),
|
).marginOnly(left: 8),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_createPeersView(),
|
_createPeersView(),
|
||||||
@ -251,6 +257,167 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget createMultiSelectionBar() {
|
||||||
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
deleteSelection(),
|
||||||
|
addSelectionToFav(),
|
||||||
|
addSelectionToAb(),
|
||||||
|
editSelectionTags(),
|
||||||
|
Expanded(child: Container()),
|
||||||
|
selectionCount(model.selectedPeers.length),
|
||||||
|
selectAll(),
|
||||||
|
closeSelection(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget deleteSelection() {
|
||||||
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
|
return InkWell(
|
||||||
|
onTap: () {
|
||||||
|
onSubmit() async {
|
||||||
|
final peers = model.selectedPeers;
|
||||||
|
switch (model.currentTab) {
|
||||||
|
case 0:
|
||||||
|
peers.map((p) async {
|
||||||
|
await bind.mainRemovePeer(id: p.id);
|
||||||
|
}).toList();
|
||||||
|
await bind.mainLoadRecentPeers();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
final favs = (await bind.mainGetFav()).toList();
|
||||||
|
peers.map((p) {
|
||||||
|
favs.remove(p.id);
|
||||||
|
}).toList();
|
||||||
|
await bind.mainStoreFav(favs: favs);
|
||||||
|
await bind.mainLoadFavPeers();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
peers.map((p) async {
|
||||||
|
await bind.mainRemoveDiscovered(id: p.id);
|
||||||
|
}).toList();
|
||||||
|
await bind.mainLoadLanPeers();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
gFFI.abModel.deletePeers(peers.map((p) => p.id).toList());
|
||||||
|
await gFFI.abModel.pushAb();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
gFFI.peerTabModel.closeSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
deletePeerConfirmDialog(onSubmit);
|
||||||
|
},
|
||||||
|
child: Tooltip(
|
||||||
|
message: translate('Delete'),
|
||||||
|
child: Icon(Icons.delete, color: Colors.red)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget addSelectionToFav() {
|
||||||
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
|
return Offstage(
|
||||||
|
offstage:
|
||||||
|
model.currentTab != PeerTabIndex.recent.index, // show based on recent
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
final peers = model.selectedPeers;
|
||||||
|
final favs = (await bind.mainGetFav()).toList();
|
||||||
|
for (var p in peers) {
|
||||||
|
if (!favs.contains(p.id)) {
|
||||||
|
favs.add(p.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await bind.mainStoreFav(favs: favs);
|
||||||
|
gFFI.peerTabModel.closeSelection();
|
||||||
|
},
|
||||||
|
child: Tooltip(
|
||||||
|
message: translate('Add to Favorites'),
|
||||||
|
child: Icon(model.icons[PeerTabIndex.fav.index]))
|
||||||
|
.marginOnly(left: isMobile ? 15 : 10),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget addSelectionToAb() {
|
||||||
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
|
return Offstage(
|
||||||
|
offstage:
|
||||||
|
!gFFI.userModel.isLogin || model.currentTab == PeerTabIndex.ab.index,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
final peers = model.selectedPeers;
|
||||||
|
gFFI.abModel.addPeers(peers);
|
||||||
|
gFFI.abModel.pushAb();
|
||||||
|
gFFI.peerTabModel.closeSelection();
|
||||||
|
},
|
||||||
|
child: Tooltip(
|
||||||
|
message: translate('Add to Address Book'),
|
||||||
|
child: Icon(model.icons[PeerTabIndex.ab.index]))
|
||||||
|
.marginOnly(left: isMobile ? 15 : 10),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget editSelectionTags() {
|
||||||
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
|
return Offstage(
|
||||||
|
offstage: !gFFI.userModel.isLogin ||
|
||||||
|
model.currentTab != PeerTabIndex.ab.index ||
|
||||||
|
gFFI.abModel.tags.isEmpty,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
editAbTagDialog(List.empty(), (selectedTags) async {
|
||||||
|
final peers = model.selectedPeers;
|
||||||
|
gFFI.abModel.changeTagForPeers(
|
||||||
|
peers.map((p) => p.id).toList(), selectedTags);
|
||||||
|
gFFI.abModel.pushAb();
|
||||||
|
gFFI.peerTabModel.closeSelection();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Tooltip(
|
||||||
|
message: translate('Edit Tag'), child: Icon(Icons.tag)))
|
||||||
|
.marginOnly(left: isMobile ? 15 : 10),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget selectionCount(int count) {
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text('$count selected'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget selectAll() {
|
||||||
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
|
return Offstage(
|
||||||
|
offstage:
|
||||||
|
model.selectedPeers.length >= model.currentTabCachedPeers.length,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
model.selectAll();
|
||||||
|
},
|
||||||
|
child: Tooltip(
|
||||||
|
message: translate('Select All'), child: Icon(Icons.select_all))
|
||||||
|
.marginOnly(left: 10),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget closeSelection() {
|
||||||
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
|
return InkWell(
|
||||||
|
onTap: () {
|
||||||
|
model.closeSelection();
|
||||||
|
},
|
||||||
|
child:
|
||||||
|
Tooltip(message: translate('Close'), child: Icon(Icons.clear)))
|
||||||
|
.marginOnly(left: 10);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PeerSearchBar extends StatefulWidget {
|
class PeerSearchBar extends StatefulWidget {
|
||||||
|
@ -172,6 +172,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
|||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
final peers = snapshot.data!;
|
final peers = snapshot.data!;
|
||||||
|
gFFI.peerTabModel.setCurrentTabCachedPeers(peers);
|
||||||
final cards = <Widget>[];
|
final cards = <Widget>[];
|
||||||
for (final peer in peers) {
|
for (final peer in peers) {
|
||||||
final visibilityChild = VisibilityDetector(
|
final visibilityChild = VisibilityDetector(
|
||||||
|
@ -131,6 +131,12 @@ class AbModel {
|
|||||||
peers.add(peer);
|
peers.add(peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addPeers(List<Peer> ps) {
|
||||||
|
for (var p in ps) {
|
||||||
|
addPeer(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void addTag(String tag) async {
|
void addTag(String tag) async {
|
||||||
if (tagContainBy(tag)) {
|
if (tagContainBy(tag)) {
|
||||||
return;
|
return;
|
||||||
@ -146,6 +152,14 @@ class AbModel {
|
|||||||
it.first.tags = tags;
|
it.first.tags = tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void changeTagForPeers(List<String> ids, List<dynamic> tags) {
|
||||||
|
peers.map((e) {
|
||||||
|
if (ids.contains(e.id)) {
|
||||||
|
e.tags = tags;
|
||||||
|
}
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> pushAb() async {
|
Future<void> pushAb() async {
|
||||||
debugPrint("pushAb");
|
debugPrint("pushAb");
|
||||||
final api = "${await bind.mainGetApiServer()}/api/ab";
|
final api = "${await bind.mainGetApiServer()}/api/ab";
|
||||||
@ -192,6 +206,10 @@ class AbModel {
|
|||||||
peers.removeWhere((element) => element.id == id);
|
peers.removeWhere((element) => element.id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deletePeers(List<String> ids) {
|
||||||
|
peers.removeWhere((e) => ids.contains(e.id));
|
||||||
|
}
|
||||||
|
|
||||||
void deleteTag(String tag) {
|
void deleteTag(String tag) {
|
||||||
gFFI.abModel.selectedTags.remove(tag);
|
gFFI.abModel.selectedTags.remove(tag);
|
||||||
tags.removeWhere((element) => element == tag);
|
tags.removeWhere((element) => element == tag);
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
import '../common.dart';
|
import '../common.dart';
|
||||||
import 'model.dart';
|
import 'model.dart';
|
||||||
|
|
||||||
const int groupTabIndex = 4;
|
enum PeerTabIndex {
|
||||||
|
recent,
|
||||||
|
fav,
|
||||||
|
lan,
|
||||||
|
ab,
|
||||||
|
group,
|
||||||
|
}
|
||||||
|
|
||||||
const String defaultGroupTabname = 'Group';
|
const String defaultGroupTabname = 'Group';
|
||||||
|
|
||||||
class PeerTabModel with ChangeNotifier {
|
class PeerTabModel with ChangeNotifier {
|
||||||
@ -26,6 +35,11 @@ class PeerTabModel with ChangeNotifier {
|
|||||||
Icons.group,
|
Icons.group,
|
||||||
];
|
];
|
||||||
List<int> get indexs => List.generate(tabNames.length, (index) => index);
|
List<int> get indexs => List.generate(tabNames.length, (index) => index);
|
||||||
|
List<Peer> _selectedPeers = List.empty(growable: true);
|
||||||
|
List<Peer> get selectedPeers => _selectedPeers;
|
||||||
|
bool get multiSelectionMode => _selectedPeers.isNotEmpty;
|
||||||
|
List<Peer> _currentTabCachedPeers = List.empty(growable: true);
|
||||||
|
List<Peer> get currentTabCachedPeers => _currentTabCachedPeers;
|
||||||
|
|
||||||
PeerTabModel(this.parent) {
|
PeerTabModel(this.parent) {
|
||||||
// init currentTab
|
// init currentTab
|
||||||
@ -45,7 +59,7 @@ class PeerTabModel with ChangeNotifier {
|
|||||||
|
|
||||||
String tabTooltip(int index, String groupName) {
|
String tabTooltip(int index, String groupName) {
|
||||||
if (index >= 0 && index < tabNames.length) {
|
if (index >= 0 && index < tabNames.length) {
|
||||||
if (index == groupTabIndex) {
|
if (index == PeerTabIndex.group.index) {
|
||||||
if (gFFI.userModel.isAdmin.value || groupName.isEmpty) {
|
if (gFFI.userModel.isAdmin.value || groupName.isEmpty) {
|
||||||
return translate(defaultGroupTabname);
|
return translate(defaultGroupTabname);
|
||||||
} else {
|
} else {
|
||||||
@ -66,4 +80,39 @@ class PeerTabModel with ChangeNotifier {
|
|||||||
assert(false);
|
assert(false);
|
||||||
return Icons.help;
|
return Icons.help;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
togglePeerSelect(Peer peer) {
|
||||||
|
if (_selectedPeers.firstWhereOrNull((p) => p.id == peer.id) != null) {
|
||||||
|
_selectedPeers.removeWhere((p) => p.id == peer.id);
|
||||||
|
} else {
|
||||||
|
_selectedPeers.add(peer);
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
onPeerCardTap(Peer peer) {
|
||||||
|
if (!multiSelectionMode) return;
|
||||||
|
togglePeerSelect(peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
closeSelection() {
|
||||||
|
_selectedPeers.clear();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentTabCachedPeers(List<Peer> peers) {
|
||||||
|
Future.delayed(Duration.zero, () {
|
||||||
|
_currentTabCachedPeers = peers;
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
selectAll() {
|
||||||
|
_selectedPeers = _currentTabCachedPeers.toList();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isPeerSelected(String id) {
|
||||||
|
return selectedPeers.firstWhereOrNull((p) => p.id == id) != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ bool refreshingUser = false;
|
|||||||
class UserModel {
|
class UserModel {
|
||||||
final RxString userName = ''.obs;
|
final RxString userName = ''.obs;
|
||||||
final RxBool isAdmin = false.obs;
|
final RxBool isAdmin = false.obs;
|
||||||
|
bool get isLogin => userName.isNotEmpty;
|
||||||
WeakReference<FFI> parent;
|
WeakReference<FFI> parent;
|
||||||
|
|
||||||
UserModel(this.parent);
|
UserModel(this.parent);
|
||||||
|
@ -978,6 +978,11 @@ impl PeerConfig {
|
|||||||
config
|
config
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
if let confy::ConfyError::GeneralLoadError(err) = &err {
|
||||||
|
if err.kind() == std::io::ErrorKind::NotFound {
|
||||||
|
return Default::default();
|
||||||
|
}
|
||||||
|
}
|
||||||
log::error!("Failed to load peer config '{}': {}", id, err);
|
log::error!("Failed to load peer config '{}': {}", id, err);
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user