Nikolas Rimikis
2 years ago
committed by
GitHub
13 changed files with 649 additions and 428 deletions
@ -1,41 +1,124 @@ |
|||||||
part of '../neon.dart'; |
|
||||||
|
|
||||||
// ignore: prefer_mixin |
// ignore: prefer_mixin |
||||||
class AppRouter extends RouterDelegate<Account> with ChangeNotifier, PopNavigatorRouterDelegateMixin<Account> { |
import 'package:flutter/material.dart'; |
||||||
|
import 'package:go_router/go_router.dart'; |
||||||
|
import 'package:neon/neon.dart'; |
||||||
|
import 'package:provider/provider.dart'; |
||||||
|
|
||||||
|
part 'router.g.dart'; |
||||||
|
|
||||||
|
class AppRouter extends GoRouter { |
||||||
AppRouter({ |
AppRouter({ |
||||||
required this.navigatorKey, |
required final GlobalKey<NavigatorState> navigatorKey, |
||||||
required this.accountsBloc, |
required final AccountsBloc accountsBloc, |
||||||
|
}) : super( |
||||||
|
refreshListenable: StreamListenable.behaviorSubject(accountsBloc.activeAccount), |
||||||
|
navigatorKey: navigatorKey, |
||||||
|
initialLocation: const HomeRoute().location, |
||||||
|
redirect: (final context, final state) { |
||||||
|
final account = accountsBloc.activeAccount.valueOrNull; |
||||||
|
|
||||||
|
if (account == null) { |
||||||
|
return const LoginRoute().location; |
||||||
|
} |
||||||
|
|
||||||
|
if (state.location == const LoginRoute().location) { |
||||||
|
return const HomeRoute().location; |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
}, |
||||||
|
routes: $appRoutes, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@immutable |
||||||
|
class AccountSettingsRoute extends GoRouteData { |
||||||
|
const AccountSettingsRoute({ |
||||||
|
required this.accountid, |
||||||
}); |
}); |
||||||
|
|
||||||
final AccountsBloc accountsBloc; |
final String accountid; |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(final BuildContext context, final GoRouterState state) { |
||||||
|
final bloc = Provider.of<AccountsBloc>(context, listen: false); |
||||||
|
final account = bloc.accounts.value.find(accountid)!; |
||||||
|
|
||||||
|
return AccountSettingsPage( |
||||||
|
bloc: bloc, |
||||||
|
account: account, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@TypedGoRoute<HomeRoute>( |
||||||
|
path: '/', |
||||||
|
name: 'home', |
||||||
|
routes: [ |
||||||
|
TypedGoRoute<SettingsRoute>( |
||||||
|
path: 'settings', |
||||||
|
name: 'Settings', |
||||||
|
routes: [ |
||||||
|
TypedGoRoute<NextcloudAppSettingsRoute>( |
||||||
|
path: ':appid', |
||||||
|
name: 'NextcloudAppSettings', |
||||||
|
), |
||||||
|
TypedGoRoute<AccountSettingsRoute>( |
||||||
|
path: 'account/:accountid', |
||||||
|
name: 'AccountSettings', |
||||||
|
), |
||||||
|
], |
||||||
|
) |
||||||
|
], |
||||||
|
) |
||||||
|
@immutable |
||||||
|
class HomeRoute extends GoRouteData { |
||||||
|
const HomeRoute(); |
||||||
|
|
||||||
@override |
@override |
||||||
final GlobalKey<NavigatorState> navigatorKey; |
Widget build(final BuildContext context, final GoRouterState state) { |
||||||
|
final accountsBloc = Provider.of<AccountsBloc>(context, listen: false); |
||||||
|
final account = accountsBloc.activeAccount.valueOrNull!; |
||||||
|
|
||||||
|
return HomePage(key: Key(account.id)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@TypedGoRoute<LoginRoute>( |
||||||
|
path: '/login', |
||||||
|
name: 'login', |
||||||
|
) |
||||||
|
@immutable |
||||||
|
class LoginRoute extends GoRouteData { |
||||||
|
const LoginRoute({this.server}); |
||||||
|
|
||||||
|
final String? server; |
||||||
|
|
||||||
@override |
@override |
||||||
Future setNewRoutePath(final Account? configuration) async {} |
Widget build(final BuildContext context, final GoRouterState state) => LoginPage(serverURL: server); |
||||||
|
} |
||||||
|
|
||||||
|
@immutable |
||||||
|
class NextcloudAppSettingsRoute extends GoRouteData { |
||||||
|
const NextcloudAppSettingsRoute({ |
||||||
|
required this.appid, |
||||||
|
}); |
||||||
|
|
||||||
|
final String appid; |
||||||
|
|
||||||
@override |
@override |
||||||
Account? get currentConfiguration => accountsBloc.activeAccount.valueOrNull; |
Widget build(final BuildContext context, final GoRouterState state) { |
||||||
|
final appImplementations = Provider.of<List<AppImplementation>>(context, listen: false); |
||||||
|
final appImplementation = appImplementations.firstWhere((final app) => app.id == appid); |
||||||
|
|
||||||
|
return NextcloudAppSettingsPage(appImplementation: appImplementation); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@immutable |
||||||
|
class SettingsRoute extends GoRouteData { |
||||||
|
const SettingsRoute(); |
||||||
|
|
||||||
@override |
@override |
||||||
Widget build(final BuildContext context) => Navigator( |
Widget build(final BuildContext context, final GoRouterState state) => const SettingsPage(); |
||||||
key: navigatorKey, |
|
||||||
onPopPage: (final route, final result) => route.didPop(result), |
|
||||||
pages: [ |
|
||||||
if (currentConfiguration == null) ...[ |
|
||||||
const MaterialPage( |
|
||||||
child: LoginPage(), |
|
||||||
), |
|
||||||
] else ...[ |
|
||||||
MaterialPage( |
|
||||||
name: 'home', |
|
||||||
child: HomePage( |
|
||||||
key: Key(currentConfiguration!.id), |
|
||||||
account: currentConfiguration!, |
|
||||||
), |
|
||||||
), |
|
||||||
], |
|
||||||
], |
|
||||||
); |
|
||||||
} |
} |
||||||
|
@ -0,0 +1,122 @@ |
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND |
||||||
|
|
||||||
|
part of 'router.dart'; |
||||||
|
|
||||||
|
// ************************************************************************** |
||||||
|
// GoRouterGenerator |
||||||
|
// ************************************************************************** |
||||||
|
|
||||||
|
List<RouteBase> get $appRoutes => [ |
||||||
|
$homeRoute, |
||||||
|
$loginRoute, |
||||||
|
]; |
||||||
|
|
||||||
|
RouteBase get $homeRoute => GoRouteData.$route( |
||||||
|
path: '/', |
||||||
|
name: 'home', |
||||||
|
factory: $HomeRouteExtension._fromState, |
||||||
|
routes: [ |
||||||
|
GoRouteData.$route( |
||||||
|
path: 'settings', |
||||||
|
name: 'Settings', |
||||||
|
factory: $SettingsRouteExtension._fromState, |
||||||
|
routes: [ |
||||||
|
GoRouteData.$route( |
||||||
|
path: ':appid', |
||||||
|
name: 'NextcloudAppSettings', |
||||||
|
factory: $NextcloudAppSettingsRouteExtension._fromState, |
||||||
|
), |
||||||
|
GoRouteData.$route( |
||||||
|
path: 'account/:accountid', |
||||||
|
name: 'AccountSettings', |
||||||
|
factory: $AccountSettingsRouteExtension._fromState, |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
|
||||||
|
extension $HomeRouteExtension on HomeRoute { |
||||||
|
static HomeRoute _fromState(GoRouterState state) => const HomeRoute(); |
||||||
|
|
||||||
|
String get location => GoRouteData.$location( |
||||||
|
'/', |
||||||
|
); |
||||||
|
|
||||||
|
void go(BuildContext context) => context.go(location); |
||||||
|
|
||||||
|
Future<T?> push<T>(BuildContext context) => context.push<T>(location); |
||||||
|
|
||||||
|
void pushReplacement(BuildContext context) => context.pushReplacement(location); |
||||||
|
} |
||||||
|
|
||||||
|
extension $SettingsRouteExtension on SettingsRoute { |
||||||
|
static SettingsRoute _fromState(GoRouterState state) => const SettingsRoute(); |
||||||
|
|
||||||
|
String get location => GoRouteData.$location( |
||||||
|
'/settings', |
||||||
|
); |
||||||
|
|
||||||
|
void go(BuildContext context) => context.go(location); |
||||||
|
|
||||||
|
Future<T?> push<T>(BuildContext context) => context.push<T>(location); |
||||||
|
|
||||||
|
void pushReplacement(BuildContext context) => context.pushReplacement(location); |
||||||
|
} |
||||||
|
|
||||||
|
extension $NextcloudAppSettingsRouteExtension on NextcloudAppSettingsRoute { |
||||||
|
static NextcloudAppSettingsRoute _fromState(GoRouterState state) => NextcloudAppSettingsRoute( |
||||||
|
appid: state.pathParameters['appid']!, |
||||||
|
); |
||||||
|
|
||||||
|
String get location => GoRouteData.$location( |
||||||
|
'/settings/${Uri.encodeComponent(appid)}', |
||||||
|
); |
||||||
|
|
||||||
|
void go(BuildContext context) => context.go(location); |
||||||
|
|
||||||
|
Future<T?> push<T>(BuildContext context) => context.push<T>(location); |
||||||
|
|
||||||
|
void pushReplacement(BuildContext context) => context.pushReplacement(location); |
||||||
|
} |
||||||
|
|
||||||
|
extension $AccountSettingsRouteExtension on AccountSettingsRoute { |
||||||
|
static AccountSettingsRoute _fromState(GoRouterState state) => AccountSettingsRoute( |
||||||
|
accountid: state.pathParameters['accountid']!, |
||||||
|
); |
||||||
|
|
||||||
|
String get location => GoRouteData.$location( |
||||||
|
'/settings/account/${Uri.encodeComponent(accountid)}', |
||||||
|
); |
||||||
|
|
||||||
|
void go(BuildContext context) => context.go(location); |
||||||
|
|
||||||
|
Future<T?> push<T>(BuildContext context) => context.push<T>(location); |
||||||
|
|
||||||
|
void pushReplacement(BuildContext context) => context.pushReplacement(location); |
||||||
|
} |
||||||
|
|
||||||
|
RouteBase get $loginRoute => GoRouteData.$route( |
||||||
|
path: '/login', |
||||||
|
name: 'login', |
||||||
|
factory: $LoginRouteExtension._fromState, |
||||||
|
); |
||||||
|
|
||||||
|
extension $LoginRouteExtension on LoginRoute { |
||||||
|
static LoginRoute _fromState(GoRouterState state) => LoginRoute( |
||||||
|
server: state.queryParameters['server'], |
||||||
|
); |
||||||
|
|
||||||
|
String get location => GoRouteData.$location( |
||||||
|
'/login', |
||||||
|
queryParams: { |
||||||
|
if (server != null) 'server': server, |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
void go(BuildContext context) => context.go(location); |
||||||
|
|
||||||
|
Future<T?> push<T>(BuildContext context) => context.push<T>(location); |
||||||
|
|
||||||
|
void pushReplacement(BuildContext context) => context.pushReplacement(location); |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
part of '../../neon.dart'; |
||||||
|
|
||||||
|
/// Listenable Stream |
||||||
|
/// |
||||||
|
/// A class that implements [Listenable] for a stream. |
||||||
|
/// Objects need to be manually disposed. |
||||||
|
class StreamListenable extends ChangeNotifier { |
||||||
|
/// Listenable Stream |
||||||
|
/// |
||||||
|
/// Implementation for all types of [Stream]s. |
||||||
|
/// For an implementation tailored towards [BehaviorSubject] have a look at [StreamListenable.behaviorSubject]. |
||||||
|
StreamListenable(final Stream<dynamic> stream) { |
||||||
|
notifyListeners(); |
||||||
|
|
||||||
|
_subscription = stream.asBroadcastStream().listen((final value) { |
||||||
|
notifyListeners(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/// Listenable BehaviorSubject |
||||||
|
/// |
||||||
|
/// Implementation for a [BehaviorSubject]. It ensures to not unececcary notify listeners. |
||||||
|
/// For an implementation tailored towards otnher kinds of [Stream] have a look at [StreamListenable]. |
||||||
|
StreamListenable.behaviorSubject(final BehaviorSubject<dynamic> subject) { |
||||||
|
_subscription = subject.listen((final value) { |
||||||
|
notifyListeners(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
late final StreamSubscription<dynamic> _subscription; |
||||||
|
|
||||||
|
@override |
||||||
|
void dispose() { |
||||||
|
unawaited(_subscription.cancel()); |
||||||
|
|
||||||
|
super.dispose(); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue