diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index da7e37e6b..d0ab51058 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -12,6 +12,7 @@ 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/file_model.dart'; 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'; @@ -39,6 +40,8 @@ EdgeInsets? _menuPadding() { class _PeerTabPageState extends State with SingleTickerProviderStateMixin { + bool _hideSort = bind.getLocalFlutterConfig(k: 'peer-tab-index') == '0'; + final List<_TabEntry> entries = [ _TabEntry( RecentPeersView( @@ -83,6 +86,7 @@ class _PeerTabPageState extends State if (tabIndex < entries.length) { gFFI.peerTabModel.setCurrentTab(tabIndex); entries[tabIndex].load(); + _hideSort = tabIndex == 0; } } @@ -95,22 +99,27 @@ class _PeerTabPageState extends State SizedBox( height: 28, child: Container( - padding: isDesktop ? null : EdgeInsets.symmetric(horizontal: 2), - constraints: isDesktop ? null : kMobilePageConstraints, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: visibleContextMenuListener( - _createSwitchBar(context))), - buildScrollJumper(), - const PeerSearchBar(), - Offstage( - offstage: !isDesktop, - child: _createPeerViewTypeSwitch(context) - .marginOnly(left: 13)), - ], - )), + padding: isDesktop ? null : EdgeInsets.symmetric(horizontal: 2), + constraints: isDesktop ? null : kMobilePageConstraints, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: + visibleContextMenuListener(_createSwitchBar(context))), + buildScrollJumper(), + const PeerSearchBar(), + Offstage( + offstage: !isDesktop, + child: _createPeerViewTypeSwitch(context) + .marginOnly(left: 13)), + Offstage( + offstage: _hideSort, + child: PeerSortDropdown(), + ) + ], + ), + ), ), _createPeersView(), ], @@ -417,3 +426,48 @@ class _PeerSearchBarState extends State { ); } } + +class PeerSortDropdown extends StatefulWidget { + const PeerSortDropdown({super.key}); + + @override + State createState() => _PeerSortDropdownState(); +} + +class _PeerSortDropdownState extends State { + final List sort_names = ['id', 'username', "status"]; + String _sortType = peerSort.value; + + @override + Widget build(BuildContext context) { + return DropdownButton( + value: _sortType, + elevation: 16, + underline: SizedBox(), + onChanged: (v) { + if (v != null) { + setState(() { + _sortType = v; + bind.setLocalFlutterConfig( + k: "peer-sorting", + v: _sortType, + ); + }); + peerSort.value = _sortType; + } + }, + dropdownColor: Theme.of(context).cardColor, + items: sort_names + .map>( + (String value) => DropdownMenuItem( + value: value, + child: Text( + value, + overflow: TextOverflow.ellipsis, + ), + ), + ) + .toList(), + ); + } +} diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart index 9c98f24b8..88e05238e 100644 --- a/flutter/lib/common/widgets/peers_view.dart +++ b/flutter/lib/common/widgets/peers_view.dart @@ -18,6 +18,13 @@ typedef PeerCardBuilder = Widget Function(Peer peer); /// for peer search text, global obs value final peerSearchText = "".obs; + +/// for peer sort, global obs value +final peerSort = bind.getLocalFlutterConfig(k: 'peer-sorting').obs; + +// list for listener +final obslist = [peerSearchText, peerSort].obs; + final peerSearchTextController = TextEditingController(text: peerSearchText.value); @@ -101,7 +108,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener { } Widget _buildPeersView(Peers peers) { - final body = ObxValue((searchText) { + final body = ObxValue((filters) { return FutureBuilder>( builder: (context, snapshot) { if (snapshot.hasData) { @@ -139,9 +146,9 @@ class _PeersViewState extends State<_PeersView> with WindowListener { ); } }, - future: matchPeers(searchText.value, peers.peers), + future: matchPeers(filters[0].value, filters[1].value, peers.peers), ); - }, peerSearchText); + }, obslist); return body; } @@ -179,11 +186,36 @@ class _PeersViewState extends State<_PeersView> with WindowListener { }(); } - Future>? matchPeers(String searchText, List peers) async { + Future>? matchPeers( + String searchText, String sortedBy, List peers) async { if (widget.peerFilter != null) { peers = peers.where((peer) => widget.peerFilter!(peer)).toList(); } + // fallback to id sorting + if (sortedBy.isEmpty) { + sortedBy = 'id'; + bind.setLocalFlutterConfig( + k: "peer-sorting", + v: sortedBy, + ); + } + + if (widget.peers.loadEvent != 'load_recent_peers') { + switch (sortedBy) { + case 'id': + peers.sort((p1, p2) => p1.id.compareTo(p2.id)); + break; + case 'username': + peers.sort((p1, p2) => + p1.username.toLowerCase().compareTo(p2.username.toLowerCase())); + break; + case 'status': + peers.sort((p1, p2) => p1.online ? 1 : -1); + break; + } + } + searchText = searchText.trim(); if (searchText.isEmpty) { return peers; @@ -197,6 +229,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener { filteredList.add(peers[i]); } } + return filteredList; } }