setting to show link open prompt

This commit is contained in:
Fabian Freund
2025-09-11 10:16:34 +02:00
parent 77cda075ca
commit 0b34383510
16 changed files with 312 additions and 86 deletions
+19
View File
@@ -54,6 +54,10 @@ part of 'routes.dart';
name: 'TabTreeRoute', name: 'TabTreeRoute',
path: 'tab_tree/:rootTabId', path: 'tab_tree/:rootTabId',
), ),
TypedGoRoute<OpenSharedContentRoute>(
name: 'OpenSharedContentRoute',
path: 'open_content',
),
], ],
) )
class BrowserRoute extends GoRouteData with _$BrowserRoute { class BrowserRoute extends GoRouteData with _$BrowserRoute {
@@ -75,9 +79,12 @@ class SearchRoute extends GoRouteData with _$SearchRoute {
//This should be nullable but isnt allowed by go_router //This should be nullable but isnt allowed by go_router
final String searchText; final String searchText;
final bool $extra;
const SearchRoute({ const SearchRoute({
required this.tabType, required this.tabType,
this.searchText = SearchRoute.emptySearchText, this.searchText = SearchRoute.emptySearchText,
this.$extra = false,
}); });
@override @override
@@ -87,6 +94,7 @@ class SearchRoute extends GoRouteData with _$SearchRoute {
initialSearchText: (searchText.isEmpty || searchText == emptySearchText) initialSearchText: (searchText.isEmpty || searchText == emptySearchText)
? null ? null
: searchText, : searchText,
launchedFromIntent: $extra,
); );
} }
} }
@@ -159,3 +167,14 @@ class TabTreeRoute extends GoRouteData with _$TabTreeRoute {
return DialogPage(builder: (_) => TabTreeDialog(rootTabId)); return DialogPage(builder: (_) => TabTreeDialog(rootTabId));
} }
} }
class OpenSharedContentRoute extends GoRouteData with _$OpenSharedContentRoute {
final Uri $extra;
const OpenSharedContentRoute(this.$extra);
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return DialogPage(builder: (_) => OpenSharedContent(sharedUrl: $extra));
}
}
+1
View File
@@ -26,6 +26,7 @@ import 'package:weblibre/features/about/presentation/screens/about.dart';
import 'package:weblibre/features/bangs/presentation/screens/categories.dart'; import 'package:weblibre/features/bangs/presentation/screens/categories.dart';
import 'package:weblibre/features/bangs/presentation/screens/list.dart'; import 'package:weblibre/features/bangs/presentation/screens/list.dart';
import 'package:weblibre/features/bangs/presentation/screens/search.dart'; import 'package:weblibre/features/bangs/presentation/screens/search.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/open_shared_content.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/tab_tree.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/browser.dart';
import 'package:weblibre/features/geckoview/features/contextmenu/extensions/hit_result.dart'; import 'package:weblibre/features/geckoview/features/contextmenu/extensions/hit_result.dart';
+38 -4
View File
@@ -333,6 +333,12 @@ RouteBase get $browserRoute => GoRouteData.$route(
factory: _$TabTreeRoute._fromState, factory: _$TabTreeRoute._fromState,
), ),
GoRouteData.$route(
path: 'open_content',
name: 'OpenSharedContentRoute',
factory: _$OpenSharedContentRoute._fromState,
),
], ],
); );
@@ -361,6 +367,7 @@ mixin _$SearchRoute on GoRouteData {
tabType: _$TabTypeEnumMap._$fromName(state.pathParameters['tabType']!)!, tabType: _$TabTypeEnumMap._$fromName(state.pathParameters['tabType']!)!,
searchText: searchText:
state.pathParameters['searchText'] ?? SearchRoute.emptySearchText, state.pathParameters['searchText'] ?? SearchRoute.emptySearchText,
$extra: state.extra as bool,
); );
SearchRoute get _self => this as SearchRoute; SearchRoute get _self => this as SearchRoute;
@@ -371,17 +378,19 @@ mixin _$SearchRoute on GoRouteData {
); );
@override @override
void go(BuildContext context) => context.go(location); void go(BuildContext context) => context.go(location, extra: _self.$extra);
@override @override
Future<T?> push<T>(BuildContext context) => context.push<T>(location); Future<T?> push<T>(BuildContext context) =>
context.push<T>(location, extra: _self.$extra);
@override @override
void pushReplacement(BuildContext context) => void pushReplacement(BuildContext context) =>
context.pushReplacement(location); context.pushReplacement(location, extra: _self.$extra);
@override @override
void replace(BuildContext context) => context.replace(location); void replace(BuildContext context) =>
context.replace(location, extra: _self.$extra);
} }
const _$TabTypeEnumMap = { const _$TabTypeEnumMap = {
@@ -552,6 +561,31 @@ mixin _$TabTreeRoute on GoRouteData {
void replace(BuildContext context) => context.replace(location); void replace(BuildContext context) => context.replace(location);
} }
mixin _$OpenSharedContentRoute on GoRouteData {
static OpenSharedContentRoute _fromState(GoRouterState state) =>
OpenSharedContentRoute(state.extra as Uri);
OpenSharedContentRoute get _self => this as OpenSharedContentRoute;
@override
String get location => GoRouteData.$location('/open_content');
@override
void go(BuildContext context) => context.go(location, extra: _self.$extra);
@override
Future<T?> push<T>(BuildContext context) =>
context.push<T>(location, extra: _self.$extra);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location, extra: _self.$extra);
@override
void replace(BuildContext context) =>
context.replace(location, extra: _self.$extra);
}
extension<T extends Enum> on Map<T, String> { extension<T extends Enum> on Map<T, String> {
T? _$fromName(String? value) => T? _$fromName(String? value) =>
entries.where((element) => element.value == value).firstOrNull?.key; entries.where((element) => element.value == value).firstOrNull?.key;
@@ -25,21 +25,16 @@ import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:nullability/nullability.dart'; import 'package:nullability/nullability.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/core/logger.dart'; import 'package:weblibre/core/logger.dart';
import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/bangs/domain/providers/bangs.dart';
import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart'; import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart';
import 'package:weblibre/features/geckoview/domain/providers.dart'; import 'package:weblibre/features/geckoview/domain/providers.dart';
import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart'; import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_list.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_list.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
import 'package:weblibre/features/geckoview/features/browser/domain/providers.dart'; import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
import 'package:weblibre/features/geckoview/features/browser/domain/providers/intent.dart';
import 'package:weblibre/features/geckoview/features/tabs/data/providers.dart'; import 'package:weblibre/features/geckoview/features/tabs/data/providers.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selected_container.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selected_container.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart';
import 'package:weblibre/features/share_intent/domain/entities/shared_content.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
import 'package:weblibre/utils/debouncer.dart'; import 'package:weblibre/utils/debouncer.dart';
part 'tab.g.dart'; part 'tab.g.dart';
@@ -68,12 +63,16 @@ class TabRepository extends _$TabRepository {
required bool private, required bool private,
HistoryMetadataKey? historyMetadata, HistoryMetadataKey? historyMetadata,
Map<String, String>? additionalHeaders, Map<String, String>? additionalHeaders,
Value<ContainerData?>? container,
bool launchedFromIntent = false,
}) async { }) async {
final selectedContainer = await ref final assingedContainer =
.read(selectedContainerProvider.notifier) container ??
.fetchData(); Value<ContainerData?>(
await ref.read(selectedContainerProvider.notifier).fetchData(),
);
return ref final newTabId = await ref
.read(tabDatabaseProvider) .read(tabDatabaseProvider)
.tabDao .tabDao
.upsertContainerTabTransactional( .upsertContainerTabTransactional(
@@ -84,7 +83,7 @@ class TabRepository extends _$TabRepository {
startLoading: startLoading, startLoading: startLoading,
parentId: parentId, parentId: parentId,
flags: flags, flags: flags,
contextId: selectedContainer?.metadata.contextualIdentity, contextId: assingedContainer.value?.metadata.contextualIdentity,
source: source, source: source,
private: private, private: private,
historyMetadata: historyMetadata, historyMetadata: historyMetadata,
@@ -92,8 +91,14 @@ class TabRepository extends _$TabRepository {
); );
}, },
parentId: Value(parentId), parentId: Value(parentId),
containerId: Value(selectedContainer?.id), containerId: Value(assingedContainer.value?.id),
); );
if (launchedFromIntent) {
_tabFromIntent.add(newTabId);
}
return newTabId;
} }
Future<String> duplicateTab({ Future<String> duplicateTab({
@@ -371,45 +376,6 @@ class TabRepository extends _$TabRepository {
}, },
); );
ref.listen(
fireImmediately: true,
engineBoundIntentStreamProvider,
(previous, next) {
next.whenData((value) async {
final isPrivate =
ref
.read(generalSettingsWithDefaultsProvider)
.defaultIntentTabType ==
TabType.private;
switch (value) {
case SharedUrl():
_tabFromIntent.add(
await addTab(url: value.url, private: isPrivate),
);
case SharedText():
final defaultSearchBang =
ref.read(selectedBangDataProvider()) ??
await ref.read(defaultSearchBangDataProvider.future);
_tabFromIntent.add(
await addTab(
url: defaultSearchBang?.getTemplateUrl(value.text),
private: isPrivate,
),
);
}
});
},
onError: (error, stackTrace) {
logger.e(
'Error listening to engineBoundIntentStreamProvider',
error: error,
stackTrace: stackTrace,
);
},
);
ref.onDispose(() async { ref.onDispose(() async {
tabStateDebouncer.dispose(); tabStateDebouncer.dispose();
await tabAddedSub.cancel(); await tabAddedSub.cancel();
@@ -6,7 +6,7 @@ part of 'tab.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$tabRepositoryHash() => r'4f562c1456eadcda7986ea2048667dd083f37618'; String _$tabRepositoryHash() => r'45af26417881bca38973cf702073b1c7857f1cfa';
/// See also [TabRepository]. /// See also [TabRepository].
@ProviderFor(TabRepository) @ProviderFor(TabRepository)
@@ -0,0 +1,99 @@
import 'package:drift/drift.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/container_chips.dart';
import 'package:weblibre/utils/form_validators.dart';
class OpenSharedContent extends HookConsumerWidget {
final Uri sharedUrl;
const OpenSharedContent({required this.sharedUrl});
@override
Widget build(BuildContext context, WidgetRef ref) {
final formKey = useMemoized(() => GlobalKey<FormState>());
final textController = useTextEditingController(text: sharedUrl.toString());
final selectedContainer = useState<ContainerData?>(null);
Future<void> openTab(bool isPrivate) async {
if (formKey.currentState?.validate() == true) {
await ref
.read(tabRepositoryProvider.notifier)
.addTab(
url: Uri.parse(textController.text),
private: isPrivate,
container: Value(selectedContainer.value),
launchedFromIntent: true,
);
}
}
return Form(
key: formKey,
child: SimpleDialog(
title: const Text('Open URL'),
children: [
Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 16.0),
child: SizedBox(
height: 48,
width: double.maxFinite,
child: ContainerChips(
displayMenu: false,
selectedContainer: selectedContainer.value,
onSelected: (container) {
selectedContainer.value = container;
},
onDeleted: (container) {
selectedContainer.value = null;
},
),
),
),
Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 16.0),
child: TextFormField(
controller: textController,
keyboardType: TextInputType.url,
minLines: 1,
maxLines: 10,
validator: (value) {
return validateUrl(
value,
onlyHttpProtocol: true,
eagerParsing: false,
);
},
),
),
ListTile(
title: const Text('Open Regular Tab'),
leading: const Icon(MdiIcons.tab),
onTap: () async {
await openTab(false);
if (context.mounted) {
context.pop();
}
},
),
ListTile(
title: const Text('Open Private Tab'),
leading: const Icon(MdiIcons.tabUnselected),
onTap: () async {
await openTab(true);
if (context.mounted) {
context.pop();
}
},
),
],
),
);
}
}
@@ -17,6 +17,7 @@
* You should have received a copy of the GNU Affero General Public License * 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/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import 'package:drift/drift.dart' show Value;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
@@ -242,6 +243,7 @@ class BrowserBottomAppBar extends HookConsumerWidget {
.addTab( .addTab(
url: ref.read(docsUriProvider), url: ref.read(docsUriProvider),
private: isPrivate, private: isPrivate,
container: const Value(null),
); );
}, },
leadingIcon: const Icon(Icons.help), leadingIcon: const Icon(Icons.help),
@@ -301,6 +303,7 @@ class BrowserBottomAppBar extends HookConsumerWidget {
.addTab( .addTab(
url: Uri.parse('https://addons.mozilla.org'), url: Uri.parse('https://addons.mozilla.org'),
private: isPrivate, private: isPrivate,
container: const Value(null),
); );
}, },
leadingIcon: const Icon(MdiIcons.puzzlePlus), leadingIcon: const Icon(MdiIcons.puzzlePlus),
@@ -44,8 +44,13 @@ import 'package:weblibre/utils/uri_parser.dart' as uri_parser;
class SearchScreen extends HookConsumerWidget { class SearchScreen extends HookConsumerWidget {
final String? initialSearchText; final String? initialSearchText;
final TabType tabType; final TabType tabType;
final bool launchedFromIntent;
const SearchScreen({required this.initialSearchText, required this.tabType}); const SearchScreen({
required this.initialSearchText,
required this.tabType,
this.launchedFromIntent = false,
});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
@@ -109,6 +114,7 @@ class SearchScreen extends HookConsumerWidget {
parentId: (selectedTabType.value == TabType.child) parentId: (selectedTabType.value == TabType.child)
? ref.read(selectedTabProvider) ? ref.read(selectedTabProvider)
: null, : null,
launchedFromIntent: launchedFromIntent,
); );
if (context.mounted) { if (context.mounted) {
@@ -212,6 +218,7 @@ class SearchScreen extends HookConsumerWidget {
(selectedTabType.value == TabType.child) (selectedTabType.value == TabType.child)
? ref.read(selectedTabProvider) ? ref.read(selectedTabProvider)
: null, : null,
launchedFromIntent: launchedFromIntent,
); );
if (context.mounted) { if (context.mounted) {
@@ -17,6 +17,7 @@
* You should have received a copy of the GNU Affero General Public License * 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/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import 'package:drift/drift.dart' show Value;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -147,6 +148,7 @@ class OnboardingScreen extends HookConsumerWidget {
.addTab( .addTab(
url: ref.read(docsUriProvider), url: ref.read(docsUriProvider),
private: false, private: false,
container: const Value(null),
); );
ref.invalidate(routerProvider); ref.invalidate(routerProvider);
@@ -175,17 +175,22 @@ class GeneralSettingsScreen extends HookConsumerWidget {
showSelectedIcon: false, showSelectedIcon: false,
segments: const [ segments: const [
ButtonSegment( ButtonSegment(
value: TabType.regular, value: TabIntentOpenSetting.ask,
label: Text('Prompt'),
icon: Icon(MdiIcons.messageQuestion),
),
ButtonSegment(
value: TabIntentOpenSetting.regular,
label: Text('Regular'), label: Text('Regular'),
icon: Icon(MdiIcons.tab), icon: Icon(MdiIcons.tab),
), ),
ButtonSegment( ButtonSegment(
value: TabType.private, value: TabIntentOpenSetting.private,
label: Text('Private'), label: Text('Private'),
icon: Icon(MdiIcons.tabUnselected), icon: Icon(MdiIcons.tabUnselected),
), ),
], ],
selected: {generalSettings.defaultIntentTabType}, selected: {generalSettings.tabIntentOpenSetting},
onSelectionChanged: (value) async { onSelectionChanged: (value) async {
await ref await ref
.read( .read(
@@ -193,15 +198,16 @@ class GeneralSettingsScreen extends HookConsumerWidget {
) )
.save( .save(
(currentSettings) => currentSettings.copyWith (currentSettings) => currentSettings.copyWith
.defaultIntentTabType(value.first), .tabIntentOpenSetting(value.first),
); );
}, },
style: switch (generalSettings.defaultIntentTabType) { style: switch (generalSettings.tabIntentOpenSetting) {
TabType.regular => null, TabIntentOpenSetting.regular => null,
TabType.private => SegmentedButton.styleFrom( TabIntentOpenSetting.private =>
selectedBackgroundColor: const Color(0x648000D7), SegmentedButton.styleFrom(
), selectedBackgroundColor: const Color(0x648000D7),
TabType.child => null, ),
TabIntentOpenSetting.ask => null,
}, },
), ),
), ),
@@ -31,6 +31,8 @@ const _fallbackAutocompleteProvider = SearchSuggestionProviders.none;
enum TabBarSwipeAction { switchLastOpened, navigateOrderedTabs } enum TabBarSwipeAction { switchLastOpened, navigateOrderedTabs }
enum TabIntentOpenSetting { regular, private, ask }
enum DeleteBrowsingDataType { enum DeleteBrowsingDataType {
tabs('Open tabs'), tabs('Open tabs'),
history('Browsing history'), history('Browsing history'),
@@ -58,7 +60,7 @@ class GeneralSettings with FastEquatable {
final bool showExtensionShortcut; final bool showExtensionShortcut;
final bool enableLocalAiFeatures; final bool enableLocalAiFeatures;
final TabType defaultCreateTabType; final TabType defaultCreateTabType;
final TabType defaultIntentTabType; final TabIntentOpenSetting tabIntentOpenSetting;
final bool proxyPrivateTabsTor; final bool proxyPrivateTabsTor;
final bool autoHideTabBar; final bool autoHideTabBar;
final TabBarSwipeAction tabBarSwipeAction; final TabBarSwipeAction tabBarSwipeAction;
@@ -75,7 +77,7 @@ class GeneralSettings with FastEquatable {
required this.showExtensionShortcut, required this.showExtensionShortcut,
required this.enableLocalAiFeatures, required this.enableLocalAiFeatures,
required this.defaultCreateTabType, required this.defaultCreateTabType,
required this.defaultIntentTabType, required this.tabIntentOpenSetting,
required this.autoHideTabBar, required this.autoHideTabBar,
required this.tabBarSwipeAction, required this.tabBarSwipeAction,
}); });
@@ -92,7 +94,7 @@ class GeneralSettings with FastEquatable {
bool? showExtensionShortcut, bool? showExtensionShortcut,
bool? enableLocalAiFeatures, bool? enableLocalAiFeatures,
TabType? defaultCreateTabType, TabType? defaultCreateTabType,
TabType? defaultIntentTabType, TabIntentOpenSetting? tabIntentOpenSetting,
bool? autoHideTabBar, bool? autoHideTabBar,
TabBarSwipeAction? tabBarSwipeAction, TabBarSwipeAction? tabBarSwipeAction,
}) : themeMode = themeMode ?? ThemeMode.dark, }) : themeMode = themeMode ?? ThemeMode.dark,
@@ -106,7 +108,7 @@ class GeneralSettings with FastEquatable {
showExtensionShortcut = showExtensionShortcut ?? false, showExtensionShortcut = showExtensionShortcut ?? false,
enableLocalAiFeatures = enableLocalAiFeatures ?? true, enableLocalAiFeatures = enableLocalAiFeatures ?? true,
defaultCreateTabType = defaultCreateTabType ?? TabType.regular, defaultCreateTabType = defaultCreateTabType ?? TabType.regular,
defaultIntentTabType = defaultIntentTabType ?? TabType.regular, tabIntentOpenSetting = tabIntentOpenSetting ?? TabIntentOpenSetting.ask,
autoHideTabBar = autoHideTabBar ?? true, autoHideTabBar = autoHideTabBar ?? true,
tabBarSwipeAction = tabBarSwipeAction =
tabBarSwipeAction ?? TabBarSwipeAction.switchLastOpened; tabBarSwipeAction ?? TabBarSwipeAction.switchLastOpened;
@@ -128,7 +130,7 @@ class GeneralSettings with FastEquatable {
showExtensionShortcut, showExtensionShortcut,
enableLocalAiFeatures, enableLocalAiFeatures,
defaultCreateTabType, defaultCreateTabType,
defaultIntentTabType, tabIntentOpenSetting,
proxyPrivateTabsTor, proxyPrivateTabsTor,
autoHideTabBar, autoHideTabBar,
tabBarSwipeAction, tabBarSwipeAction,
@@ -33,7 +33,9 @@ abstract class _$GeneralSettingsCWProxy {
GeneralSettings defaultCreateTabType(TabType defaultCreateTabType); GeneralSettings defaultCreateTabType(TabType defaultCreateTabType);
GeneralSettings defaultIntentTabType(TabType defaultIntentTabType); GeneralSettings tabIntentOpenSetting(
TabIntentOpenSetting tabIntentOpenSetting,
);
GeneralSettings autoHideTabBar(bool autoHideTabBar); GeneralSettings autoHideTabBar(bool autoHideTabBar);
@@ -57,7 +59,7 @@ abstract class _$GeneralSettingsCWProxy {
bool showExtensionShortcut, bool showExtensionShortcut,
bool enableLocalAiFeatures, bool enableLocalAiFeatures,
TabType defaultCreateTabType, TabType defaultCreateTabType,
TabType defaultIntentTabType, TabIntentOpenSetting tabIntentOpenSetting,
bool autoHideTabBar, bool autoHideTabBar,
TabBarSwipeAction tabBarSwipeAction, TabBarSwipeAction tabBarSwipeAction,
}); });
@@ -115,8 +117,9 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
this(defaultCreateTabType: defaultCreateTabType); this(defaultCreateTabType: defaultCreateTabType);
@override @override
GeneralSettings defaultIntentTabType(TabType defaultIntentTabType) => GeneralSettings tabIntentOpenSetting(
this(defaultIntentTabType: defaultIntentTabType); TabIntentOpenSetting tabIntentOpenSetting,
) => this(tabIntentOpenSetting: tabIntentOpenSetting);
@override @override
GeneralSettings autoHideTabBar(bool autoHideTabBar) => GeneralSettings autoHideTabBar(bool autoHideTabBar) =>
@@ -145,7 +148,7 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
Object? showExtensionShortcut = const $CopyWithPlaceholder(), Object? showExtensionShortcut = const $CopyWithPlaceholder(),
Object? enableLocalAiFeatures = const $CopyWithPlaceholder(), Object? enableLocalAiFeatures = const $CopyWithPlaceholder(),
Object? defaultCreateTabType = const $CopyWithPlaceholder(), Object? defaultCreateTabType = const $CopyWithPlaceholder(),
Object? defaultIntentTabType = const $CopyWithPlaceholder(), Object? tabIntentOpenSetting = const $CopyWithPlaceholder(),
Object? autoHideTabBar = const $CopyWithPlaceholder(), Object? autoHideTabBar = const $CopyWithPlaceholder(),
Object? tabBarSwipeAction = const $CopyWithPlaceholder(), Object? tabBarSwipeAction = const $CopyWithPlaceholder(),
}) { }) {
@@ -200,10 +203,10 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
? _value.defaultCreateTabType ? _value.defaultCreateTabType
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
: defaultCreateTabType as TabType, : defaultCreateTabType as TabType,
defaultIntentTabType: defaultIntentTabType == const $CopyWithPlaceholder() tabIntentOpenSetting: tabIntentOpenSetting == const $CopyWithPlaceholder()
? _value.defaultIntentTabType ? _value.tabIntentOpenSetting
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
: defaultIntentTabType as TabType, : tabIntentOpenSetting as TabIntentOpenSetting,
autoHideTabBar: autoHideTabBar == const $CopyWithPlaceholder() autoHideTabBar: autoHideTabBar == const $CopyWithPlaceholder()
? _value.autoHideTabBar ? _value.autoHideTabBar
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
@@ -248,9 +251,9 @@ GeneralSettings _$GeneralSettingsFromJson(Map<String, dynamic> json) =>
_$TabTypeEnumMap, _$TabTypeEnumMap,
json['defaultCreateTabType'], json['defaultCreateTabType'],
), ),
defaultIntentTabType: $enumDecodeNullable( tabIntentOpenSetting: $enumDecodeNullable(
_$TabTypeEnumMap, _$TabIntentOpenSettingEnumMap,
json['defaultIntentTabType'], json['tabIntentOpenSetting'],
), ),
autoHideTabBar: json['autoHideTabBar'] as bool?, autoHideTabBar: json['autoHideTabBar'] as bool?,
tabBarSwipeAction: $enumDecodeNullable( tabBarSwipeAction: $enumDecodeNullable(
@@ -276,7 +279,8 @@ Map<String, dynamic> _$GeneralSettingsToJson(
'showExtensionShortcut': instance.showExtensionShortcut, 'showExtensionShortcut': instance.showExtensionShortcut,
'enableLocalAiFeatures': instance.enableLocalAiFeatures, 'enableLocalAiFeatures': instance.enableLocalAiFeatures,
'defaultCreateTabType': _$TabTypeEnumMap[instance.defaultCreateTabType]!, 'defaultCreateTabType': _$TabTypeEnumMap[instance.defaultCreateTabType]!,
'defaultIntentTabType': _$TabTypeEnumMap[instance.defaultIntentTabType]!, 'tabIntentOpenSetting':
_$TabIntentOpenSettingEnumMap[instance.tabIntentOpenSetting]!,
'proxyPrivateTabsTor': instance.proxyPrivateTabsTor, 'proxyPrivateTabsTor': instance.proxyPrivateTabsTor,
'autoHideTabBar': instance.autoHideTabBar, 'autoHideTabBar': instance.autoHideTabBar,
'tabBarSwipeAction': _$TabBarSwipeActionEnumMap[instance.tabBarSwipeAction]!, 'tabBarSwipeAction': _$TabBarSwipeActionEnumMap[instance.tabBarSwipeAction]!,
@@ -311,6 +315,12 @@ const _$TabTypeEnumMap = {
TabType.child: 'child', TabType.child: 'child',
}; };
const _$TabIntentOpenSettingEnumMap = {
TabIntentOpenSetting.regular: 'regular',
TabIntentOpenSetting.private: 'private',
TabIntentOpenSetting.ask: 'ask',
};
const _$TabBarSwipeActionEnumMap = { const _$TabBarSwipeActionEnumMap = {
TabBarSwipeAction.switchLastOpened: 'switchLastOpened', TabBarSwipeAction.switchLastOpened: 'switchLastOpened',
TabBarSwipeAction.navigateOrderedTabs: 'navigateOrderedTabs', TabBarSwipeAction.navigateOrderedTabs: 'navigateOrderedTabs',
@@ -88,7 +88,7 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository {
DriftSqlType.string, DriftSqlType.string,
db.typeMapping, db.typeMapping,
), ),
'defaultIntentTabType': settings['defaultIntentTabType']?.readAs( 'tabIntentOpenSetting': settings['tabIntentOpenSetting']?.readAs(
DriftSqlType.string, DriftSqlType.string,
db.typeMapping, db.typeMapping,
), ),
@@ -27,7 +27,7 @@ final generalSettingsWithDefaultsProvider =
typedef GeneralSettingsWithDefaultsRef = typedef GeneralSettingsWithDefaultsRef =
AutoDisposeProviderRef<GeneralSettings>; AutoDisposeProviderRef<GeneralSettings>;
String _$generalSettingsRepositoryHash() => String _$generalSettingsRepositoryHash() =>
r'e311760a7773109a90c28275262883fcca348bbb'; r'7dd57d726d4bf86e04cb33da9b7a1399d49f7915';
/// See also [GeneralSettingsRepository]. /// See also [GeneralSettingsRepository].
@ProviderFor(GeneralSettingsRepository) @ProviderFor(GeneralSettingsRepository)
@@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:drift/drift.dart' show Value;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_markdown/flutter_markdown.dart';
@@ -205,6 +206,7 @@ class FeedArticleScreen extends HookConsumerWidget {
.addTab( .addTab(
url: articleLink.uri, url: articleLink.uri,
private: isPrivate, private: isPrivate,
container: const Value(null),
); );
if (context.mounted) { if (context.mounted) {
+75
View File
@@ -19,8 +19,18 @@
*/ */
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/core/providers/router.dart'; import 'package:weblibre/core/providers/router.dart';
import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/domain/services/app_initialization.dart'; import 'package:weblibre/domain/services/app_initialization.dart';
import 'package:weblibre/features/bangs/domain/providers/bangs.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
import 'package:weblibre/features/geckoview/features/browser/domain/providers.dart';
import 'package:weblibre/features/geckoview/features/browser/domain/providers/intent.dart';
import 'package:weblibre/features/share_intent/domain/entities/shared_content.dart';
import 'package:weblibre/features/user/data/models/general_settings.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
import 'package:weblibre/presentation/widgets/failure_widget.dart'; import 'package:weblibre/presentation/widgets/failure_widget.dart';
class MainApp extends HookConsumerWidget { class MainApp extends HookConsumerWidget {
@@ -40,6 +50,71 @@ class MainApp extends HookConsumerWidget {
final initializationResult = ref.watch(appInitializationServiceProvider); final initializationResult = ref.watch(appInitializationServiceProvider);
final router = ref.watch(routerProvider); final router = ref.watch(routerProvider);
ref.listen(
engineBoundIntentStreamProvider,
(previous, next) {
next.whenData((sharedContent) async {
final router = await ref.read(routerProvider.future);
final settings = ref.read(generalSettingsWithDefaultsProvider);
switch (settings.tabIntentOpenSetting) {
case TabIntentOpenSetting.regular:
case TabIntentOpenSetting.private:
switch (sharedContent) {
case SharedUrl():
await ref
.read(tabRepositoryProvider.notifier)
.addTab(
url: sharedContent.url,
private:
settings.tabIntentOpenSetting ==
TabIntentOpenSetting.private,
launchedFromIntent: true,
);
case SharedText():
final defaultSearchBang =
ref.read(selectedBangDataProvider()) ??
await ref.read(defaultSearchBangDataProvider.future);
await ref
.read(tabRepositoryProvider.notifier)
.addTab(
url: defaultSearchBang?.getTemplateUrl(
sharedContent.text,
),
private:
settings.tabIntentOpenSetting ==
TabIntentOpenSetting.private,
launchedFromIntent: true,
);
}
case TabIntentOpenSetting.ask:
switch (sharedContent) {
case SharedUrl():
final route = OpenSharedContentRoute(sharedContent.url);
await router.push(route.location, extra: route.$extra);
case SharedText():
final route = SearchRoute(
tabType:
ref.read(selectedTabTypeProvider) ??
settings.defaultCreateTabType,
searchText: sharedContent.text,
$extra: true, //launched from intent
);
await router.push(route.location);
}
}
});
},
onError: (error, stackTrace) {
logger.e(
'Error listening to engineBoundIntentStreamProvider',
error: error,
stackTrace: stackTrace,
);
},
);
return initializationResult.fold( return initializationResult.fold(
(initializationState) { (initializationState) {
if (!initializationState.initialized) { if (!initializationState.initialized) {