mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-27 14:59:02 +08:00
peer tab use icon
Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
parent
e6bade2c5f
commit
c02a502a7f
BIN
flutter/assets/address_book.ttf
Normal file
BIN
flutter/assets/address_book.ttf
Normal file
Binary file not shown.
@ -94,6 +94,8 @@ class IconFont {
|
||||
static const IconData menu = IconData(0xe628, fontFamily: _family1);
|
||||
static const IconData search = IconData(0xe6a4, fontFamily: _family2);
|
||||
static const IconData roundClose = IconData(0xe6ed, fontFamily: _family2);
|
||||
static const IconData addressBook =
|
||||
IconData(0xe602, fontFamily: "AddressBook");
|
||||
}
|
||||
|
||||
class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
|
@ -3,7 +3,6 @@ import 'package:flutter_hbb/common/formatter/id_formatter.dart';
|
||||
import 'package:flutter_hbb/common/widgets/peer_card.dart';
|
||||
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import '../../consts.dart';
|
||||
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
||||
import 'package:get/get.dart';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common/hbbs/hbbs.dart';
|
||||
import 'package:flutter_hbb/common/widgets/login.dart';
|
||||
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../common.dart';
|
||||
@ -28,24 +28,20 @@ class _MyGroupState extends State<MyGroup> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() => Offstage(
|
||||
offstage: stateGlobal.svcStatus.value != SvcStatus.ready,
|
||||
child: FutureBuilder<Widget>(
|
||||
future: buildBody(context),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return snapshot.data!;
|
||||
} else {
|
||||
return const Offstage();
|
||||
}
|
||||
}),
|
||||
));
|
||||
return Obx(() {
|
||||
// use username to be same with ab
|
||||
if (gFFI.userModel.userName.value.isEmpty) {
|
||||
return Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: loginDialog, child: Text(translate("Login"))));
|
||||
}
|
||||
return buildBody(context);
|
||||
});
|
||||
}
|
||||
|
||||
Future<Widget> buildBody(BuildContext context) async {
|
||||
Widget buildBody(BuildContext context) {
|
||||
return Obx(() {
|
||||
if (gFFI.groupModel.groupLoading.value ||
|
||||
gFFI.groupModel.peerLoading.value) {
|
||||
if (gFFI.groupModel.groupLoading.value) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
@ -109,9 +105,9 @@ class _MyGroupState extends State<MyGroup> {
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: MyGroupPeerView(
|
||||
child: Obx(() => MyGroupPeerView(
|
||||
menuPadding: widget.menuPadding,
|
||||
initPeers: gFFI.groupModel.peersShow)),
|
||||
initPeers: gFFI.groupModel.peersShow.value))),
|
||||
)
|
||||
],
|
||||
);
|
||||
@ -147,9 +143,9 @@ class _MyGroupState extends State<MyGroup> {
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: MyGroupPeerView(
|
||||
child: Obx(() => MyGroupPeerView(
|
||||
menuPadding: widget.menuPadding,
|
||||
initPeers: gFFI.groupModel.peersShow)),
|
||||
initPeers: gFFI.groupModel.peersShow.value))),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
@ -1,7 +1,3 @@
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common/widgets/address_book.dart';
|
||||
import 'package:flutter_hbb/common/widgets/my_group.dart';
|
||||
@ -10,16 +6,13 @@ import 'package:flutter_hbb/common/widgets/peer_card.dart';
|
||||
import 'package:flutter_hbb/common/widgets/animated_rotation_widget.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/material_mod_popup_menu.dart'
|
||||
as mod_menu;
|
||||
|
||||
import 'package:flutter_hbb/models/peer_tab_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get_rx/src/rx_workers/utils/debouncer.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:visibility_detector/visibility_detector.dart';
|
||||
|
||||
import '../../common.dart';
|
||||
import '../../models/group_model.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
|
||||
class PeerTabPage extends StatefulWidget {
|
||||
@ -68,7 +61,6 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
() => gFFI.groupModel.pull(),
|
||||
),
|
||||
];
|
||||
final _scrollDebounce = Debouncer(delay: Duration(milliseconds: 50));
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -101,11 +93,8 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child:
|
||||
visibleContextMenuListener(_createSwitchBar(context))),
|
||||
buildScrollJumper(),
|
||||
const PeerSearchBar().marginOnly(right: 13),
|
||||
Expanded(child: _createSwitchBar(context)),
|
||||
const PeerSearchBar().marginOnly(right: isMobile ? 0 : 13),
|
||||
_createRefresh(),
|
||||
Offstage(
|
||||
offstage: !isDesktop,
|
||||
@ -124,121 +113,58 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
}
|
||||
|
||||
Widget _createSwitchBar(BuildContext context) {
|
||||
getListener({required Key key, required Widget child, required int index}) {
|
||||
if (isMobile) {
|
||||
return ReorderableDelayedDragStartListener(
|
||||
key: key, child: child, index: index);
|
||||
} else {
|
||||
return ReorderableDragStartListener(
|
||||
key: key, child: child, index: index);
|
||||
}
|
||||
final model = Provider.of<PeerTabModel>(context);
|
||||
|
||||
getTabChild(int t) {
|
||||
final color = model.currentTab == t
|
||||
? MyTheme.tabbar(context).selectedTextColor
|
||||
: MyTheme.tabbar(context).unSelectedTextColor
|
||||
?..withOpacity(0.5);
|
||||
return Obx(() => Tooltip(
|
||||
message: model.tabTooltip(t, gFFI.groupModel.groupName.value),
|
||||
child: Icon(model.tabIcon(t), color: color),
|
||||
));
|
||||
}
|
||||
|
||||
final model = Provider.of<PeerTabModel>(context);
|
||||
int indexCounter = -1;
|
||||
return ReorderableListView(
|
||||
buildDefaultDragHandles: false,
|
||||
onReorder: (oldIndex, newIndex) {
|
||||
model.onReorder(oldIndex, newIndex);
|
||||
},
|
||||
return ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
scrollController: model.sc,
|
||||
children: model.visibleOrderedTabs.map((t) {
|
||||
indexCounter++;
|
||||
return getListener(
|
||||
key: ValueKey(t),
|
||||
index: indexCounter,
|
||||
child: VisibilityDetector(
|
||||
key: ValueKey(t),
|
||||
onVisibilityChanged: (info) {
|
||||
final id = (info.key as ValueKey).value;
|
||||
model.setTabFullyVisible(id, info.visibleFraction > 0.99);
|
||||
},
|
||||
child: Listener(
|
||||
// handle mouse wheel
|
||||
onPointerSignal: (e) {
|
||||
if (e is PointerScrollEvent) {
|
||||
if (!model.sc.canScroll) return;
|
||||
_scrollDebounce.call(() {
|
||||
model.sc.animateTo(model.sc.offset + e.scrollDelta.dy,
|
||||
duration: Duration(milliseconds: 200),
|
||||
curve: Curves.ease);
|
||||
});
|
||||
}
|
||||
},
|
||||
child: InkWell(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: model.currentTab == t
|
||||
? Theme.of(context).colorScheme.background
|
||||
: null,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
model.translatedTabname(t),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 14,
|
||||
color: model.currentTab == t
|
||||
? MyTheme.tabbar(context).selectedTextColor
|
||||
: MyTheme.tabbar(context).unSelectedTextColor
|
||||
?..withOpacity(0.5)),
|
||||
),
|
||||
)),
|
||||
onTap: () async {
|
||||
await handleTabSelection(t);
|
||||
await bind.setLocalFlutterConfig(
|
||||
k: 'peer-tab-index', v: t.toString());
|
||||
},
|
||||
children: model.indexs.map((t) {
|
||||
return InkWell(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: model.currentTab == t
|
||||
? Theme.of(context).colorScheme.background
|
||||
: null,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.center,
|
||||
child: getTabChild(t),
|
||||
)),
|
||||
onTap: () async {
|
||||
await handleTabSelection(t);
|
||||
await bind.setLocalFlutterConfig(
|
||||
k: 'peer-tab-index', v: t.toString());
|
||||
},
|
||||
);
|
||||
}).toList());
|
||||
}
|
||||
|
||||
Widget buildScrollJumper() {
|
||||
final model = Provider.of<PeerTabModel>(context);
|
||||
return Offstage(
|
||||
offstage: !model.showScrollBtn,
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
child: Icon(Icons.arrow_left,
|
||||
size: 22,
|
||||
color: model.leftFullyVisible
|
||||
? Theme.of(context).disabledColor
|
||||
: null),
|
||||
onTap: model.sc.backward),
|
||||
GestureDetector(
|
||||
child: Icon(Icons.arrow_right,
|
||||
size: 22,
|
||||
color: model.rightFullyVisible
|
||||
? Theme.of(context).disabledColor
|
||||
: null),
|
||||
onTap: model.sc.forward)
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
Widget _createPeersView() {
|
||||
final model = Provider.of<PeerTabModel>(context);
|
||||
Widget child;
|
||||
if (model.visibleOrderedTabs.isEmpty) {
|
||||
child = visibleContextMenuListener(Center(
|
||||
if (model.indexs.isEmpty) {
|
||||
child = Center(
|
||||
child: Text(translate('Right click to select tabs')),
|
||||
));
|
||||
);
|
||||
} else {
|
||||
if (model.visibleOrderedTabs.contains(model.currentTab)) {
|
||||
if (model.indexs.contains(model.currentTab)) {
|
||||
child = entries[model.currentTab].widget;
|
||||
} else {
|
||||
Future.delayed(Duration.zero, () {
|
||||
model.setCurrentTab(model.visibleOrderedTabs[0]);
|
||||
model.setCurrentTab(model.indexs[0]);
|
||||
});
|
||||
child = entries[0].widget;
|
||||
}
|
||||
@ -300,57 +226,6 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget visibleContextMenuListener(Widget child) {
|
||||
return Listener(
|
||||
onPointerDown: (e) {
|
||||
if (e.kind != ui.PointerDeviceKind.mouse) {
|
||||
return;
|
||||
}
|
||||
if (e.buttons == 2) {
|
||||
showRightMenu(
|
||||
(CancelFunc cancelFunc) {
|
||||
return visibleContextMenu(cancelFunc);
|
||||
},
|
||||
target: e.position,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: child);
|
||||
}
|
||||
|
||||
Widget visibleContextMenu(CancelFunc cancelFunc) {
|
||||
final model = Provider.of<PeerTabModel>(context);
|
||||
final List<MenuEntryBase> menu = List.empty(growable: true);
|
||||
final List<int> menuIndex = List.empty(growable: true);
|
||||
var list = model.orderedNotFilteredTabs();
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
int tabIndex = list[i];
|
||||
int bitMask = 1 << tabIndex;
|
||||
menuIndex.add(tabIndex);
|
||||
menu.add(MenuEntrySwitch(
|
||||
switchType: SwitchType.scheckbox,
|
||||
text: model.translatedTabname(tabIndex),
|
||||
getter: () async {
|
||||
return model.tabHiddenFlag & bitMask == 0;
|
||||
},
|
||||
setter: (show) async {
|
||||
model.onHideShow(tabIndex, show);
|
||||
cancelFunc();
|
||||
}));
|
||||
}
|
||||
return mod_menu.PopupMenu(
|
||||
items: menu
|
||||
.map((entry) => entry.build(
|
||||
context,
|
||||
const MenuConfig(
|
||||
commonColor: MyTheme.accent,
|
||||
height: 20.0,
|
||||
dividerHeight: 12.0,
|
||||
)))
|
||||
.expand((i) => i)
|
||||
.toList());
|
||||
}
|
||||
}
|
||||
|
||||
class PeerSearchBar extends StatefulWidget {
|
||||
|
@ -8,6 +8,7 @@ import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
||||
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
|
||||
import 'package:flutter_hbb/models/group_model.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:flutter_hbb/models/server_model.dart';
|
||||
import 'package:flutter_hbb/plugin/manager.dart';
|
||||
|
@ -11,23 +11,17 @@ import 'package:http/http.dart' as http;
|
||||
class GroupModel {
|
||||
final RxBool groupLoading = false.obs;
|
||||
final RxString groupLoadError = "".obs;
|
||||
final RxBool peerLoading = false.obs; //to-do: not used
|
||||
final RxString peerLoadError = "".obs;
|
||||
final RxString groupName = ''.obs;
|
||||
final RxString groupId = ''.obs;
|
||||
RxString groupName = ''.obs;
|
||||
final RxList<UserPayload> users = RxList.empty(growable: true);
|
||||
final List<Peer> peersShow = List.empty(growable: true);
|
||||
final RxList<Peer> peersShow = RxList.empty(growable: true);
|
||||
final RxString selectedUser = ''.obs;
|
||||
final RxString searchUserText = ''.obs;
|
||||
WeakReference<FFI> parent;
|
||||
|
||||
GroupModel(this.parent);
|
||||
|
||||
Future<void> reset() async {
|
||||
groupLoading.value = false;
|
||||
groupLoadError.value = "";
|
||||
peerLoading.value = false;
|
||||
peerLoadError.value = "";
|
||||
reset() {
|
||||
groupName.value = '';
|
||||
groupId.value = '';
|
||||
users.clear();
|
||||
@ -35,7 +29,14 @@ class GroupModel {
|
||||
}
|
||||
|
||||
Future<void> pull() async {
|
||||
await reset();
|
||||
groupLoading.value = true;
|
||||
groupLoadError.value = "";
|
||||
await _pull();
|
||||
groupLoading.value = false;
|
||||
}
|
||||
|
||||
Future<void> _pull() async {
|
||||
reset();
|
||||
if (bind.mainGetLocalOption(key: 'access_token') == '') {
|
||||
return;
|
||||
}
|
||||
@ -49,13 +50,6 @@ class GroupModel {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
if (gFFI.userModel.userName.isEmpty ||
|
||||
(gFFI.userModel.isAdmin.isFalse && groupName.isEmpty)) {
|
||||
gFFI.peerTabModel.check_dynamic_tabs();
|
||||
return;
|
||||
}
|
||||
groupLoading.value = true;
|
||||
groupLoadError.value = "";
|
||||
final api = "${await bind.mainGetApiServer()}/api/users";
|
||||
try {
|
||||
var uri0 = Uri.parse(api);
|
||||
@ -101,8 +95,6 @@ class GroupModel {
|
||||
debugPrint('$err');
|
||||
groupLoadError.value = err.toString();
|
||||
} finally {
|
||||
groupLoading.value = false;
|
||||
gFFI.peerTabModel.check_dynamic_tabs();
|
||||
_pullUserPeers();
|
||||
}
|
||||
}
|
||||
@ -130,6 +122,7 @@ class GroupModel {
|
||||
return groupId.value.isNotEmpty && groupName.isNotEmpty;
|
||||
} catch (e) {
|
||||
debugPrint('$e');
|
||||
groupLoadError.value = e.toString();
|
||||
} finally {}
|
||||
|
||||
return false;
|
||||
@ -137,9 +130,6 @@ class GroupModel {
|
||||
|
||||
Future<void> _pullUserPeers() async {
|
||||
peersShow.clear();
|
||||
peerLoading.value = true;
|
||||
peerLoadError.value = "";
|
||||
List<PeerPayload> peerPayloads = List.empty(growable: true);
|
||||
final api = "${await bind.mainGetApiServer()}/api/peers";
|
||||
try {
|
||||
var uri0 = Uri.parse(api);
|
||||
@ -173,9 +163,11 @@ class GroupModel {
|
||||
final data = json['data'];
|
||||
if (data is List) {
|
||||
for (final p in data) {
|
||||
final peer = PeerPayload.fromJson(p);
|
||||
peerPayloads.add(peer);
|
||||
peersShow.add(PeerPayload.toPeer(peer));
|
||||
final peerPayload = PeerPayload.fromJson(p);
|
||||
final peer = PeerPayload.toPeer(peerPayload);
|
||||
if (!peersShow.any((e) => e.id == peer.id)) {
|
||||
peersShow.add(peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -185,9 +177,7 @@ class GroupModel {
|
||||
} while (current * pageSize < total);
|
||||
} catch (err) {
|
||||
debugPrint('$err');
|
||||
peerLoadError.value = err.toString();
|
||||
} finally {
|
||||
peerLoading.value = false;
|
||||
}
|
||||
groupLoadError.value = err.toString();
|
||||
} finally {}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,5 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:scroll_pos/scroll_pos.dart';
|
||||
|
||||
import '../common.dart';
|
||||
import 'model.dart';
|
||||
@ -15,20 +11,6 @@ class PeerTabModel with ChangeNotifier {
|
||||
WeakReference<FFI> parent;
|
||||
int get currentTab => _currentTab;
|
||||
int _currentTab = 0; // index in tabNames
|
||||
List<int> get visibleOrderedTabs => _visibleOrderedTabs;
|
||||
List<int> _visibleOrderedTabs = List.empty(growable: true);
|
||||
List<int> get tabOrder => _tabOrder;
|
||||
List<int> _tabOrder = List.from([0, 1, 2, 3, 4]); // constant length
|
||||
int get tabHiddenFlag => _tabHiddenFlag;
|
||||
int _tabHiddenFlag = 0;
|
||||
bool get showScrollBtn => _showScrollBtn;
|
||||
bool _showScrollBtn = false;
|
||||
final List<bool> _fullyVisible = List.filled(5, false);
|
||||
bool get leftFullyVisible => _leftFullyVisible;
|
||||
bool _leftFullyVisible = false;
|
||||
bool get rightFullyVisible => _rightFullyVisible;
|
||||
bool _rightFullyVisible = false;
|
||||
ScrollPosController sc = ScrollPosController();
|
||||
List<String> tabNames = [
|
||||
'Recent Sessions',
|
||||
'Favorites',
|
||||
@ -36,66 +18,22 @@ class PeerTabModel with ChangeNotifier {
|
||||
'Address Book',
|
||||
defaultGroupTabname,
|
||||
];
|
||||
final List<IconData> icons = [
|
||||
Icons.access_time,
|
||||
Icons.star,
|
||||
Icons.explore,
|
||||
IconFont.addressBook,
|
||||
Icons.group,
|
||||
];
|
||||
List<int> get indexs => List.generate(tabNames.length, (index) => index);
|
||||
|
||||
PeerTabModel(this.parent) {
|
||||
// init tabHiddenFlag
|
||||
_tabHiddenFlag = int.tryParse(
|
||||
bind.getLocalFlutterConfig(k: 'hidden-peer-card'),
|
||||
radix: 2) ??
|
||||
0;
|
||||
var tabs = _notHiddenTabs();
|
||||
// remove dynamic tabs
|
||||
tabs.remove(groupTabIndex);
|
||||
// init tabOrder
|
||||
try {
|
||||
final conf = bind.getLocalFlutterConfig(k: 'peer-tab-order');
|
||||
if (conf.isNotEmpty) {
|
||||
final json = jsonDecode(conf);
|
||||
if (json is List) {
|
||||
final List<int> list =
|
||||
json.map((e) => int.tryParse(e.toString()) ?? -1).toList();
|
||||
if (list.length == _tabOrder.length &&
|
||||
_tabOrder.every((e) => list.contains(e))) {
|
||||
_tabOrder = list;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrintStack(label: '$e');
|
||||
}
|
||||
// init visibleOrderedTabs
|
||||
var tempList = _tabOrder.toList();
|
||||
tempList.removeWhere((e) => !tabs.contains(e));
|
||||
_visibleOrderedTabs = tempList;
|
||||
// init currentTab
|
||||
_currentTab =
|
||||
int.tryParse(bind.getLocalFlutterConfig(k: 'peer-tab-index')) ?? 0;
|
||||
if (!tabs.contains(_currentTab)) {
|
||||
if (tabs.isNotEmpty) {
|
||||
_currentTab = tabs[0];
|
||||
} else {
|
||||
_currentTab = 0;
|
||||
}
|
||||
if (_currentTab < 0 || _currentTab >= tabNames.length) {
|
||||
_currentTab = 0;
|
||||
}
|
||||
sc.itemCount = _visibleOrderedTabs.length;
|
||||
}
|
||||
|
||||
check_dynamic_tabs() {
|
||||
var visible = visibleTabs();
|
||||
_visibleOrderedTabs = _tabOrder.where((e) => visible.contains(e)).toList();
|
||||
if (_visibleOrderedTabs.contains(groupTabIndex) &&
|
||||
int.tryParse(bind.getLocalFlutterConfig(k: 'peer-tab-index')) ==
|
||||
groupTabIndex) {
|
||||
_currentTab = groupTabIndex;
|
||||
}
|
||||
if (gFFI.userModel.isAdmin.isFalse &&
|
||||
gFFI.groupModel.groupName.isNotEmpty) {
|
||||
tabNames[groupTabIndex] = gFFI.groupModel.groupName.value;
|
||||
} else {
|
||||
tabNames[groupTabIndex] = defaultGroupTabname;
|
||||
}
|
||||
sc.itemCount = _visibleOrderedTabs.length;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
setCurrentTab(int index) {
|
||||
@ -105,172 +43,27 @@ class PeerTabModel with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
setTabFullyVisible(int index, bool visible) {
|
||||
if (index >= 0 && index < _fullyVisible.length) {
|
||||
if (visible != _fullyVisible[index]) {
|
||||
_fullyVisible[index] = visible;
|
||||
bool changed = false;
|
||||
bool show = _visibleOrderedTabs.any((e) => !_fullyVisible[e]);
|
||||
if (show != _showScrollBtn) {
|
||||
_showScrollBtn = show;
|
||||
changed = true;
|
||||
}
|
||||
if (_visibleOrderedTabs.isNotEmpty && _visibleOrderedTabs[0] == index) {
|
||||
if (_leftFullyVisible != visible) {
|
||||
_leftFullyVisible = visible;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (_visibleOrderedTabs.isNotEmpty &&
|
||||
_visibleOrderedTabs.last == index) {
|
||||
if (_rightFullyVisible != visible) {
|
||||
_rightFullyVisible = visible;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onReorder(oldIndex, newIndex) {
|
||||
if (oldIndex < newIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
var list = _visibleOrderedTabs.toList();
|
||||
final int item = list.removeAt(oldIndex);
|
||||
list.insert(newIndex, item);
|
||||
_visibleOrderedTabs = list;
|
||||
|
||||
var tmpTabOrder = _visibleOrderedTabs.toList();
|
||||
var left = _tabOrder.where((e) => !tmpTabOrder.contains(e)).toList();
|
||||
for (var t in left) {
|
||||
_addTabInOrder(tmpTabOrder, t);
|
||||
}
|
||||
_tabOrder = tmpTabOrder;
|
||||
bind.setLocalFlutterConfig(k: 'peer-tab-order', v: jsonEncode(tmpTabOrder));
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
onHideShow(int index, bool show) async {
|
||||
int bitMask = 1 << index;
|
||||
if (show) {
|
||||
_tabHiddenFlag &= ~bitMask;
|
||||
} else {
|
||||
_tabHiddenFlag |= bitMask;
|
||||
}
|
||||
await bind.setLocalFlutterConfig(
|
||||
k: 'hidden-peer-card', v: _tabHiddenFlag.toRadixString(2));
|
||||
var visible = visibleTabs();
|
||||
_visibleOrderedTabs = _tabOrder.where((e) => visible.contains(e)).toList();
|
||||
if (_visibleOrderedTabs.isNotEmpty &&
|
||||
!_visibleOrderedTabs.contains(_currentTab)) {
|
||||
_currentTab = _visibleOrderedTabs[0];
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<int> orderedNotFilteredTabs() {
|
||||
var list = tabOrder.toList();
|
||||
if (_filterGroupCard()) {
|
||||
list.remove(groupTabIndex);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// return index array of tabNames
|
||||
List<int> visibleTabs() {
|
||||
var v = List<int>.empty(growable: true);
|
||||
for (int i = 0; i < tabNames.length; i++) {
|
||||
if (!_isTabHidden(i) && !_isTabFilter(i)) {
|
||||
v.add(i);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
String translatedTabname(int index) {
|
||||
String tabTooltip(int index, String groupName) {
|
||||
if (index >= 0 && index < tabNames.length) {
|
||||
final name = tabNames[index];
|
||||
if (index == groupTabIndex) {
|
||||
if (name == defaultGroupTabname) {
|
||||
return translate(name);
|
||||
if (gFFI.userModel.isAdmin.value || groupName.isEmpty) {
|
||||
return translate(defaultGroupTabname);
|
||||
} else {
|
||||
return name;
|
||||
return '${translate('Group')}: $groupName';
|
||||
}
|
||||
} else {
|
||||
return translate(name);
|
||||
return translate(tabNames[index]);
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
return index.toString();
|
||||
}
|
||||
|
||||
bool _isTabHidden(int tabindex) {
|
||||
return _tabHiddenFlag & (1 << tabindex) != 0;
|
||||
}
|
||||
|
||||
bool _isTabFilter(int tabIndex) {
|
||||
if (tabIndex == groupTabIndex) {
|
||||
return _filterGroupCard();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if hide group card
|
||||
bool _filterGroupCard() {
|
||||
if (gFFI.groupModel.users.isEmpty ||
|
||||
(gFFI.userModel.isAdmin.isFalse && gFFI.groupModel.groupName.isEmpty)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
List<int> _notHiddenTabs() {
|
||||
var v = List<int>.empty(growable: true);
|
||||
for (int i = 0; i < tabNames.length; i++) {
|
||||
if (!_isTabHidden(i)) {
|
||||
v.add(i);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// add tabIndex to list
|
||||
_addTabInOrder(List<int> list, int tabIndex) {
|
||||
if (!_tabOrder.contains(tabIndex) || list.contains(tabIndex)) {
|
||||
return;
|
||||
}
|
||||
bool sameOrder = true;
|
||||
int lastIndex = -1;
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
var index = _tabOrder.lastIndexOf(list[i]);
|
||||
if (index > lastIndex) {
|
||||
lastIndex = index;
|
||||
continue;
|
||||
} else {
|
||||
sameOrder = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sameOrder) {
|
||||
var indexInTabOrder = _tabOrder.indexOf(tabIndex);
|
||||
var left = List.empty(growable: true);
|
||||
for (int i = 0; i < indexInTabOrder; i++) {
|
||||
left.add(_tabOrder[i]);
|
||||
}
|
||||
int insertIndex = list.lastIndexWhere((e) => left.contains(e));
|
||||
if (insertIndex < 0) {
|
||||
insertIndex = 0;
|
||||
} else {
|
||||
insertIndex += 1;
|
||||
}
|
||||
list.insert(insertIndex, tabIndex);
|
||||
} else {
|
||||
list.add(tabIndex);
|
||||
IconData tabIcon(int index) {
|
||||
if (index >= 0 && index < tabNames.length) {
|
||||
return icons[index];
|
||||
}
|
||||
assert(false);
|
||||
return Icons.help;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common/hbbs/hbbs.dart';
|
||||
import 'package:flutter_hbb/common/widgets/peer_tab_page.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
@ -79,7 +77,6 @@ class UserModel {
|
||||
await gFFI.abModel.reset();
|
||||
await gFFI.groupModel.reset();
|
||||
userName.value = '';
|
||||
gFFI.peerTabModel.check_dynamic_tabs();
|
||||
}
|
||||
|
||||
_parseAndUpdateUser(UserPayload user) {
|
||||
@ -89,8 +86,7 @@ class UserModel {
|
||||
|
||||
// update ab and group status
|
||||
static Future<void> updateOtherModels() async {
|
||||
await gFFI.abModel.pullAb();
|
||||
await gFFI.groupModel.pull();
|
||||
await Future.wait([gFFI.abModel.pullAb(), gFFI.groupModel.pull()]);
|
||||
}
|
||||
|
||||
Future<void> logOut() async {
|
||||
|
@ -145,6 +145,9 @@ flutter:
|
||||
- family: PeerSearchbar
|
||||
fonts:
|
||||
- asset: assets/peer_searchbar.ttf
|
||||
- family: AddressBook
|
||||
fonts:
|
||||
- asset: assets/address_book.ttf
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||
|
Loading…
Reference in New Issue
Block a user