diff --git a/app/lib/domain/services/generic_website.g.dart b/app/lib/domain/services/generic_website.g.dart index 7d56be38..e581cc5f 100644 --- a/app/lib/domain/services/generic_website.g.dart +++ b/app/lib/domain/services/generic_website.g.dart @@ -7,7 +7,7 @@ part of 'generic_website.dart'; // ************************************************************************** String _$genericWebsiteServiceHash() => - r'19ce8c27e316d9220152f6ad9cfa8024d191c0f0'; + r'c20897405a4d31983a22d61e955ffb7430d06a54'; /// See also [GenericWebsiteService]. @ProviderFor(GenericWebsiteService) diff --git a/app/lib/features/bangs/presentation/screens/list.dart b/app/lib/features/bangs/presentation/screens/list.dart index 4d9aa0b3..843b5e39 100644 --- a/app/lib/features/bangs/presentation/screens/list.dart +++ b/app/lib/features/bangs/presentation/screens/list.dart @@ -25,6 +25,7 @@ import 'package:weblibre/features/bangs/domain/providers/bangs.dart'; import 'package:weblibre/features/bangs/presentation/widgets/bang_details.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/user/domain/repositories/general_settings.dart'; import 'package:weblibre/presentation/widgets/failure_widget.dart'; class BangListScreen extends HookConsumerWidget { @@ -61,10 +62,14 @@ class BangListScreen extends HookConsumerWidget { .read(selectedBangTriggerProvider().notifier) .setTrigger(bang.trigger); + final settings = ref.read( + generalSettingsRepositoryProvider, + ); + SearchRoute( tabType: ref.read(selectedTabTypeProvider) ?? - TabType.regular, + settings.defaultCreateTabType, ).go(context); }, ); diff --git a/app/lib/features/bangs/presentation/widgets/bang_details.dart b/app/lib/features/bangs/presentation/widgets/bang_details.dart index 7e0bec81..c31360c6 100644 --- a/app/lib/features/bangs/presentation/widgets/bang_details.dart +++ b/app/lib/features/bangs/presentation/widgets/bang_details.dart @@ -22,6 +22,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/bangs/data/models/bang_data.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; +import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/presentation/widgets/url_icon.dart'; class BangDetails extends HookConsumerWidget { @@ -86,10 +87,15 @@ class BangDetails extends HookConsumerWidget { ), onPressed: () async { final url = Uri.parse(bangData.getTemplateUrl('').origin); + final isPrivate = + ref + .read(generalSettingsRepositoryProvider) + .defaultCreateTabType == + TabType.private; await ref .read(tabRepositoryProvider.notifier) - .addTab(url: url); + .addTab(url: url, private: isPrivate); if (context.mounted) { BrowserRoute().go(context); diff --git a/app/lib/features/bangs/presentation/widgets/site_search.dart b/app/lib/features/bangs/presentation/widgets/site_search.dart index 69af54ec..aab24a16 100644 --- a/app/lib/features/bangs/presentation/widgets/site_search.dart +++ b/app/lib/features/bangs/presentation/widgets/site_search.dart @@ -21,6 +21,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:nullability/nullability.dart'; +import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/extensions/uri.dart'; import 'package:weblibre/features/bangs/data/models/bang_data.dart'; import 'package:weblibre/features/bangs/domain/providers/bangs.dart'; @@ -33,6 +34,7 @@ import 'package:weblibre/features/geckoview/features/browser/domain/providers.da import 'package:weblibre/features/geckoview/features/search/domain/providers/search_suggestions.dart'; import 'package:weblibre/features/geckoview/features/search/presentation/widgets/search_field.dart'; import 'package:weblibre/features/geckoview/features/search/presentation/widgets/search_modules/fixed_search_suggestions.dart'; +import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/presentation/hooks/listenable_callback.dart'; import 'package:weblibre/presentation/widgets/selectable_chips.dart'; import 'package:weblibre/presentation/widgets/url_icon.dart'; @@ -82,7 +84,11 @@ class SiteSearch extends HookConsumerWidget { ref.read(bottomSheetControllerProvider.notifier).dismiss(); } else if (bang != null) { final isPrivate = - ref.read(selectedTabStateProvider)?.isPrivate ?? false; + ref.read(selectedTabStateProvider)?.isPrivate ?? + ref + .read(generalSettingsRepositoryProvider) + .defaultCreateTabType == + TabType.private; final searchUri = bang.getTemplateUrl(value); diff --git a/app/lib/features/geckoview/domain/providers.dart b/app/lib/features/geckoview/domain/providers.dart index cdd4f175..7ae7f526 100644 --- a/app/lib/features/geckoview/domain/providers.dart +++ b/app/lib/features/geckoview/domain/providers.dart @@ -32,6 +32,7 @@ import 'package:weblibre/features/geckoview/domain/providers/selected_tab.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/find_in_page/presentation/controllers/find_in_page.dart'; +import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; part 'providers.g.dart'; @@ -43,8 +44,11 @@ GeckoSelectionActionService selectionActionService(Ref ref) { service.setActions([ SearchAction((text) async { final router = await ref.read(routerProvider.future); + final settings = ref.read(generalSettingsRepositoryProvider); final route = SearchRoute( - tabType: ref.read(selectedTabTypeProvider) ?? TabType.regular, + tabType: + ref.read(selectedTabTypeProvider) ?? + settings.defaultCreateTabType, searchText: text, ); @@ -57,13 +61,19 @@ GeckoSelectionActionService selectionActionService(Ref ref) { if (defaultSearchBang != null) { final currentTab = ref.read(selectedTabStateProvider); + final isPrivate = + currentTab?.isPrivate ?? + ref + .read(generalSettingsRepositoryProvider) + .defaultCreateTabType == + TabType.private; await ref .read(tabRepositoryProvider.notifier) .addTab( url: defaultSearchBang.getTemplateUrl(text), parentId: currentTab?.id, - private: currentTab?.isPrivate ?? false, + private: isPrivate, ); } else { logger.e('No default search bang found'); diff --git a/app/lib/features/geckoview/domain/providers.g.dart b/app/lib/features/geckoview/domain/providers.g.dart index e8888022..354ab754 100644 --- a/app/lib/features/geckoview/domain/providers.g.dart +++ b/app/lib/features/geckoview/domain/providers.g.dart @@ -7,7 +7,7 @@ part of 'providers.dart'; // ************************************************************************** String _$selectionActionServiceHash() => - r'ab6aeb105020a16ed24911acd73ba8553e5b3905'; + r'f356434878b46fd443c8e3a8a48cf07c34a63b70'; /// See also [selectionActionService]. @ProviderFor(selectionActionService) diff --git a/app/lib/features/geckoview/domain/repositories/tab.dart b/app/lib/features/geckoview/domain/repositories/tab.dart index e3cda26d..1420537e 100644 --- a/app/lib/features/geckoview/domain/repositories/tab.dart +++ b/app/lib/features/geckoview/domain/repositories/tab.dart @@ -25,6 +25,7 @@ import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:nullability/nullability.dart'; import 'package:riverpod_annotation/riverpod_annotation.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/providers.dart'; @@ -38,6 +39,7 @@ import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selec 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/share_intent/domain/entities/shared_content.dart'; +import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/utils/debouncer.dart'; part 'tab.g.dart'; @@ -64,7 +66,7 @@ class TabRepository extends _$TabRepository { String? parentId, LoadUrlFlags flags = LoadUrlFlags.NONE, Source source = Internal.newTab, - bool private = false, + required bool private, HistoryMetadataKey? historyMetadata, Map? additionalHeaders, }) async { @@ -222,7 +224,7 @@ class TabRepository extends _$TabRepository { if (availableContainers.isNotEmpty) { //Last resort push new tab to avoid any authenticated tab is selected // ignore: avoid_redundant_argument_values - await addTab(selectTab: true); + await addTab(selectTab: true, private: false); } } @@ -345,9 +347,17 @@ class TabRepository extends _$TabRepository { engineBoundIntentStreamProvider, (previous, next) { next.whenData((value) async { + final isPrivate = + ref + .read(generalSettingsRepositoryProvider) + .defaultIntentTabType == + TabType.private; + switch (value) { case SharedUrl(): - _tabFromIntent.add(await addTab(url: value.url)); + _tabFromIntent.add( + await addTab(url: value.url, private: isPrivate), + ); case SharedText(): final defaultSearchBang = ref.read(selectedBangDataProvider()) ?? @@ -356,6 +366,7 @@ class TabRepository extends _$TabRepository { _tabFromIntent.add( await addTab( url: defaultSearchBang?.getTemplateUrl(value.text), + private: isPrivate, ), ); } diff --git a/app/lib/features/geckoview/domain/repositories/tab.g.dart b/app/lib/features/geckoview/domain/repositories/tab.g.dart index 7f6e6544..b964f5b6 100644 --- a/app/lib/features/geckoview/domain/repositories/tab.g.dart +++ b/app/lib/features/geckoview/domain/repositories/tab.g.dart @@ -6,7 +6,7 @@ part of 'tab.dart'; // RiverpodGenerator // ************************************************************************** -String _$tabRepositoryHash() => r'4b0dc3bbfd3635398009d655d811a4a35dbfb503'; +String _$tabRepositoryHash() => r'fccaa6521e1bad7e8b3f82e6d611469348dc8667'; /// See also [TabRepository]. @ProviderFor(TabRepository) diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart index 95bacca3..de9df4d9 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart @@ -207,9 +207,18 @@ class BrowserBottomAppBar extends HookConsumerWidget { ), MenuItemButton( onPressed: () async { + final isPrivate = + ref + .read(generalSettingsRepositoryProvider) + .defaultCreateTabType == + TabType.private; + await ref .read(tabRepositoryProvider.notifier) - .addTab(url: ref.read(docsUriProvider)); + .addTab( + url: ref.read(docsUriProvider), + private: isPrivate, + ); }, leadingIcon: const Icon(Icons.help), child: const Text('Help and feedback'), @@ -257,10 +266,17 @@ class BrowserBottomAppBar extends HookConsumerWidget { ), MenuItemButton( onPressed: () async { + final isPrivate = + ref + .read(generalSettingsRepositoryProvider) + .defaultCreateTabType == + TabType.private; + await ref .read(tabRepositoryProvider.notifier) .addTab( url: Uri.parse('https://addons.mozilla.org'), + private: isPrivate, ); }, leadingIcon: const Icon(MdiIcons.puzzlePlus), diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart index 57df47cf..4dc89ad7 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart @@ -313,13 +313,16 @@ class _BrowserViewState extends ConsumerState widget.suggestionTimeout) { //Don't do anything if a child route is active if (GoRouterState.of(context).topRoute?.name == BrowserRoute.name) { + final settings = ref.read(generalSettingsRepositoryProvider); + unawaited( showSuggestNewTabMessage( context, onAdd: (searchText) async { await SearchRoute( tabType: - ref.read(selectedTabTypeProvider) ?? TabType.regular, + ref.read(selectedTabTypeProvider) ?? + settings.defaultCreateTabType, searchText: searchText ?? SearchRoute.emptySearchText, ).push(context); }, diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tab.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tab.dart index 675dedc0..ce65c70d 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tab.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tab.dart @@ -215,7 +215,7 @@ class ViewTabSheetWidget extends HookConsumerWidget { onTap: () async { final tabId = await ref .read(tabRepositoryProvider.notifier) - .addTab(url: initialTabState.url); + .addTab(url: initialTabState.url, private: false); if (context.mounted) { //save reference before pop `ref` gets disposed diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart index 5d31e5af..1a394816 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart @@ -605,8 +605,12 @@ class ViewTabsSheetWidget extends HookConsumerWidget { ), child: FloatingActionButton.small( onPressed: () async { + final settings = ref.read(generalSettingsRepositoryProvider); + await SearchRoute( - tabType: ref.read(selectedTabTypeProvider) ?? TabType.regular, + tabType: + ref.read(selectedTabTypeProvider) ?? + settings.defaultCreateTabType, ).push(context); onClose(); @@ -782,8 +786,12 @@ class ViewTabTreesSheetWidget extends HookConsumerWidget { ), child: FloatingActionButton.small( onPressed: () async { + final settings = ref.read(generalSettingsRepositoryProvider); + await SearchRoute( - tabType: ref.read(selectedTabTypeProvider) ?? TabType.regular, + tabType: + ref.read(selectedTabTypeProvider) ?? + settings.defaultCreateTabType, ).push(context); onClose(); diff --git a/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_image_new_tab.dart b/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_image_new_tab.dart index 3a7b1eaa..b2c7e95e 100644 --- a/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_image_new_tab.dart +++ b/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_image_new_tab.dart @@ -22,9 +22,11 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.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:weblibre/core/routing/routes.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/contextmenu/extensions/hit_result.dart'; +import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/utils/ui_helper.dart'; class OpenImageInNewTab extends HookConsumerWidget { @@ -43,6 +45,10 @@ class OpenImageInNewTab extends HookConsumerWidget { title: const Text('Open image in new tab'), onTap: () async { final currentTab = ref.read(selectedTabStateProvider); + final isPrivate = + currentTab?.isPrivate ?? + ref.read(generalSettingsRepositoryProvider).defaultCreateTabType == + TabType.private; final tabId = await ref .read(tabRepositoryProvider.notifier) @@ -50,7 +56,7 @@ class OpenImageInNewTab extends HookConsumerWidget { url: hitResult.tryGetLink(), parentId: currentTab?.id, selectTab: false, - private: currentTab?.isPrivate ?? false, + private: isPrivate, ); if (context.mounted) { diff --git a/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_new_tab.dart b/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_new_tab.dart index eec24b32..0cba7694 100644 --- a/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_new_tab.dart +++ b/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_new_tab.dart @@ -22,9 +22,11 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.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:weblibre/core/routing/routes.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/contextmenu/extensions/hit_result.dart'; +import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/utils/ui_helper.dart'; class OpenInNewTab extends HookConsumerWidget { @@ -43,6 +45,10 @@ class OpenInNewTab extends HookConsumerWidget { title: const Text('Open in new tab'), onTap: () async { final currentTab = ref.read(selectedTabStateProvider); + final isPrivate = + currentTab?.isPrivate ?? + ref.read(generalSettingsRepositoryProvider).defaultCreateTabType == + TabType.private; final tabId = await ref .read(tabRepositoryProvider.notifier) @@ -50,7 +56,7 @@ class OpenInNewTab extends HookConsumerWidget { url: hitResult.tryGetLink(), parentId: currentTab?.id, selectTab: false, - private: currentTab?.isPrivate ?? false, + private: isPrivate, ); if (context.mounted) { diff --git a/app/lib/features/onboarding/presentation/onboarding.dart b/app/lib/features/onboarding/presentation/onboarding.dart index 5fe51ccd..ac32dd5a 100644 --- a/app/lib/features/onboarding/presentation/onboarding.dart +++ b/app/lib/features/onboarding/presentation/onboarding.dart @@ -144,7 +144,10 @@ class OnboardingScreen extends HookConsumerWidget { await ref .read(tabRepositoryProvider.notifier) - .addTab(url: ref.read(docsUriProvider)); + .addTab( + url: ref.read(docsUriProvider), + private: false, + ); ref.invalidate(routerProvider); }, diff --git a/app/lib/features/settings/presentation/screens/general_settings.dart b/app/lib/features/settings/presentation/screens/general_settings.dart index abe3fc10..6dd191e5 100644 --- a/app/lib/features/settings/presentation/screens/general_settings.dart +++ b/app/lib/features/settings/presentation/screens/general_settings.dart @@ -23,6 +23,7 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.dart import 'package:google_fonts/google_fonts.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:nullability/nullability.dart'; +import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/search/domain/entities/abstract/i_search_suggestion_provider.dart'; import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart'; import 'package:weblibre/features/settings/presentation/widgets/bang_icon.dart'; @@ -97,6 +98,116 @@ class GeneralSettingsScreen extends HookConsumerWidget { ], ), ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ListTile( + title: Text('New Tab Default'), + subtitle: Text( + 'Choose the default type for manually created tabs', + ), + leading: Icon(MdiIcons.tab), + contentPadding: EdgeInsets.zero, + ), + Center( + child: SegmentedButton( + showSelectedIcon: false, + segments: const [ + ButtonSegment( + value: TabType.regular, + label: Text('Regular'), + icon: Icon(MdiIcons.tab), + ), + ButtonSegment( + value: TabType.private, + label: Text('Private'), + icon: Icon(MdiIcons.tabUnselected), + ), + ], + selected: {generalSettings.defaultCreateTabType}, + onSelectionChanged: (value) async { + await ref + .read( + saveGeneralSettingsControllerProvider.notifier, + ) + .save( + (currentSettings) => currentSettings.copyWith + .defaultCreateTabType(value.first), + ); + }, + style: switch (generalSettings.defaultCreateTabType) { + TabType.regular => null, + TabType.private => SegmentedButton.styleFrom( + selectedBackgroundColor: const Color(0x648000D7), + ), + TabType.child => null, + }, + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ListTile( + title: Text('External Link Handling'), + subtitle: Text( + 'Choose how external links open in WebLibre', + ), + leading: Icon(MdiIcons.tabPlus), + contentPadding: EdgeInsets.zero, + ), + Center( + child: SegmentedButton( + showSelectedIcon: false, + segments: const [ + ButtonSegment( + value: TabType.regular, + label: Text('Regular'), + icon: Icon(MdiIcons.tab), + ), + ButtonSegment( + value: TabType.private, + label: Text('Private'), + icon: Icon(MdiIcons.tabUnselected), + ), + ], + selected: {generalSettings.defaultIntentTabType}, + onSelectionChanged: (value) async { + await ref + .read( + saveGeneralSettingsControllerProvider.notifier, + ) + .save( + (currentSettings) => currentSettings.copyWith + .defaultIntentTabType(value.first), + ); + }, + style: switch (generalSettings.defaultIntentTabType) { + TabType.regular => null, + TabType.private => SegmentedButton.styleFrom( + selectedBackgroundColor: const Color(0x648000D7), + ), + TabType.child => null, + }, + ), + ), + ], + ), + ), const Padding( padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), child: Column( diff --git a/app/lib/features/user/data/models/general_settings.dart b/app/lib/features/user/data/models/general_settings.dart index 78c53aa1..08801338 100644 --- a/app/lib/features/user/data/models/general_settings.dart +++ b/app/lib/features/user/data/models/general_settings.dart @@ -21,6 +21,7 @@ import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:fast_equatable/fast_equatable.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/search/domain/entities/abstract/i_search_suggestion_provider.dart'; part 'general_settings.g.dart'; @@ -54,6 +55,8 @@ class GeneralSettings with FastEquatable { final bool createChildTabsOption; final bool showExtensionShortcut; final bool enableLocalAiFeatures; + final TabType defaultCreateTabType; + final TabType defaultIntentTabType; final bool proxyPrivateTabsTor; @@ -68,6 +71,8 @@ class GeneralSettings with FastEquatable { required this.proxyPrivateTabsTor, required this.showExtensionShortcut, required this.enableLocalAiFeatures, + required this.defaultCreateTabType, + required this.defaultIntentTabType, }); GeneralSettings.withDefaults({ @@ -81,6 +86,8 @@ class GeneralSettings with FastEquatable { bool? proxyPrivateTabsTor, bool? showExtensionShortcut, bool? enableLocalAiFeatures, + TabType? defaultCreateTabType, + TabType? defaultIntentTabType, }) : themeMode = themeMode ?? ThemeMode.dark, enableReadability = enableReadability ?? true, enforceReadability = enforceReadability ?? false, @@ -90,7 +97,9 @@ class GeneralSettings with FastEquatable { createChildTabsOption = createChildTabsOption ?? false, proxyPrivateTabsTor = proxyPrivateTabsTor ?? false, showExtensionShortcut = showExtensionShortcut ?? false, - enableLocalAiFeatures = enableLocalAiFeatures ?? true; + enableLocalAiFeatures = enableLocalAiFeatures ?? true, + defaultCreateTabType = defaultCreateTabType ?? TabType.regular, + defaultIntentTabType = defaultIntentTabType ?? TabType.regular; factory GeneralSettings.fromJson(Map json) => _$GeneralSettingsFromJson(json); @@ -107,7 +116,9 @@ class GeneralSettings with FastEquatable { defaultSearchSuggestionsProvider, createChildTabsOption, showExtensionShortcut, - proxyPrivateTabsTor, enableLocalAiFeatures, + defaultCreateTabType, + defaultIntentTabType, + proxyPrivateTabsTor, ]; } diff --git a/app/lib/features/user/data/models/general_settings.g.dart b/app/lib/features/user/data/models/general_settings.g.dart index f0aeb8d0..3bd31280 100644 --- a/app/lib/features/user/data/models/general_settings.g.dart +++ b/app/lib/features/user/data/models/general_settings.g.dart @@ -31,6 +31,10 @@ abstract class _$GeneralSettingsCWProxy { GeneralSettings enableLocalAiFeatures(bool enableLocalAiFeatures); + GeneralSettings defaultCreateTabType(TabType defaultCreateTabType); + + GeneralSettings defaultIntentTabType(TabType defaultIntentTabType); + /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `GeneralSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support. /// /// Usage @@ -48,6 +52,8 @@ abstract class _$GeneralSettingsCWProxy { bool proxyPrivateTabsTor, bool showExtensionShortcut, bool enableLocalAiFeatures, + TabType defaultCreateTabType, + TabType defaultIntentTabType, }); } @@ -98,6 +104,14 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy { GeneralSettings enableLocalAiFeatures(bool enableLocalAiFeatures) => this(enableLocalAiFeatures: enableLocalAiFeatures); + @override + GeneralSettings defaultCreateTabType(TabType defaultCreateTabType) => + this(defaultCreateTabType: defaultCreateTabType); + + @override + GeneralSettings defaultIntentTabType(TabType defaultIntentTabType) => + this(defaultIntentTabType: defaultIntentTabType); + @override /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `GeneralSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support. /// @@ -116,6 +130,8 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy { Object? proxyPrivateTabsTor = const $CopyWithPlaceholder(), Object? showExtensionShortcut = const $CopyWithPlaceholder(), Object? enableLocalAiFeatures = const $CopyWithPlaceholder(), + Object? defaultCreateTabType = const $CopyWithPlaceholder(), + Object? defaultIntentTabType = const $CopyWithPlaceholder(), }) { return GeneralSettings( themeMode: themeMode == const $CopyWithPlaceholder() @@ -164,6 +180,14 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy { ? _value.enableLocalAiFeatures // ignore: cast_nullable_to_non_nullable : enableLocalAiFeatures as bool, + defaultCreateTabType: defaultCreateTabType == const $CopyWithPlaceholder() + ? _value.defaultCreateTabType + // ignore: cast_nullable_to_non_nullable + : defaultCreateTabType as TabType, + defaultIntentTabType: defaultIntentTabType == const $CopyWithPlaceholder() + ? _value.defaultIntentTabType + // ignore: cast_nullable_to_non_nullable + : defaultIntentTabType as TabType, ); } } @@ -196,6 +220,14 @@ GeneralSettings _$GeneralSettingsFromJson(Map json) => proxyPrivateTabsTor: json['proxyPrivateTabsTor'] as bool?, showExtensionShortcut: json['showExtensionShortcut'] as bool?, enableLocalAiFeatures: json['enableLocalAiFeatures'] as bool?, + defaultCreateTabType: $enumDecodeNullable( + _$TabTypeEnumMap, + json['defaultCreateTabType'], + ), + defaultIntentTabType: $enumDecodeNullable( + _$TabTypeEnumMap, + json['defaultIntentTabType'], + ), ); Map _$GeneralSettingsToJson(GeneralSettings instance) => @@ -213,6 +245,8 @@ Map _$GeneralSettingsToJson(GeneralSettings instance) => 'createChildTabsOption': instance.createChildTabsOption, 'showExtensionShortcut': instance.showExtensionShortcut, 'enableLocalAiFeatures': instance.enableLocalAiFeatures, + 'defaultCreateTabType': _$TabTypeEnumMap[instance.defaultCreateTabType]!, + 'defaultIntentTabType': _$TabTypeEnumMap[instance.defaultIntentTabType]!, 'proxyPrivateTabsTor': instance.proxyPrivateTabsTor, }; @@ -238,3 +272,9 @@ const _$SearchSuggestionProvidersEnumMap = { SearchSuggestionProviders.kagi: 'kagi', SearchSuggestionProviders.qwant: 'qwant', }; + +const _$TabTypeEnumMap = { + TabType.regular: 'regular', + TabType.private: 'private', + TabType.child: 'child', +}; diff --git a/app/lib/features/user/domain/repositories/general_settings.dart b/app/lib/features/user/domain/repositories/general_settings.dart index 504e3f69..b2e8657f 100644 --- a/app/lib/features/user/domain/repositories/general_settings.dart +++ b/app/lib/features/user/domain/repositories/general_settings.dart @@ -83,6 +83,14 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository { DriftSqlType.bool, db.typeMapping, ), + 'defaultCreateTabType': settings['defaultCreateTabType']?.readAs( + DriftSqlType.string, + db.typeMapping, + ), + 'defaultIntentTabType': settings['defaultIntentTabType']?.readAs( + DriftSqlType.string, + db.typeMapping, + ), }); } diff --git a/app/lib/features/user/domain/repositories/general_settings.g.dart b/app/lib/features/user/domain/repositories/general_settings.g.dart index 89a05795..42ecc406 100644 --- a/app/lib/features/user/domain/repositories/general_settings.g.dart +++ b/app/lib/features/user/domain/repositories/general_settings.g.dart @@ -7,7 +7,7 @@ part of 'general_settings.dart'; // ************************************************************************** String _$generalSettingsRepositoryHash() => - r'b3c60b14cdfba7e0bcf4817addc8295b5281a9b5'; + r'538e03e9eff8beefc0918614c86d8919224c3039'; /// See also [GeneralSettingsRepository]. @ProviderFor(GeneralSettingsRepository) diff --git a/app/lib/features/web_feed/domain/services/feed_reader.g.dart b/app/lib/features/web_feed/domain/services/feed_reader.g.dart index b49b2d57..b72b20e5 100644 --- a/app/lib/features/web_feed/domain/services/feed_reader.g.dart +++ b/app/lib/features/web_feed/domain/services/feed_reader.g.dart @@ -6,7 +6,7 @@ part of 'feed_reader.dart'; // RiverpodGenerator // ************************************************************************** -String _$feedReaderHash() => r'301e01fa831c58723dc0ab118496f4fb34c38610'; +String _$feedReaderHash() => r'5d1ca364fe7ad702628a7f2bbd3e706876bc3111'; /// See also [FeedReader]. @ProviderFor(FeedReader) diff --git a/app/lib/features/web_feed/presentation/screens/feed_article.dart b/app/lib/features/web_feed/presentation/screens/feed_article.dart index 3145503a..b1cb6ff4 100644 --- a/app/lib/features/web_feed/presentation/screens/feed_article.dart +++ b/app/lib/features/web_feed/presentation/screens/feed_article.dart @@ -26,6 +26,7 @@ import 'package:nullability/nullability.dart'; import 'package:weblibre/core/providers/format.dart'; import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; +import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/features/web_feed/data/models/feed_link.dart'; import 'package:weblibre/features/web_feed/domain/providers.dart'; import 'package:weblibre/features/web_feed/extensions/atom.dart'; @@ -193,9 +194,18 @@ class FeedArticleScreen extends HookConsumerWidget { if (articleLink != null) IconButton( onPressed: () async { + final isPrivate = + ref + .read(generalSettingsRepositoryProvider) + .defaultCreateTabType == + TabType.private; + await ref .read(tabRepositoryProvider.notifier) - .addTab(url: articleLink.uri); + .addTab( + url: articleLink.uri, + private: isPrivate, + ); if (context.mounted) { BrowserRoute().go(context); @@ -231,9 +241,15 @@ class FeedArticleScreen extends HookConsumerWidget { onTapLink: (text, href, title) async { if (href.mapNotNull(Uri.tryParse) case final Uri url) { + final isPrivate = + ref + .read(generalSettingsRepositoryProvider) + .defaultCreateTabType == + TabType.private; + await ref .read(tabRepositoryProvider.notifier) - .addTab(url: url); + .addTab(url: url, private: isPrivate); if (context.mounted) { showTabOpenedMessage(