- Changed window size to mobile phone format (400x800) - Removed width condition for ActiveProxyFooter - now always visible - Added run-umbrix.sh launch script with icon copying - Stats cards now display on all screen sizes
123 lines
3.9 KiB
Dart
123 lines
3.9 KiB
Dart
import 'package:flutter/foundation.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
import 'package:umbrix/core/preferences/general_preferences.dart';
|
||
import 'package:umbrix/core/router/routes.dart';
|
||
import 'package:umbrix/features/deep_link/notifier/deep_link_notifier.dart';
|
||
import 'package:umbrix/utils/utils.dart';
|
||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||
|
||
part 'app_router.g.dart';
|
||
|
||
bool _debugMobileRouter = false;
|
||
|
||
final useMobileRouter = !PlatformUtils.isDesktop || (kDebugMode && _debugMobileRouter);
|
||
final GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>();
|
||
|
||
// TODO: test and improve handling of deep link
|
||
@riverpod
|
||
GoRouter router(RouterRef ref) {
|
||
final notifier = ref.watch(routerListenableProvider.notifier);
|
||
final deepLink = ref.listen(
|
||
deepLinkNotifierProvider,
|
||
(_, next) async {
|
||
if (next case AsyncData(value: final link?)) {
|
||
await ref.state.push(AddProfileRoute(url: link.url).location);
|
||
}
|
||
},
|
||
);
|
||
final initialLink = deepLink.read();
|
||
String initialLocation = const HomeRoute().location;
|
||
if (initialLink case AsyncData(value: final link?)) {
|
||
initialLocation = AddProfileRoute(url: link.url).location;
|
||
}
|
||
|
||
return GoRouter(
|
||
navigatorKey: rootNavigatorKey,
|
||
initialLocation: initialLocation,
|
||
debugLogDiagnostics: true,
|
||
routes: [
|
||
if (useMobileRouter) $mobileWrapperRoute else $desktopWrapperRoute,
|
||
$introRoute,
|
||
],
|
||
refreshListenable: notifier,
|
||
redirect: notifier.redirect,
|
||
observers: [
|
||
SentryNavigatorObserver(),
|
||
],
|
||
);
|
||
}
|
||
|
||
final tabLocations = [
|
||
const HomeRoute().location, // 0: Главная
|
||
const ProxiesRoute().location, // 1: Локации
|
||
const PerAppProxyRoute().location, // 2: Исключения
|
||
const SettingsRoute().location, // 3: Настройки
|
||
const AboutRoute().location, // 4: О программе
|
||
];
|
||
|
||
int getCurrentIndex(BuildContext context) {
|
||
final String location = GoRouterState.of(context).uri.path;
|
||
|
||
// Проверяем точное совпадение для главной
|
||
if (location == const HomeRoute().location) return 0;
|
||
|
||
// Проверяем остальные маршруты по порядку
|
||
// ВАЖНО: более длинные пути проверяем раньше!
|
||
if (location.startsWith(const PerAppProxyRoute().location)) return 2; // /settings/per-app-proxy
|
||
if (location.startsWith(const ProxiesRoute().location)) return 1; // /proxies
|
||
if (location.startsWith(const SettingsRoute().location)) return 3; // /settings
|
||
if (location.startsWith(const AboutRoute().location)) return 4; // /about
|
||
|
||
return 0;
|
||
}
|
||
|
||
void switchTab(int index, BuildContext context) {
|
||
assert(index >= 0 && index < tabLocations.length);
|
||
final location = tabLocations[index];
|
||
return context.go(location);
|
||
}
|
||
|
||
@riverpod
|
||
class RouterListenable extends _$RouterListenable with AppLogger implements Listenable {
|
||
VoidCallback? _routerListener;
|
||
bool _introCompleted = false;
|
||
|
||
@override
|
||
Future<void> build() async {
|
||
_introCompleted = ref.watch(Preferences.introCompleted);
|
||
|
||
ref.listenSelf((_, __) {
|
||
if (state.isLoading) return;
|
||
loggy.debug("triggering listener");
|
||
_routerListener?.call();
|
||
});
|
||
}
|
||
|
||
// ignore: avoid_build_context_in_providers
|
||
String? redirect(BuildContext context, GoRouterState state) {
|
||
// if (this.state.isLoading || this.state.hasError) return null;
|
||
|
||
final isIntro = state.uri.path == const IntroRoute().location;
|
||
|
||
if (!_introCompleted) {
|
||
return const IntroRoute().location;
|
||
} else if (isIntro) {
|
||
return const HomeRoute().location;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
@override
|
||
void addListener(VoidCallback listener) {
|
||
_routerListener = listener;
|
||
}
|
||
|
||
@override
|
||
void removeListener(VoidCallback listener) {
|
||
_routerListener = null;
|
||
}
|
||
}
|