Init repo

This commit is contained in:
PartyDonut 2024-09-15 14:12:28 +02:00
commit 764b6034e3
566 changed files with 212335 additions and 0 deletions

121
lib/routes/app_routes.dart Normal file
View file

@ -0,0 +1,121 @@
import 'package:fladder/providers/shared_provider.dart';
import 'package:fladder/providers/user_provider.dart';
import 'package:fladder/routes/build_routes/home_routes.dart';
import 'package:fladder/routes/build_routes/route_builder.dart';
import 'package:fladder/routes/build_routes/settings_routes.dart';
import 'package:fladder/screens/home.dart';
import 'package:fladder/screens/settings/settings_screen.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
class AppRoutes {
static final parentKey = GlobalKey<NavigatorState>();
static final homeShellKey = GlobalKey<NavigatorState>();
static final settingsKey = GlobalKey<NavigatorState>();
static final loginRoute = LoginRoute();
static final splashRoute = SplashRoute();
static final scrollController = ScrollController();
static GoRouter routes({required WidgetRef ref, required ScreenLayout screenLayout}) {
return GoRouter(
navigatorKey: parentKey,
initialLocation: splashRoute.route,
//Only useful for web if the user is not in an active session yet
redirect: kIsWeb
? (context, state) async {
if (state.uri.toString() == loginRoute.route || state.uri.toString() == splashRoute.route) return null;
await Future.microtask(() {
final lastUsedAccount = ref.read(sharedUtilityProvider).getActiveAccount();
if (lastUsedAccount == null) return loginRoute.route;
if (ref.read(userProvider) == null) ref.read(userProvider.notifier).loginUser(lastUsedAccount);
});
if (ref.read(userProvider) == null) {
return loginRoute.route;
} else {
return state.uri.toString();
}
}
: null,
routes: AppRoutes().getRoutes(screenLayout),
);
}
List<RouteBase> getRoutes(ScreenLayout screenLayout) {
switch (screenLayout) {
case ScreenLayout.single:
return linearRoutes;
case ScreenLayout.dual:
return nestedRoutes;
}
}
static List<RouteBase> linearRoutes = [
loginRoute.go,
splashRoute.go,
LockScreenRoute().go,
ShellRoute(
navigatorKey: homeShellKey,
pageBuilder: (context, state, child) {
return NoTransitionPage(
child: Home(
key: state.pageKey,
location: state.uri.toString(),
nestedChild: child,
),
);
},
routes: [
DashboardRoute().go,
FavouritesRoute().go,
SyncRoute().go,
],
),
DetailsRoute(navKey: parentKey).go,
LibrarySearchRoute(navKey: parentKey).go,
// Settings routes
SettingsRoute(navKey: parentKey).go,
ClientSettingsRoute(navKey: parentKey).go,
SecuritySettingsRoute(navKey: parentKey).go,
PlayerSettingsRoute(navKey: parentKey).go,
];
static List<RouteBase> nestedRoutes = [
loginRoute.go,
splashRoute.go,
LockScreenRoute().go,
ShellRoute(
navigatorKey: homeShellKey,
pageBuilder: (context, state, child) => NoTransitionPage(
child: Home(
key: state.pageKey,
location: state.uri.toString(),
nestedChild: child,
),
),
routes: [
ShellRoute(
navigatorKey: settingsKey,
pageBuilder: (context, state, child) => NoTransitionPage(
key: state.pageKey,
child: SettingsScreen(location: state.uri.toString(), child: child),
),
routes: [
ClientSettingsRoute(navKey: settingsKey).go,
SecuritySettingsRoute(navKey: settingsKey).go,
PlayerSettingsRoute(navKey: settingsKey).go,
],
),
DashboardRoute().go,
FavouritesRoute().go,
SyncRoute().go,
DetailsRoute(navKey: homeShellKey).go,
LibrarySearchRoute(navKey: homeShellKey).go,
],
),
];
}

View file

