prepare for multiple apps

This commit is contained in:
Fabian Freund
2026-04-06 12:23:11 +02:00
parent bd1600e8dc
commit 5afc323f04
904 changed files with 29 additions and 29 deletions
@@ -0,0 +1,152 @@
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
part of 'routes.dart';
@TypedGoRoute<BangMenuRoute>(
name: 'BangRoute',
path: '/bangs',
routes: [
TypedGoRoute<UserBangsRoute>(
name: 'UserBangsRoute',
path: 'user',
routes: [
TypedGoRoute<NewUserBangRoute>(name: 'NewUserBangRoute', path: 'new'),
TypedGoRoute<EditUserBangRoute>(
name: 'EditUserBangRoute',
path: 'edit',
),
],
),
TypedGoRoute<BangSearchRoute>(
name: 'BangSearchRoute',
path: 'search/:searchText',
),
TypedGoRoute<BangCategoriesRoute>(
name: 'BangCategoriesRoute',
path: 'categories',
routes: [
TypedGoRoute<BangCategoryRoute>(
name: 'BangCategoryRoute',
path: 'category/:category',
routes: [
TypedGoRoute<BangSubCategoryRoute>(
name: 'BangSubCategoryRoute',
path: ':subCategory',
),
],
),
],
),
],
)
class BangMenuRoute extends GoRouteData with $BangMenuRoute {
const BangMenuRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return const BangMenuScreen();
}
}
class BangCategoriesRoute extends GoRouteData with $BangCategoriesRoute {
const BangCategoriesRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return const BangCategoriesScreen();
}
}
class BangCategoryRoute extends GoRouteData with $BangCategoryRoute {
final String category;
const BangCategoryRoute({required this.category});
@override
Widget build(BuildContext context, GoRouterState state) {
return BangCategoryScreen(category: category);
}
}
class BangSubCategoryRoute extends GoRouteData with $BangSubCategoryRoute {
final String category;
final String subCategory;
const BangSubCategoryRoute({
required this.category,
required this.subCategory,
});
@override
Widget build(BuildContext context, GoRouterState state) {
return BangCategoryScreen(category: category, subCategory: subCategory);
}
}
class BangSearchRoute extends GoRouteData with $BangSearchRoute {
static const String emptySearchText = ' ';
//This should be nullable but isnt allowed by go_router
final String searchText;
const BangSearchRoute({this.searchText = BangSearchRoute.emptySearchText});
@override
Widget build(BuildContext context, GoRouterState state) {
return BangSearchScreen(
initialSearchText: (searchText.isEmpty || searchText == emptySearchText)
? null
: searchText,
);
}
}
class UserBangsRoute extends GoRouteData with $UserBangsRoute {
const UserBangsRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return const UserBangs();
}
}
class NewUserBangRoute extends GoRouteData with $NewUserBangRoute {
const NewUserBangRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return const EditBangScreen(initialBang: null);
}
}
class EditUserBangRoute extends GoRouteData with $EditUserBangRoute {
final String initialBang;
const EditUserBangRoute({required this.initialBang});
@override
Widget build(BuildContext context, GoRouterState state) {
return EditBangScreen(
initialBang: Bang.fromJson(
jsonDecode(initialBang) as Map<String, dynamic>,
),
);
}
}
@@ -0,0 +1,121 @@
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
part of 'routes.dart';
@TypedGoRoute<BookmarksRoute>(
name: 'BookmarksRoute',
path: '/bookmarks',
routes: [
TypedGoRoute<BookmarkListRoute>(
name: 'BookmarkListRoute',
path: 'list/:entryGuid',
),
TypedGoRoute<BookmarkFolderAddRoute>(
name: 'BookmarkFolderAddRoute',
path: 'createFolder',
),
TypedGoRoute<BookmarkFolderEditRoute>(
name: 'BookmarkFolderEditRoute',
path: 'editFolder',
),
TypedGoRoute<BookmarkEntryAddRoute>(
name: 'BookmarkEntryAddRoute',
path: 'createEntry',
),
TypedGoRoute<BookmarkEntryEditRoute>(
name: 'BookmarkEntryEditRoute',
path: 'editEntry',
),
],
)
class BookmarksRoute extends GoRouteData with $BookmarksRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const SizedBox.shrink();
}
}
class BookmarkListRoute extends GoRouteData with $BookmarkListRoute {
final String entryGuid;
const BookmarkListRoute({required this.entryGuid});
@override
Widget build(BuildContext context, GoRouterState state) {
return BookmarkListScreen(entryGuid: entryGuid);
}
}
class BookmarkFolderAddRoute extends GoRouteData with $BookmarkFolderAddRoute {
final String? parentGuid;
const BookmarkFolderAddRoute({required this.parentGuid});
@override
Widget build(BuildContext context, GoRouterState state) {
return BookmarkFolderEditScreen(parentGuid: parentGuid, folder: null);
}
}
class BookmarkFolderEditRoute extends GoRouteData
with $BookmarkFolderEditRoute {
final String folder;
const BookmarkFolderEditRoute({required this.folder});
@override
Widget build(BuildContext context, GoRouterState state) {
return BookmarkFolderEditScreen(
folder: BookmarkFolder.fromJson(
jsonDecode(folder) as Map<String, dynamic>,
),
);
}
}
class BookmarkEntryAddRoute extends GoRouteData with $BookmarkEntryAddRoute {
final String bookmarkInfo;
const BookmarkEntryAddRoute({required this.bookmarkInfo});
@override
Widget build(BuildContext context, GoRouterState state) {
return BookmarkEntryEditScreen(
initialInfo: BookmarkInfo.decode(jsonDecode(bookmarkInfo) as Object),
exisitingEntry: null,
);
}
}
class BookmarkEntryEditRoute extends GoRouteData with $BookmarkEntryEditRoute {
final String bookmarkEntry;
const BookmarkEntryEditRoute({required this.bookmarkEntry});
@override
Widget build(BuildContext context, GoRouterState state) {
return BookmarkEntryEditScreen(
initialInfo: null,
exisitingEntry: BookmarkEntry.fromJson(
jsonDecode(bookmarkEntry) as Map<String, dynamic>,
),
);
}
}
@@ -0,0 +1,282 @@
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
part of 'routes.dart';
@TypedGoRoute<BrowserRoute>(
name: BrowserRoute.name,
path: '/browser',
routes: [
TypedGoRoute<SearchRoute>(
name: 'SearchRoute',
path: 'search/:tabType/:searchText',
),
TypedGoRoute<TabViewRoute>(name: 'TabViewRoute', path: 'tab_view'),
TypedGoRoute<ContextMenuRoute>(
name: 'ContextMenuRoute',
path: 'context_menu',
),
TypedGoRoute<ContainerDraftRoute>(
name: 'ContainerDraftRoute',
path: 'container_draft',
),
TypedGoRoute<ContainerListRoute>(
name: 'ContainerListRoute',
path: 'containers',
routes: [
TypedGoRoute<ContainerCreateRoute>(
name: 'ContainerCreateRoute',
path: 'create/:containerData',
),
TypedGoRoute<ContainerEditRoute>(
name: 'ContainerEditRoute',
path: 'edit/:containerData',
),
],
),
TypedGoRoute<ContainerSelectionRoute>(
name: 'ContainerSelectionRoute',
path: 'select_container',
),
TypedGoRoute<TabTreeRoute>(
name: 'TabTreeRoute',
path: 'tab_tree/:rootTabId',
),
TypedGoRoute<OpenSharedContentRoute>(
name: 'OpenSharedContentRoute',
path: 'open_content',
),
TypedGoRoute<SelectProfileRoute>(
name: 'SelectProfileRoute',
path: 'profile',
),
],
)
class BrowserRoute extends GoRouteData with $BrowserRoute {
static const name = 'BrowserRoute';
const BrowserRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return const BrowserScreen();
}
}
enum TabType { regular, private, child, isolated }
class SearchRoute extends GoRouteData with $SearchRoute {
static const String emptySearchText = ' ';
final TabType tabType;
//This should be nullable but isnt allowed by go_router
final String searchText;
final bool launchedFromIntent;
/// When provided, the search screen will load URLs into this existing tab
/// instead of creating a new tab. This also changes the UI to show
/// site-specific bangs instead of the tab type selector.
final String? tabId;
const SearchRoute({
required this.tabType,
this.searchText = SearchRoute.emptySearchText,
this.launchedFromIntent = false,
this.tabId,
});
@override
Widget build(BuildContext context, GoRouterState state) {
return SearchScreen(
tabType: tabType,
initialSearchText: (searchText.isEmpty || searchText == emptySearchText)
? null
: searchText,
launchedFromIntent: launchedFromIntent,
tabId: tabId,
);
}
}
bool _isContainerUiEnabled(BuildContext context) {
final settings = ProviderScope.containerOf(
context,
listen: false,
).read(generalSettingsWithDefaultsProvider);
return settings.showContainerUi;
}
class ContainerDraftRoute extends GoRouteData with $ContainerDraftRoute {
const ContainerDraftRoute();
@override
String? redirect(BuildContext context, GoRouterState state) {
return _isContainerUiEnabled(context)
? null
: const BrowserRoute().location;
}
@override
Widget build(BuildContext context, GoRouterState state) {
return const ContainerDraftSuggestionsScreen();
}
}
class ContainerListRoute extends GoRouteData with $ContainerListRoute {
const ContainerListRoute();
@override
String? redirect(BuildContext context, GoRouterState state) {
return _isContainerUiEnabled(context)
? null
: const BrowserRoute().location;
}
@override
Widget build(BuildContext context, GoRouterState state) {
return const ContainerListScreen();
}
}
class ContainerSelectionRoute extends GoRouteData
with $ContainerSelectionRoute {
const ContainerSelectionRoute();
@override
String? redirect(BuildContext context, GoRouterState state) {
return _isContainerUiEnabled(context)
? null
: const BrowserRoute().location;
}
@override
Widget build(BuildContext context, GoRouterState state) {
return const ContainerSelectionScreen();
}
}
class ContainerEditRoute extends GoRouteData with $ContainerEditRoute {
final String containerData;
const ContainerEditRoute({required this.containerData});
@override
String? redirect(BuildContext context, GoRouterState state) {
return _isContainerUiEnabled(context)
? null
: const BrowserRoute().location;
}
@override
Widget build(BuildContext context, GoRouterState state) {
return ContainerEditScreen.edit(
initialContainer: ContainerDataWithCount.fromJson(
jsonDecode(containerData) as Map<String, dynamic>,
),
);
}
}
class ContainerCreateRoute extends GoRouteData with $ContainerCreateRoute {
final String containerData;
final String tabIds;
ContainerCreateRoute({required this.containerData, this.tabIds = '[]'});
@override
String? redirect(BuildContext context, GoRouterState state) {
return _isContainerUiEnabled(context)
? null
: const BrowserRoute().location;
}
@override
Widget build(BuildContext context, GoRouterState state) {
final tabIdsList = jsonDecode(tabIds) as List;
final tabIdsSet = tabIdsList.cast<String>().toSet();
return ContainerEditScreen.create(
initialContainer: ContainerData.fromJson(
jsonDecode(containerData) as Map<String, dynamic>,
),
tabIds: tabIdsSet.isNotEmpty ? tabIdsSet : null,
);
}
}
class ContextMenuRoute extends GoRouteData with $ContextMenuRoute {
final String hitResult;
const ContextMenuRoute({required this.hitResult});
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return DialogPage(
builder: (_) =>
ContextMenuDialog(hitResult: HitResultJson.fromJson(hitResult)),
);
}
}
class TabTreeRoute extends GoRouteData with $TabTreeRoute {
final String rootTabId;
const TabTreeRoute(this.rootTabId);
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return DialogPage(builder: (_) => TabTreeDialog(rootTabId));
}
}
class OpenSharedContentRoute extends GoRouteData with $OpenSharedContentRoute {
final String sharedUrl;
const OpenSharedContentRoute({this.sharedUrl = 'about:blank'});
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return BottomSheetPage(
builder: (_) => OpenSharedContent(
sharedUrl: Uri.tryParse(sharedUrl) ?? Uri.parse('about:blank'),
),
);
}
}
class TabViewRoute extends GoRouteData with $TabViewRoute {
const TabViewRoute();
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return DialogPage(builder: (_) => const TabViewScreen());
}
}
class SelectProfileRoute extends GoRouteData with $SelectProfileRoute {
const SelectProfileRoute();
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return BottomSheetPage(builder: (_) => const SelectProfileDialog());
}
}
+151
View File
@@ -0,0 +1,151 @@
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:nullability/nullability.dart';
import 'package:weblibre/core/routing/widgets/bottom_sheet_page.dart';
import 'package:weblibre/core/routing/widgets/dialog_page.dart';
import 'package:weblibre/domain/entities/profile.dart';
import 'package:weblibre/features/about/presentation/screens/about.dart';
import 'package:weblibre/features/bangs/data/models/bang.dart';
import 'package:weblibre/features/bangs/presentation/screens/categories.dart';
import 'package:weblibre/features/bangs/presentation/screens/category.dart';
import 'package:weblibre/features/bangs/presentation/screens/edit.dart';
import 'package:weblibre/features/bangs/presentation/screens/menu.dart';
import 'package:weblibre/features/bangs/presentation/screens/search.dart';
import 'package:weblibre/features/bangs/presentation/screens/user.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/presentation/screens/bookmark_entry_edit.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/presentation/screens/bookmark_folder_edit.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/presentation/screens/bookmark_list.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/tab_tree.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/screens/browser.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/screens/tab_view.dart';
import 'package:weblibre/features/geckoview/features/contextmenu/extensions/hit_result.dart';
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/context_menu_dialog.dart';
import 'package:weblibre/features/geckoview/features/history/presentation/screens/history.dart';
import 'package:weblibre/features/geckoview/features/open_link_tools/presentation/dialogs/open_shared_content.dart';
import 'package:weblibre/features/geckoview/features/open_link_tools/presentation/screens/unshortener_settings.dart';
import 'package:weblibre/features/geckoview/features/open_link_tools/presentation/screens/url_cleaner_settings.dart';
import 'package:weblibre/features/geckoview/features/search/presentation/screens/search.dart';
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_draft_suggestions.dart';
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_edit.dart';
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_list.dart';
import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_selection.dart';
import 'package:weblibre/features/onboarding/presentation/onboarding.dart';
import 'package:weblibre/features/settings/presentation/screens/addon_collection.dart';
import 'package:weblibre/features/settings/presentation/screens/advanced_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/bang_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/browsing_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/contextual_toolbar_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/custom_tracking_protection.dart';
import 'package:weblibre/features/settings/presentation/screens/doh_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/error_logs_screen.dart';
import 'package:weblibre/features/settings/presentation/screens/experimental_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/extensions_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/fingerprint_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/general_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/locale_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/privacy_security_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/search_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/settings.dart';
import 'package:weblibre/features/settings/presentation/screens/toolbar_layout_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/tracking_protection_exceptions.dart';
import 'package:weblibre/features/settings/presentation/screens/web_content_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening.dart';
import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening_group.dart';
import 'package:weblibre/features/sync/presentation/screens/sync_settings.dart';
import 'package:weblibre/features/tor/presentation/screens/country_picker.dart';
import 'package:weblibre/features/tor/presentation/screens/tor_proxy.dart';
import 'package:weblibre/features/user/domain/presentation/dialogs/select_profile.dart';
import 'package:weblibre/features/user/domain/presentation/screens/profile_backup.dart';
import 'package:weblibre/features/user/domain/presentation/screens/profile_backup_list.dart';
import 'package:weblibre/features/user/domain/presentation/screens/profile_edit.dart';
import 'package:weblibre/features/user/domain/presentation/screens/profile_list.dart';
import 'package:weblibre/features/user/domain/presentation/screens/profile_restore.dart';
import 'package:weblibre/features/user/domain/presentation/widgets/auth_gate.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
import 'package:weblibre/features/web_feed/presentation/add_feed_dialog.dart';
import 'package:weblibre/features/web_feed/presentation/screens/feed_article.dart';
import 'package:weblibre/features/web_feed/presentation/screens/feed_article_list.dart';
import 'package:weblibre/features/web_feed/presentation/screens/feed_edit.dart';
import 'package:weblibre/features/web_feed/presentation/screens/feed_list.dart';
import 'package:weblibre/features/web_feed/presentation/select_feed_dialog.dart';
part 'routes.bangs.dart';
part 'routes.bookmarks.dart';
part 'routes.browser.dart';
part 'routes.feeds.dart';
part 'routes.g.dart';
part 'routes.history.dart';
part 'routes.profiles.dart';
part 'routes.settings.dart';
part 'routes.tor.dart';
@TypedGoRoute<AboutRoute>(name: 'AboutRoute', path: '/about')
class AboutRoute extends GoRouteData with $AboutRoute {
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return DialogPage(builder: (_) => const AboutDialogScreen());
}
}
@TypedGoRoute<OnboardingRoute>(
name: OnboardingRoute.name,
path: '${OnboardingRoute.pathPrefix}/:currentRevision/:targetRevision',
)
class OnboardingRoute extends GoRouteData with $OnboardingRoute {
static const name = 'OnboardingRoute';
static const pathPrefix = '/onboarding';
final int currentRevision;
final int targetRevision;
const OnboardingRoute({
required this.currentRevision,
required this.targetRevision,
});
@override
Widget build(BuildContext context, GoRouterState state) {
return OnboardingScreen(
currentRevision: currentRevision,
targetRevision: targetRevision,
);
}
}
@TypedGoRoute<LockRoute>(name: LockRoute.name, path: LockRoute.path)
class LockRoute extends GoRouteData with $LockRoute {
static const name = 'LockRoute';
static const path = '/lock';
const LockRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return const LockScreen();
}
}
@@ -0,0 +1,130 @@
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
part of 'routes.dart';
@TypedGoRoute<FeedListRoute>(
name: 'FeedListRoute',
path: '/feeds',
routes: [
TypedGoRoute<FeedAddRoute>(name: FeedAddRoute.name, path: 'add'),
TypedGoRoute<FeedArticleListRoute>(
name: 'FeedArticleListRoute',
path: 'articles/:feedId',
),
TypedGoRoute<FeedArticleRoute>(
name: 'FeedArticleRoute',
path: 'article/:articleId',
),
TypedGoRoute<FeedCreateRoute>(
name: 'FeedCreateRoute',
path: 'create/:feedId',
),
TypedGoRoute<SelectFeedDialogRoute>(
name: 'SelectFeedDialogRoute',
path: 'available/:feedsJson',
),
TypedGoRoute<FeedEditRoute>(name: 'FeedEditRoute', path: 'edit/:feedId'),
],
)
class FeedListRoute extends GoRouteData with $FeedListRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const FeedListScreen();
}
}
class FeedCreateRoute extends GoRouteData with $FeedCreateRoute {
final Uri feedId;
FeedCreateRoute({required this.feedId});
@override
Widget build(BuildContext context, GoRouterState state) {
return FeedEditScreen.create(feedId: feedId);
}
}
class SelectFeedDialogRoute extends GoRouteData with $SelectFeedDialogRoute {
final String feedsJson;
const SelectFeedDialogRoute({required this.feedsJson});
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
final feedUris = Set<Uri>.from(
(jsonDecode(feedsJson) as List<dynamic>).map(
(url) => Uri.parse(url as String),
),
);
return BottomSheetPage(
builder: (_) => SelectFeedDialog(feedUris: feedUris),
);
}
}
class FeedEditRoute extends GoRouteData with $FeedEditRoute {
final Uri feedId;
const FeedEditRoute({required this.feedId});
@override
Widget build(BuildContext context, GoRouterState state) {
return FeedEditScreen.edit(feedId: feedId);
}
}
class FeedAddRoute extends GoRouteData with $FeedAddRoute {
final String? uri;
static const name = 'FeedAddRoute';
const FeedAddRoute({required this.uri});
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return DialogPage(
builder: (_) =>
AddFeedDialog(initialUri: uri.mapNotNull((uri) => Uri.tryParse(uri))),
);
}
}
class FeedArticleListRoute extends GoRouteData with $FeedArticleListRoute {
final Uri feedId;
FeedArticleListRoute({required this.feedId});
@override
Widget build(BuildContext context, GoRouterState state) {
return FeedArticleListScreen(feedId: feedId);
}
}
class FeedArticleRoute extends GoRouteData with $FeedArticleRoute {
final String articleId;
FeedArticleRoute({required this.articleId});
@override
Widget build(BuildContext context, GoRouterState state) {
return FeedArticleScreen(articleId: articleId);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
part of 'routes.dart';
@TypedGoRoute<HistoryRoute>(
name: 'HistoryRoute',
path: '/history',
routes: [
TypedGoRoute<HistoryDownloadsRoute>(
name: 'HistoryDownloadsRoute',
path: 'downloads',
),
],
)
class HistoryRoute extends GoRouteData with $HistoryRoute {
const HistoryRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return const HistoryScreen();
}
}
class HistoryDownloadsRoute extends GoRouteData with $HistoryDownloadsRoute {
const HistoryDownloadsRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return const HistoryScreen(mode: HistoryScreenMode.downloads);
}
}
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
part of 'routes.dart';
@TypedGoRoute<ProfileListRoute>(
name: 'ProfileListRoute',
path: '/profiles',
routes: [
TypedGoRoute<EditProfileRoute>(name: 'ProfileEditRoute', path: 'edit'),
TypedGoRoute<ProfileBackupListRoute>(
name: 'ProfileBackupListRoute',
path: 'backup_list',
),
TypedGoRoute<RestoreProfileRoute>(
name: 'RestoreProfileRoute',
path: 'restore',
),
TypedGoRoute<BackupProfileRoute>(
name: 'BackupProfileRoute',
path: 'backup',
),
TypedGoRoute<CreateProfileRoute>(
name: 'CreateProfileRoute',
path: 'create',
),
],
)
class ProfileListRoute extends GoRouteData with $ProfileListRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const ProfileListScreen();
}
}
class CreateProfileRoute extends GoRouteData with $CreateProfileRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const ProfileEditScreen(profile: null);
}
}
class EditProfileRoute extends GoRouteData with $EditProfileRoute {
final String profile;
const EditProfileRoute({required this.profile});
@override
Widget build(BuildContext context, GoRouterState state) {
return ProfileEditScreen(
profile: Profile.fromJson(jsonDecode(profile) as Map<String, dynamic>),
);
}
}
class BackupProfileRoute extends GoRouteData with $BackupProfileRoute {
final String profile;
const BackupProfileRoute({required this.profile});
@override
Widget build(BuildContext context, GoRouterState state) {
return ProfileBackupScreen(
profile: Profile.fromJson(jsonDecode(profile) as Map<String, dynamic>),
);
}
}
class RestoreProfileRoute extends GoRouteData with $RestoreProfileRoute {
final String backupFileUri;
const RestoreProfileRoute({required this.backupFileUri});
@override
Widget build(BuildContext context, GoRouterState state) {
return ProfileRestoreScreen(backupFileUri: Uri.parse(backupFileUri));
}
}
class ProfileBackupListRoute extends GoRouteData with $ProfileBackupListRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const ProfileBackupListScreen();
}
}
@@ -0,0 +1,295 @@
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
part of 'routes.dart';
@TypedGoRoute<SettingsRoute>(
name: 'SettingsRoute',
path: '/settings',
routes: [
TypedGoRoute<GeneralSettingsRoute>(
name: 'GeneralSettingsRoute',
path: 'general',
),
TypedGoRoute<BrowsingSettingsRoute>(
name: 'BrowsingSettingsRoute',
path: 'browsing',
),
TypedGoRoute<PrivacySecuritySettingsRoute>(
name: 'PrivacySecuritySettingsRoute',
path: 'privacy_security',
),
TypedGoRoute<ToolbarLayoutSettingsRoute>(
name: 'ToolbarLayoutSettingsRoute',
path: 'toolbar_layout',
),
TypedGoRoute<WebContentSettingsRoute>(
name: 'WebContentSettingsRoute',
path: 'web_content',
),
TypedGoRoute<SearchSettingsRoute>(
name: 'SearchSettingsRoute',
path: 'search',
),
TypedGoRoute<ExtensionsSettingsRoute>(
name: 'ExtensionsSettingsRoute',
path: 'extensions',
),
TypedGoRoute<AdvancedSettingsRoute>(
name: 'AdvancedSettingsRoute',
path: 'advanced',
),
TypedGoRoute<ExperimentalSettingsRoute>(
name: 'ExperimentalSettingsRoute',
path: 'experimental',
),
TypedGoRoute<BangSettingsRoute>(name: 'BangSettingsRoute', path: 'bang'),
TypedGoRoute<WebEngineHardeningRoute>(
name: 'WebEngineHardeningRoute',
path: 'hardening',
routes: [
TypedGoRoute<WebEngineHardeningGroupRoute>(
name: 'WebEngineHardeningGroupRoute',
path: 'group/:group',
),
],
),
TypedGoRoute<DohSettingsRoute>(name: 'DohSettingsRoute', path: 'doh'),
TypedGoRoute<FingerprintSettingsRoute>(
name: 'FingerprintSettingsRoute',
path: 'fingerprint',
),
TypedGoRoute<LocaleSettingsRoute>(
name: 'LocaleSettingsRoute',
path: 'locales',
),
TypedGoRoute<AddonCollectionRoute>(
name: 'AddonCollectionRoute',
path: 'addon_collection',
),
TypedGoRoute<TrackingProtectionExceptionsRoute>(
name: 'TrackingProtectionExceptionsRoute',
path: 'tracking_protection_exceptions',
),
TypedGoRoute<CustomTrackingProtectionRoute>(
name: 'CustomTrackingProtectionRoute',
path: 'custom_tracking_protection',
),
TypedGoRoute<ErrorLogsRoute>(name: 'ErrorLogsRoute', path: 'error_logs'),
TypedGoRoute<SyncSettingsRoute>(name: 'SyncSettingsRoute', path: 'sync'),
TypedGoRoute<UrlCleanerSettingsRoute>(
name: 'UrlCleanerSettingsRoute',
path: 'url_cleaner',
),
TypedGoRoute<UnshortenerSettingsRoute>(
name: 'UnshortenerSettingsRoute',
path: 'unshortener',
),
TypedGoRoute<ContextualToolbarSettingsRoute>(
name: 'ContextualToolbarSettingsRoute',
path: 'contextual_toolbar',
),
],
)
class SettingsRoute extends GoRouteData with $SettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const SettingsScreen();
}
}
class GeneralSettingsRoute extends GoRouteData with $GeneralSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const GeneralSettingsScreen();
}
}
class BrowsingSettingsRoute extends GoRouteData with $BrowsingSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const BrowsingSettingsScreen();
}
}
class PrivacySecuritySettingsRoute extends GoRouteData
with $PrivacySecuritySettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const PrivacySecuritySettingsScreen();
}
}
class ToolbarLayoutSettingsRoute extends GoRouteData
with $ToolbarLayoutSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const ToolbarLayoutSettingsScreen();
}
}
class WebContentSettingsRoute extends GoRouteData
with $WebContentSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const WebContentSettingsScreen();
}
}
class SearchSettingsRoute extends GoRouteData with $SearchSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const SearchSettingsScreen();
}
}
class ExtensionsSettingsRoute extends GoRouteData
with $ExtensionsSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const ExtensionsSettingsScreen();
}
}
class AdvancedSettingsRoute extends GoRouteData with $AdvancedSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const AdvancedSettingsScreen();
}
}
class ExperimentalSettingsRoute extends GoRouteData
with $ExperimentalSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const ExperimentalSettingsScreen();
}
}
class BangSettingsRoute extends GoRouteData with $BangSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const BangSettingsScreen();
}
}
class DohSettingsRoute extends GoRouteData with $DohSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const DohSettingsScreen();
}
}
class FingerprintSettingsRoute extends GoRouteData
with $FingerprintSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const FingerprintSettingsScreen();
}
}
class LocaleSettingsRoute extends GoRouteData with $LocaleSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const LocaleSettingsScreen();
}
}
class AddonCollectionRoute extends GoRouteData with $AddonCollectionRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const AddonCollectionScreen();
}
}
class WebEngineHardeningRoute extends GoRouteData
with $WebEngineHardeningRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const WebEngineHardeningScreen();
}
}
class WebEngineHardeningGroupRoute extends GoRouteData
with $WebEngineHardeningGroupRoute {
final String group;
const WebEngineHardeningGroupRoute({required this.group});
@override
Widget build(BuildContext context, GoRouterState state) {
return WebEngineHardeningGroupScreen(groupName: group);
}
}
class TrackingProtectionExceptionsRoute extends GoRouteData
with $TrackingProtectionExceptionsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const TrackingProtectionExceptionsScreen();
}
}
class ErrorLogsRoute extends GoRouteData with $ErrorLogsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const ErrorLogsScreen();
}
}
class CustomTrackingProtectionRoute extends GoRouteData
with $CustomTrackingProtectionRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const CustomTrackingProtectionScreen();
}
}
class SyncSettingsRoute extends GoRouteData with $SyncSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const SyncSettingsScreen();
}
}
class UrlCleanerSettingsRoute extends GoRouteData
with $UrlCleanerSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const UrlCleanerSettingsScreen();
}
}
class UnshortenerSettingsRoute extends GoRouteData
with $UnshortenerSettingsRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const UnshortenerSettingsScreen();
}
}
class ContextualToolbarSettingsRoute extends GoRouteData
with $ContextualToolbarSettingsRoute {
const ContextualToolbarSettingsRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return const ContextualToolbarSettingsScreen();
}
}
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
part of 'routes.dart';
@TypedGoRoute<TorProxyRoute>(
name: 'TorProxyRoute',
path: '/tor',
routes: [
TypedGoRoute<TorCountryPickerRoute>(
name: 'TorCountryPickerRoute',
path: 'country_picker',
),
],
)
class TorProxyRoute extends GoRouteData with $TorProxyRoute {
const TorProxyRoute();
@override
Widget build(BuildContext context, GoRouterState state) {
return const TorProxyScreen();
}
}
class TorCountryPickerRoute extends GoRouteData with $TorCountryPickerRoute {
final String title;
final String? $extra;
const TorCountryPickerRoute({required this.title, this.$extra});
@override
Widget build(BuildContext context, GoRouterState state) {
return CountryPickerScreen(title: title, selectedCountryCode: $extra);
}
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:flutter/material.dart';
/// A bottom sheet page with Material entrance and exit animations.
/// Similar to DialogPage but displays content as a modal bottom sheet.
class BottomSheetPage<T> extends Page<T> {
final WidgetBuilder builder;
final Color? barrierColor;
final bool barrierDismissible;
final String? barrierLabel;
final bool isScrollControlled;
final bool useSafeArea;
const BottomSheetPage({
required this.builder,
this.barrierColor,
this.barrierDismissible = true,
this.barrierLabel,
this.isScrollControlled = true,
this.useSafeArea = true,
super.key,
super.name,
super.arguments,
super.restorationId,
});
@override
Route<T> createRoute(BuildContext context) => ModalBottomSheetRoute<T>(
settings: this,
builder: builder,
barrierLabel:
barrierLabel ??
MaterialLocalizations.of(context).modalBarrierDismissLabel,
backgroundColor: Theme.of(context).bottomSheetTheme.modalBackgroundColor,
elevation: Theme.of(context).bottomSheetTheme.modalElevation,
shape: Theme.of(context).bottomSheetTheme.shape,
clipBehavior: Clip.antiAlias,
constraints: Theme.of(context).bottomSheetTheme.constraints,
modalBarrierColor:
barrierColor ?? Theme.of(context).bottomSheetTheme.modalBarrierColor,
isScrollControlled: isScrollControlled,
isDismissible: barrierDismissible,
useSafeArea: useSafeArea,
);
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:flutter/material.dart';
/// A dialog page with Material entrance and exit animations, modal barrier color,
/// and modal barrier behavior (dialog is dismissible with a tap on the barrier).
class DialogPage<T> extends Page<T> {
final Offset? anchorPoint;
final Color? barrierColor;
final bool barrierDismissible;
final String? barrierLabel;
final bool useSafeArea;
final CapturedThemes? themes;
final WidgetBuilder builder;
const DialogPage({
required this.builder,
this.anchorPoint,
this.barrierColor,
this.barrierDismissible = true,
this.barrierLabel,
this.useSafeArea = true,
this.themes,
super.key,
super.name,
super.arguments,
super.restorationId,
});
@override
Route<T> createRoute(BuildContext context) => DialogRoute<T>(
context: context,
settings: this,
builder: builder,
anchorPoint: anchorPoint,
barrierColor:
barrierColor ??
DialogTheme.of(context).barrierColor ??
Theme.of(context).dialogTheme.barrierColor ??
Colors.black54,
barrierDismissible: barrierDismissible,
barrierLabel: barrierLabel,
useSafeArea: useSafeArea,
themes: themes,
);
}