2023-09-14 16:30:45 +08:00
|
|
|
import 'dart:convert';
|
2023-08-04 13:11:24 +08:00
|
|
|
import 'dart:math';
|
|
|
|
|
2023-02-03 15:07:45 +08:00
|
|
|
import 'package:flutter/material.dart';
|
2024-05-18 23:13:54 +08:00
|
|
|
import 'package:flutter_hbb/consts.dart';
|
2023-08-03 16:48:14 +08:00
|
|
|
import 'package:flutter_hbb/models/peer_model.dart';
|
2023-02-03 15:07:45 +08:00
|
|
|
import 'package:flutter_hbb/models/platform_model.dart';
|
2023-08-03 16:48:14 +08:00
|
|
|
import 'package:get/get.dart';
|
2023-02-03 15:07:45 +08:00
|
|
|
|
|
|
|
import '../common.dart';
|
|
|
|
import 'model.dart';
|
|
|
|
|
2023-08-03 16:48:14 +08:00
|
|
|
enum PeerTabIndex {
|
|
|
|
recent,
|
|
|
|
fav,
|
|
|
|
lan,
|
|
|
|
ab,
|
|
|
|
group,
|
|
|
|
}
|
|
|
|
|
2023-02-03 15:07:45 +08:00
|
|
|
class PeerTabModel with ChangeNotifier {
|
|
|
|
WeakReference<FFI> parent;
|
|
|
|
int get currentTab => _currentTab;
|
|
|
|
int _currentTab = 0; // index in tabNames
|
2024-04-06 17:53:03 +08:00
|
|
|
static const int maxTabCount = 5;
|
|
|
|
static const List<String> tabNames = [
|
2023-11-06 20:12:01 +08:00
|
|
|
'Recent sessions',
|
2023-02-03 15:07:45 +08:00
|
|
|
'Favorites',
|
2024-04-06 17:53:03 +08:00
|
|
|
'Discovered',
|
|
|
|
'Address book',
|
|
|
|
'Group',
|
2023-02-03 15:07:45 +08:00
|
|
|
];
|
2024-04-06 17:53:03 +08:00
|
|
|
static const List<IconData> icons = [
|
2023-06-23 12:58:27 +08:00
|
|
|
Icons.access_time_filled,
|
2023-06-21 16:04:52 +08:00
|
|
|
Icons.star,
|
2024-04-06 17:53:03 +08:00
|
|
|
Icons.explore,
|
|
|
|
IconFont.addressBook,
|
|
|
|
Icons.group,
|
2023-06-21 16:04:52 +08:00
|
|
|
];
|
2024-04-06 17:53:03 +08:00
|
|
|
List<bool> isEnabled = List.from([
|
|
|
|
true,
|
|
|
|
true,
|
|
|
|
!isWeb,
|
|
|
|
!(bind.isDisableAb() || bind.isDisableAccount()),
|
2024-05-28 20:10:12 +08:00
|
|
|
!(bind.isDisableGroupPanel() || bind.isDisableAccount()),
|
2024-04-06 17:53:03 +08:00
|
|
|
]);
|
|
|
|
final List<bool> _isVisible = List.filled(maxTabCount, true, growable: false);
|
|
|
|
List<bool> get isVisibleEnabled => () {
|
|
|
|
final list = _isVisible.toList();
|
|
|
|
for (int i = 0; i < maxTabCount; i++) {
|
|
|
|
list[i] = list[i] && isEnabled[i];
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}();
|
|
|
|
final List<int> orders =
|
|
|
|
List.generate(maxTabCount, (index) => index, growable: false);
|
|
|
|
List<int> get visibleEnabledOrderedIndexs =>
|
|
|
|
orders.where((e) => isVisibleEnabled[e]).toList();
|
2023-08-03 16:48:14 +08:00
|
|
|
List<Peer> _selectedPeers = List.empty(growable: true);
|
|
|
|
List<Peer> get selectedPeers => _selectedPeers;
|
2023-08-09 22:00:15 +08:00
|
|
|
bool _multiSelectionMode = false;
|
|
|
|
bool get multiSelectionMode => _multiSelectionMode;
|
2023-08-03 16:48:14 +08:00
|
|
|
List<Peer> _currentTabCachedPeers = List.empty(growable: true);
|
|
|
|
List<Peer> get currentTabCachedPeers => _currentTabCachedPeers;
|
2023-08-09 22:00:15 +08:00
|
|
|
bool _isShiftDown = false;
|
|
|
|
bool get isShiftDown => _isShiftDown;
|
|
|
|
String _lastId = '';
|
|
|
|
String get lastId => _lastId;
|
2023-02-03 15:07:45 +08:00
|
|
|
|
|
|
|
PeerTabModel(this.parent) {
|
2023-09-14 16:30:45 +08:00
|
|
|
// visible
|
|
|
|
try {
|
2024-05-18 23:13:54 +08:00
|
|
|
final option = bind.getLocalFlutterOption(k: kOptionPeerTabVisible);
|
2023-09-14 16:30:45 +08:00
|
|
|
if (option.isNotEmpty) {
|
|
|
|
List<dynamic> decodeList = jsonDecode(option);
|
|
|
|
if (decodeList.length == _isVisible.length) {
|
|
|
|
for (int i = 0; i < _isVisible.length; i++) {
|
|
|
|
if (decodeList[i] is bool) {
|
|
|
|
_isVisible[i] = decodeList[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
debugPrint("failed to get peer tab visible list:$e");
|
|
|
|
}
|
2024-04-06 17:53:03 +08:00
|
|
|
// order
|
|
|
|
try {
|
2024-05-18 23:13:54 +08:00
|
|
|
final option = bind.getLocalFlutterOption(k: kOptionPeerTabOrder);
|
2024-04-06 17:53:03 +08:00
|
|
|
if (option.isNotEmpty) {
|
|
|
|
List<dynamic> decodeList = jsonDecode(option);
|
|
|
|
if (decodeList.length == maxTabCount) {
|
|
|
|
var sortedList = decodeList.toList();
|
|
|
|
sortedList.sort();
|
|
|
|
bool valid = true;
|
|
|
|
for (int i = 0; i < maxTabCount; i++) {
|
|
|
|
if (sortedList[i] is! int || sortedList[i] != i) {
|
|
|
|
valid = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (valid) {
|
|
|
|
for (int i = 0; i < orders.length; i++) {
|
|
|
|
orders[i] = decodeList[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
debugPrint("failed to get peer tab order list: $e");
|
|
|
|
}
|
2023-02-03 15:07:45 +08:00
|
|
|
// init currentTab
|
|
|
|
_currentTab =
|
2024-05-18 23:13:54 +08:00
|
|
|
int.tryParse(bind.getLocalFlutterOption(k: kOptionPeerTabIndex)) ?? 0;
|
2024-04-06 17:53:03 +08:00
|
|
|
if (_currentTab < 0 || _currentTab >= maxTabCount) {
|
2023-06-21 16:04:52 +08:00
|
|
|
_currentTab = 0;
|
2023-02-03 15:07:45 +08:00
|
|
|
}
|
2024-04-06 17:53:03 +08:00
|
|
|
_trySetCurrentTabToFirstVisibleEnabled();
|
2023-02-03 15:07:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
setCurrentTab(int index) {
|
|
|
|
if (_currentTab != index) {
|
|
|
|
_currentTab = index;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-14 10:17:03 +08:00
|
|
|
String tabTooltip(int index) {
|
2023-02-03 15:07:45 +08:00
|
|
|
if (index >= 0 && index < tabNames.length) {
|
2023-09-14 10:17:03 +08:00
|
|
|
return translate(tabNames[index]);
|
2023-02-03 15:07:45 +08:00
|
|
|
}
|
|
|
|
return index.toString();
|
|
|
|
}
|
|
|
|
|
2023-06-21 16:04:52 +08:00
|
|
|
IconData tabIcon(int index) {
|
2024-04-06 17:53:03 +08:00
|
|
|
if (index >= 0 && index < icons.length) {
|
2023-06-21 16:04:52 +08:00
|
|
|
return icons[index];
|
2023-02-03 15:07:45 +08:00
|
|
|
}
|
2023-06-21 16:04:52 +08:00
|
|
|
return Icons.help;
|
2023-02-03 15:07:45 +08:00
|
|
|
}
|
2023-08-03 16:48:14 +08:00
|
|
|
|
2023-08-09 22:00:15 +08:00
|
|
|
setMultiSelectionMode(bool mode) {
|
|
|
|
_multiSelectionMode = mode;
|
|
|
|
if (!mode) {
|
|
|
|
_selectedPeers.clear();
|
|
|
|
_lastId = '';
|
|
|
|
}
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
select(Peer peer) {
|
|
|
|
if (!_multiSelectionMode) {
|
|
|
|
// https://github.com/flutter/flutter/issues/101275#issuecomment-1604541700
|
|
|
|
// After onTap, the shift key should be pressed for a while when not in multiselection mode,
|
|
|
|
// because onTap is delayed when onDoubleTap is not null
|
|
|
|
if (isDesktop && !_isShiftDown) return;
|
|
|
|
_multiSelectionMode = true;
|
|
|
|
}
|
2023-08-04 13:11:24 +08:00
|
|
|
final cached = _currentTabCachedPeers.map((e) => e.id).toList();
|
|
|
|
int thisIndex = cached.indexOf(peer.id);
|
2023-08-09 22:00:15 +08:00
|
|
|
int lastIndex = cached.indexOf(_lastId);
|
|
|
|
if (_isShiftDown && thisIndex >= 0 && lastIndex >= 0) {
|
|
|
|
int start = min(thisIndex, lastIndex);
|
|
|
|
int end = max(thisIndex, lastIndex);
|
|
|
|
bool remove = isPeerSelected(peer.id);
|
2023-08-04 13:11:24 +08:00
|
|
|
for (var i = start; i <= end; i++) {
|
2023-08-09 22:00:15 +08:00
|
|
|
if (remove) {
|
|
|
|
if (isPeerSelected(cached[i])) {
|
|
|
|
_selectedPeers.removeWhere((p) => p.id == cached[i]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!isPeerSelected(cached[i])) {
|
|
|
|
_selectedPeers.add(_currentTabCachedPeers[i]);
|
|
|
|
}
|
2023-08-04 13:11:24 +08:00
|
|
|
}
|
|
|
|
}
|
2023-08-03 16:48:14 +08:00
|
|
|
} else {
|
2023-08-04 13:11:24 +08:00
|
|
|
if (isPeerSelected(peer.id)) {
|
|
|
|
_selectedPeers.removeWhere((p) => p.id == peer.id);
|
|
|
|
} else {
|
|
|
|
_selectedPeers.add(peer);
|
|
|
|
}
|
2023-08-03 16:48:14 +08:00
|
|
|
}
|
2023-08-09 22:00:15 +08:00
|
|
|
_lastId = peer.id;
|
2023-08-03 16:48:14 +08:00
|
|
|
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;
|
|
|
|
}
|
2023-08-09 22:00:15 +08:00
|
|
|
|
|
|
|
setShiftDown(bool v) {
|
|
|
|
if (_isShiftDown != v) {
|
|
|
|
_isShiftDown = v;
|
|
|
|
if (_multiSelectionMode) {
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-09-14 16:30:45 +08:00
|
|
|
|
|
|
|
setTabVisible(int index, bool visible) {
|
2024-04-06 17:53:03 +08:00
|
|
|
if (index >= 0 && index < maxTabCount) {
|
2023-09-14 16:30:45 +08:00
|
|
|
if (_isVisible[index] != visible) {
|
|
|
|
_isVisible[index] = visible;
|
|
|
|
if (index == _currentTab && !visible) {
|
2024-04-06 17:53:03 +08:00
|
|
|
_trySetCurrentTabToFirstVisibleEnabled();
|
|
|
|
} else if (visible && visibleEnabledOrderedIndexs.length == 1) {
|
2023-09-14 16:30:45 +08:00
|
|
|
_currentTab = index;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
bind.setLocalFlutterOption(
|
2024-05-18 23:13:54 +08:00
|
|
|
k: kOptionPeerTabVisible, v: jsonEncode(_isVisible));
|
2023-09-14 16:30:45 +08:00
|
|
|
} catch (_) {}
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-06 17:53:03 +08:00
|
|
|
_trySetCurrentTabToFirstVisibleEnabled() {
|
|
|
|
if (!visibleEnabledOrderedIndexs.contains(_currentTab)) {
|
|
|
|
if (visibleEnabledOrderedIndexs.isNotEmpty) {
|
|
|
|
_currentTab = visibleEnabledOrderedIndexs.first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
reorder(int oldIndex, int newIndex) {
|
|
|
|
if (oldIndex < newIndex) {
|
|
|
|
newIndex -= 1;
|
|
|
|
}
|
|
|
|
if (oldIndex < 0 || oldIndex >= visibleEnabledOrderedIndexs.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (newIndex < 0 || newIndex >= visibleEnabledOrderedIndexs.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final oldTabValue = visibleEnabledOrderedIndexs[oldIndex];
|
|
|
|
final newTabValue = visibleEnabledOrderedIndexs[newIndex];
|
|
|
|
int oldValueIndex = orders.indexOf(oldTabValue);
|
|
|
|
int newValueIndex = orders.indexOf(newTabValue);
|
|
|
|
final list = orders.toList();
|
|
|
|
if (oldIndex != -1 && newIndex != -1) {
|
|
|
|
list.removeAt(oldValueIndex);
|
|
|
|
list.insert(newValueIndex, oldTabValue);
|
|
|
|
for (int i = 0; i < list.length; i++) {
|
|
|
|
orders[i] = list[i];
|
2023-09-14 16:30:45 +08:00
|
|
|
}
|
2024-05-18 23:13:54 +08:00
|
|
|
bind.setLocalFlutterOption(k: kOptionPeerTabOrder, v: jsonEncode(orders));
|
2024-04-06 17:53:03 +08:00
|
|
|
notifyListeners();
|
2023-09-14 16:30:45 +08:00
|
|
|
}
|
|
|
|
}
|
2023-02-03 15:07:45 +08:00
|
|
|
}
|