@ -0,0 +1,182 @@
import 'package:animations/animations.dart';
import 'package:collection/collection.dart';
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/library_search/library_search_options.dart';
import 'package:fladder/routes/app_routes.dart';
import 'package:fladder/routes/build_routes/route_builder.dart';
import 'package:fladder/screens/dashboard/dashboard_screen.dart';
import 'package:fladder/screens/syncing/synced_screen.dart';
import 'package:fladder/screens/favourites/favourites_screen.dart';
import 'package:fladder/screens/library_search/library_search_screen.dart';
import 'package:fladder/screens/shared/detail_scaffold.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class DashboardRoute extends CustomRoute {
DashboardRoute() : super(name: 'Dashboard', basePath: '/dashboard');
@override
GoRoute get go => GoRoute(
path: basePath,
parentNavigatorKey: AppRoutes.homeShellKey,
pageBuilder: (context, state) {
return CustomTransitionPage(
key: state.pageKey,
child: DashboardScreen(
key: Key(basePath),
navigationScrollController: AppRoutes.scrollController,
),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
child: child,
);
});
},
);
}
class FavouritesRoute extends CustomRoute {
FavouritesRoute() : super(name: 'Favorites', basePath: '/favorites');
@override
GoRoute get go => GoRoute(
path: basePath,
parentNavigatorKey: AppRoutes.homeShellKey,
pageBuilder: (context, state) {
return CustomTransitionPage(
key: state.pageKey,
child: FavouritesScreen(
key: Key(basePath),
navigationScrollController: AppRoutes.scrollController,
),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
child: child,
);
});
},
);
}
class SyncRoute extends CustomRoute {
SyncRoute() : super(name: 'Sync', basePath: '/sync');
@override
GoRoute get go => GoRoute(
path: basePath,
parentNavigatorKey: AppRoutes.homeShellKey,
pageBuilder: (context, state) {
return CustomTransitionPage(
key: state.pageKey,
child: SyncedScreen(
key: Key(basePath),
navigationScrollController: AppRoutes.scrollController,
),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
child: child,
);
});
},
);
}
class DetailsRoute extends CustomRoute {
final String? id;
final GlobalKey<NavigatorState>? navKey;
DetailsRoute({this.id, this.navKey}) : super(name: 'Details', basePath: '/details', arguments: ':itemId');
@override
String get route => "$basePath/$id";
@override
GoRoute get go => GoRoute(
path: path,
parentNavigatorKey: navKey,
pageBuilder: (context, state) {
final String id = state.pathParameters['itemId'] ?? "nothing";
ItemBaseModel? item = state.extra as ItemBaseModel?;
return CustomTransitionPage(
key: state.pageKey,
child: DetailScreen(
key: Key(id),
id: id,
item: item,
),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeThroughTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
child: child,
);
},
);
},
);
}
const _libraryKey = "libraryId";
const _sortOptionsKey = "sortOptions";
const _sortOrderKey = "sortOrder";
const _favoriteKey = "favorite";
const _folderKey = "folder";
class LibrarySearchRoute extends CustomRoute {
final String? id;
final bool? favorites;
final SortingOptions? sortOptions;
final SortOrder? sortOrder;
final String? folderId;
final GlobalKey<NavigatorState>? navKey;
LibrarySearchRoute({this.id, this.favorites, this.sortOptions, this.sortOrder, this.folderId, this.navKey})
: super(
name: 'LibrarySearch',
basePath: '/library',
queryParameters: {
_libraryKey: id,
_sortOptionsKey: sortOptions?.name,
_sortOrderKey: sortOrder?.name,
_favoriteKey: favorites,
_folderKey: folderId,
},
);
@override
String get route => "$basePath${parseUrlParameters(queryParameters)}";
@override
GoRoute get go => GoRoute(
path: basePath,
parentNavigatorKey: navKey,
pageBuilder: (context, state) {
final String? id = state.uri.queryParameters[_libraryKey];
final bool? favourites = bool.tryParse(state.uri.queryParameters[_favoriteKey] ?? "");
final String? folderId = state.uri.queryParameters[_folderKey];
final SortingOptions? sortingOptions = SortingOptions.values
.firstWhereOrNull((element) => element.name == state.uri.queryParameters[_sortOptionsKey]);
final SortingOrder? sortOrder = SortingOrder.values
.firstWhereOrNull((element) => element.name == state.uri.queryParameters[_sortOrderKey]);
return CustomTransitionPage(
key: state.pageKey,
child: LibrarySearchScreen(
key: Key(id ?? "librarySearch"),
viewModelId: id,
folderId: folderId?.split(','),
sortingOptions: sortingOptions,
sortOrder: sortOrder,
favourites: favourites,
),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
child: child,
);
},
);
},
);
}

View file

