From 490b1164276db271d0880f89ebe971f496459ba7 Mon Sep 17 00:00:00 2001 From: problematicconsumer Date: Wed, 6 Sep 2023 17:14:24 +0330 Subject: [PATCH] Add proxies sort --- assets/translations/strings.i18n.json | 7 +- assets/translations/strings_fa.i18n.json | 7 +- lib/domain/singbox/proxy_type.dart | 2 + .../proxies/notifier/proxies_notifier.dart | 65 +++++++++++++++++-- lib/features/proxies/view/proxies_page.dart | 23 ++++++- 5 files changed, 96 insertions(+), 8 deletions(-) diff --git a/assets/translations/strings.i18n.json b/assets/translations/strings.i18n.json index 306ec733..054962f1 100644 --- a/assets/translations/strings.i18n.json +++ b/assets/translations/strings.i18n.json @@ -79,7 +79,12 @@ "pageTitle": "proxies", "emptyProxiesMsg": "no proxies", "delayTestTooltip": "delay test", - "cancelTestButtonText": "cancel test" + "sortTooltip": "sort proxies", + "sortOptions": { + "unsorted": "Default", + "name": "by Name", + "delay": "by Delay" + } }, "logs": { "pageTitle": "logs", diff --git a/assets/translations/strings_fa.i18n.json b/assets/translations/strings_fa.i18n.json index bcd050a2..3ece366a 100644 --- a/assets/translations/strings_fa.i18n.json +++ b/assets/translations/strings_fa.i18n.json @@ -79,7 +79,12 @@ "pageTitle": "پراکسی‌ها", "emptyProxiesMsg": "پراکسی وجود ندارد", "delayTestTooltip": "تست تاخیر", - "cancelTestButtonText": "لغو تست" + "sortTooltip": "مرتب‌سازی پراکسی‌ها", + "sortOptions": { + "unsorted": "پیش‌فرض", + "name": "براساس نام", + "delay": "براساس تاخیر" + } }, "logs": { "pageTitle": "لاگ‌ها", diff --git a/lib/domain/singbox/proxy_type.dart b/lib/domain/singbox/proxy_type.dart index dbc46401..ef4331fc 100644 --- a/lib/domain/singbox/proxy_type.dart +++ b/lib/domain/singbox/proxy_type.dart @@ -28,4 +28,6 @@ enum ProxyType { String get key => name; static List groupValues = [selector, urltest]; + + bool get isGroup => ProxyType.groupValues.contains(this); } diff --git a/lib/features/proxies/notifier/proxies_notifier.dart b/lib/features/proxies/notifier/proxies_notifier.dart index 5dead08f..5613c8e6 100644 --- a/lib/features/proxies/notifier/proxies_notifier.dart +++ b/lib/features/proxies/notifier/proxies_notifier.dart @@ -1,31 +1,86 @@ import 'dart:async'; +import 'package:combine/combine.dart'; +import 'package:dartx/dartx.dart'; +import 'package:hiddify/core/prefs/prefs.dart'; import 'package:hiddify/data/data_providers.dart'; import 'package:hiddify/domain/core_service_failure.dart'; import 'package:hiddify/domain/singbox/singbox.dart'; import 'package:hiddify/features/common/connectivity/connectivity_controller.dart'; +import 'package:hiddify/utils/pref_notifier.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'proxies_notifier.g.dart'; -@Riverpod(keepAlive: true) +enum ProxiesSort { + unsorted, + name, + delay; + + String present(TranslationsEn t) => switch (this) { + ProxiesSort.unsorted => t.proxies.sortOptions.unsorted, + ProxiesSort.name => t.proxies.sortOptions.name, + ProxiesSort.delay => t.proxies.sortOptions.delay, + }; +} + +final proxiesSortProvider = AlwaysAlivePrefNotifier.provider( + "proxies_sort_mode", + ProxiesSort.unsorted, + mapFrom: ProxiesSort.values.byName, + mapTo: (value) => value.name, +); + +@riverpod class ProxiesNotifier extends _$ProxiesNotifier with AppLogger { @override Stream> build() async* { - loggy.debug("building"); final serviceRunning = await ref.watch(serviceRunningProvider.future); if (!serviceRunning) { throw const CoreServiceNotRunning(); } - yield* ref.watch(coreFacadeProvider).watchOutbounds().map( + final sortBy = ref.watch(proxiesSortProvider); + yield* ref + .watch(coreFacadeProvider) + .watchOutbounds() + .map( (event) => event.getOrElse( (f) { - loggy.warning("error receiving proxies", f); + loggy.warning("error receiving proxies: $f"); throw f; }, ), - ); + ) + .asyncMap((proxies) async => _sortOutbounds(proxies, sortBy)); + } + + Future> _sortOutbounds( + List outbounds, + ProxiesSort sortBy, + ) async { + return CombineWorker().execute( + () { + final sortedOutbounds = []; + for (final group in outbounds) { + final items = switch (sortBy) { + ProxiesSort.name => group.items.sortedBy((e) => e.tag), + ProxiesSort.delay => group.items.sortedWith((a, b) { + final ai = a.urlTestDelay; + final bi = b.urlTestDelay; + if (ai == 0 && bi == 0) return -1; + if (ai == 0 && bi > 0) return 1; + if (ai > 0 && bi == 0) return -1; + if (ai == bi && a.type.isGroup) return -1; + return ai.compareTo(bi); + }), + ProxiesSort.unsorted => group.items, + }; + sortedOutbounds.add(group.copyWith(items: items)); + } + return sortedOutbounds; + }, + ); } Future changeProxy(String groupTag, String outboundTag) async { diff --git a/lib/features/proxies/view/proxies_page.dart b/lib/features/proxies/view/proxies_page.dart index a3095d24..c8abc68c 100644 --- a/lib/features/proxies/view/proxies_page.dart +++ b/lib/features/proxies/view/proxies_page.dart @@ -17,6 +17,7 @@ class ProxiesPage extends HookConsumerWidget with PresLogger { final asyncProxies = ref.watch(proxiesNotifierProvider); final notifier = ref.watch(proxiesNotifierProvider.notifier); + final sortBy = ref.watch(proxiesSortProvider); final selectActiveProxyMutation = useMutation( initialOnFailure: (error) => @@ -50,7 +51,27 @@ class ProxiesPage extends HookConsumerWidget with PresLogger { return Scaffold( body: CustomScrollView( slivers: [ - NestedTabAppBar(title: Text(t.proxies.pageTitle.titleCase)), + NestedTabAppBar( + title: Text(t.proxies.pageTitle.titleCase), + actions: [ + PopupMenuButton( + initialValue: sortBy, + onSelected: ref.read(proxiesSortProvider.notifier).update, + icon: const Icon(Icons.sort), + tooltip: t.proxies.sortTooltip, + itemBuilder: (context) { + return [ + ...ProxiesSort.values.map( + (e) => PopupMenuItem( + value: e, + child: Text(e.present(t)), + ), + ), + ]; + }, + ), + ], + ), SliverLayoutBuilder( builder: (context, constraints) { final width = constraints.crossAxisExtent;