Files
umbrix/lib/features/proxy/overview/proxies_overview_page.dart
2025-12-26 02:47:20 +03:00

193 lines
6.5 KiB
Dart

import 'package:fluentui_system_icons/fluentui_system_icons.dart';
import 'package:flutter/material.dart';
import 'package:hiddify/core/localization/translations.dart';
import 'package:hiddify/core/model/failures.dart';
import 'package:hiddify/features/common/nested_app_bar.dart';
import 'package:hiddify/features/proxy/overview/proxies_overview_notifier.dart';
import 'package:hiddify/features/proxy/widget/proxy_tile.dart';
import 'package:hiddify/utils/utils.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class ProxiesOverviewPage extends HookConsumerWidget with PresLogger {
const ProxiesOverviewPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final t = ref.watch(translationsProvider);
final asyncProxies = ref.watch(proxiesOverviewNotifierProvider);
final notifier = ref.watch(proxiesOverviewNotifierProvider.notifier);
final sortBy = ref.watch(proxiesSortNotifierProvider);
final selectActiveProxyMutation = useMutation(
initialOnFailure: (error) => CustomToast.error(t.presentShortError(error)).show(context),
);
final appBar = NestedAppBar(
title: Text(t.proxies.pageTitle),
actions: [
PopupMenuButton<ProxiesSort>(
initialValue: sortBy,
onSelected: ref.read(proxiesSortNotifierProvider.notifier).update,
icon: const Icon(FluentIcons.arrow_sort_24_regular),
tooltip: t.proxies.sortTooltip,
itemBuilder: (context) {
return [
...ProxiesSort.values.map(
(e) => PopupMenuItem(
value: e,
child: Text(e.present(t)),
),
),
];
},
),
],
);
switch (asyncProxies) {
case AsyncData(value: final groups):
if (groups.isEmpty) {
return Scaffold(
body: CustomScrollView(
slivers: [
appBar,
SliverFillRemaining(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(t.proxies.emptyProxiesMsg),
],
),
),
],
),
);
}
final group = groups.first;
return Scaffold(
body: CustomScrollView(
slivers: [
appBar,
SliverLayoutBuilder(
builder: (context, constraints) {
final width = constraints.crossAxisExtent;
if (!PlatformUtils.isDesktop && width < 648) {
return SliverPadding(
padding: const EdgeInsets.only(bottom: 86),
sliver: SliverList.builder(
itemBuilder: (_, index) {
final proxy = group.items[index];
return ProxyTile(
proxy,
selected: group.selected == proxy.tag,
onSelect: () async {
if (selectActiveProxyMutation.state.isInProgress) {
return;
}
selectActiveProxyMutation.setFuture(
notifier.changeProxy(group.tag, proxy.tag),
);
},
);
},
itemCount: group.items.length,
),
);
}
return SliverGrid.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: (width / 268).floor(),
mainAxisExtent: 68,
),
itemBuilder: (context, index) {
final proxy = group.items[index];
return ProxyTile(
proxy,
selected: group.selected == proxy.tag,
onSelect: () async {
if (selectActiveProxyMutation.state.isInProgress) {
return;
}
selectActiveProxyMutation.setFuture(
notifier.changeProxy(
group.tag,
proxy.tag,
),
);
},
);
},
itemCount: group.items.length,
);
},
),
],
),
floatingActionButton: Builder(
builder: (context) {
final theme = Theme.of(context);
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
theme.colorScheme.primary,
theme.colorScheme.primary.withOpacity(0.8),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: theme.colorScheme.primary.withOpacity(0.4),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: FloatingActionButton(
onPressed: () async => notifier.urlTest(group.tag),
tooltip: t.proxies.delayTestTooltip,
backgroundColor: Colors.transparent,
elevation: 0,
child: const Icon(FluentIcons.arrow_clockwise_24_filled),
),
);
},
),
);
case AsyncError(:final error):
return Scaffold(
body: CustomScrollView(
slivers: [
appBar,
SliverErrorBodyPlaceholder(
t.presentShortError(error),
icon: null,
),
],
),
);
case AsyncLoading():
return Scaffold(
body: CustomScrollView(
slivers: [
appBar,
const SliverLoadingBodyPlaceholder(),
],
),
);
// TODO: remove
default:
return const Scaffold();
}
}
}