@ -0,0 +1,124 @@
import 'package:fladder/screens/login/login_screen.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:fladder/routes/app_routes.dart';
import 'package:fladder/screens/login/lock_screen.dart';
import 'package:fladder/screens/splash_screen.dart';
import 'package:fladder/util/adaptive_layout.dart';
extension RouteContextBuilder on BuildContext {
/// Push a location onto the page stack.
Future<T?> routePush<T>(CustomRoute route, {Object? extra}) async {
return push(route.route, extra: extra);
}
/// Replaces the top-most page of the page stack with the given URL location
void routeReplace(CustomRoute route, {Object? extra}) {
replace(route.route, extra: extra);
}
/// Navigate to a location.
void routeGo(CustomRoute route, {Object? extra}) {
go(route.route, extra: extra);
}
/// [Pushed] if nested(single) else [Go]
void routePushOrGo(CustomRoute route, {Object? extra}) {
switch (AdaptiveLayout.of(this).size) {
case ScreenLayout.single:
routePush(route, extra: extra);
break;
case ScreenLayout.dual:
routeGo(route, extra: extra);
break;
}
}
/// [Push] if nested(single) else [Replace]
void routeReplaceOrPush(CustomRoute route, {Object? extra}) {
switch (AdaptiveLayout.of(this).size) {
case ScreenLayout.single:
routePush(route, extra: extra);
break;
case ScreenLayout.dual:
routeReplace(route, extra: extra);
break;
}
}
}
abstract class CustomRoute {
final String name;
final String basePath;
final String? arguments;
final Map<String, dynamic>? queryParameters;
CustomRoute({required this.name, required this.basePath, this.arguments, this.queryParameters})
: assert(basePath.isNotEmpty, 'GoRoute path cannot be empty');
String get route => basePath;
String get path => "$basePath/${arguments ?? queryParameters}";
void navigate() {
AppRoutes.parentKey.currentContext?.routeGo(this);
}
void replaceRoot() {
AppRoutes.parentKey.currentContext?.routeReplace(this);
}
RouteBase get go;
}
class LoginRoute extends CustomRoute {
LoginRoute() : super(name: 'Login', basePath: '/login');
@override
GoRoute get go => GoRoute(
path: basePath,
builder: (context, state) {
return const LoginScreen();
},
);
}
class SplashRoute extends CustomRoute {
SplashRoute() : super(name: 'Splash', basePath: '/splash');
@override
GoRoute get go => GoRoute(
path: basePath,
builder: (context, state) {
return const SplashScreen();
},
);
}
class LockScreenRoute extends CustomRoute {
final bool selfLock;
LockScreenRoute({this.selfLock = false}) : super(name: 'Lock', basePath: '/lock');
@override
GoRoute get go => GoRoute(
parentNavigatorKey: AppRoutes.parentKey,
path: basePath,
builder: (context, state) {
return LockScreen(
selfLock: selfLock,
);
},
);
}
String parseUrlParameters(Map<String, dynamic>? parameters) {
if (parameters == null) return '';
String parameterString = '?';
for (String key in parameters.keys) {
if (parameters[key] != null) parameterString += '$key=${parameters[key]}&';
}
return parameterString.substring(0, parameterString.length - 1);
}

View file

@ -0,0 +1,103 @@
import 'package:fladder/routes/build_routes/route_builder.dart';
import 'package:fladder/screens/settings/client_settings_page.dart';
import 'package:fladder/screens/settings/player_settings_page.dart';
import 'package:fladder/screens/settings/settings_screen.dart';
import 'package:fladder/screens/settings/security_settings_page.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class SettingsRoute extends CustomRoute {
final String? id;
final GlobalKey<NavigatorState>? navKey;
SettingsRoute({this.id, this.navKey}) : super(name: 'Settings', basePath: '/settings');
@override
GoRoute get go => GoRoute(
path: basePath,
parentNavigatorKey: navKey,
pageBuilder: (context, state) {
return CustomTransitionPage(
key: state.pageKey,
child: const SettingsScreen(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
child: child,
);
},
);
},
);
}
class ClientSettingsRoute extends CustomRoute {
final String? id;
final GlobalKey<NavigatorState>? navKey;
ClientSettingsRoute({this.id, this.navKey}) : super(name: 'ClientSettings', basePath: '/settings/client');
@override
GoRoute get go => GoRoute(
path: basePath,
parentNavigatorKey: navKey,
pageBuilder: (context, state) {
return CustomTransitionPage(
key: state.pageKey,
child: const ClientSettingsPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
child: child,
);
},
);
},
);
}
class SecuritySettingsRoute extends CustomRoute {
final String? id;
final GlobalKey<NavigatorState>? navKey;
SecuritySettingsRoute({this.id, this.navKey}) : super(name: 'SecuritySettings', basePath: '/settings/security');
@override
GoRoute get go => GoRoute(
path: basePath,
parentNavigatorKey: navKey,
pageBuilder: (context, state) {
return CustomTransitionPage(
key: state.pageKey,
child: const SecuritySettingsPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
child: child,
);
},
);
},
);
}
class PlayerSettingsRoute extends CustomRoute {
final String? id;
final GlobalKey<NavigatorState>? navKey;
PlayerSettingsRoute({this.id, this.navKey}) : super(name: 'PlayerSettings', basePath: '/settings/player');
@override
GoRoute get go => GoRoute(
path: basePath,
parentNavigatorKey: navKey,
pageBuilder: (context, state) {
return CustomTransitionPage(
key: state.pageKey,
child: const PlayerSettingsPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
child: child,
);
},
);
},
);
}