reorganize tab bar menus; readerview button visibility via setting;
This commit is contained in:
+72
-84
@@ -24,12 +24,11 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.dart
|
|||||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
|
||||||
import 'package:weblibre/core/providers/defaults.dart';
|
import 'package:weblibre/core/providers/defaults.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart';
|
import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/domain/entities/states/readerable.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/providers.dart';
|
import 'package:weblibre/features/geckoview/domain/providers.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/providers/desktop_mode.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_session.dart';
|
import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
|
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
|
||||||
@@ -40,8 +39,8 @@ import 'package:weblibre/features/geckoview/features/browser/presentation/widget
|
|||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/extension_badge_icon.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/extension_badge_icon.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/extension_shortcut_menu.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/extension_shortcut_menu.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_creation_menu.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_creation_menu.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_menu.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tabs_action_button.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tabs_action_button.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.dart';
|
|
||||||
import 'package:weblibre/features/geckoview/features/readerview/presentation/controllers/readerable.dart';
|
import 'package:weblibre/features/geckoview/features/readerview/presentation/controllers/readerable.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/readerview/presentation/widgets/reader_button.dart';
|
import 'package:weblibre/features/geckoview/features/readerview/presentation/widgets/reader_button.dart';
|
||||||
import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
|
import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
|
||||||
@@ -62,6 +61,7 @@ class BrowserBottomAppBar extends HookConsumerWidget {
|
|||||||
|
|
||||||
final tabMenuController = useMenuController();
|
final tabMenuController = useMenuController();
|
||||||
final extensionMenuController = useMenuController();
|
final extensionMenuController = useMenuController();
|
||||||
|
final hamburgerMenuController = useMenuController();
|
||||||
final trippleDotMenuController = useMenuController();
|
final trippleDotMenuController = useMenuController();
|
||||||
|
|
||||||
final selectedTabId = ref.watch(selectedTabProvider);
|
final selectedTabId = ref.watch(selectedTabProvider);
|
||||||
@@ -138,7 +138,48 @@ class BrowserBottomAppBar extends HookConsumerWidget {
|
|||||||
: null,
|
: null,
|
||||||
actions: [
|
actions: [
|
||||||
if (selectedTabId != null && displayedSheet is! ViewTabsSheet)
|
if (selectedTabId != null && displayedSheet is! ViewTabsSheet)
|
||||||
ReaderButton(),
|
Consumer(
|
||||||
|
builder: (context, ref, child) {
|
||||||
|
final tabBarReaderView = ref.watch(
|
||||||
|
generalSettingsWithDefaultsProvider.select(
|
||||||
|
(value) => value.tabBarReaderView,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final readerabilityStateActive = ref.watch(
|
||||||
|
selectedTabStateProvider.select(
|
||||||
|
(state) =>
|
||||||
|
(state?.readerableState ?? ReaderableState.$default())
|
||||||
|
.active,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Visibility(
|
||||||
|
visible: tabBarReaderView || readerabilityStateActive,
|
||||||
|
child: ReaderButton(
|
||||||
|
buttonBuilder: (isLoading, readerActive, icon) => InkWell(
|
||||||
|
onTap: isLoading
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
await ref
|
||||||
|
.read(
|
||||||
|
readerableScreenControllerProvider
|
||||||
|
.notifier,
|
||||||
|
)
|
||||||
|
.toggleReaderView(!readerActive);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 15.0,
|
||||||
|
horizontal: 8.0,
|
||||||
|
),
|
||||||
|
child: icon,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
if (showExtensionShortcut)
|
if (showExtensionShortcut)
|
||||||
ExtensionShortcutMenu(
|
ExtensionShortcutMenu(
|
||||||
controller: extensionMenuController,
|
controller: extensionMenuController,
|
||||||
@@ -153,6 +194,27 @@ class BrowserBottomAppBar extends HookConsumerWidget {
|
|||||||
icon: const Icon(MdiIcons.puzzle),
|
icon: const Icon(MdiIcons.puzzle),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (selectedTabId != null)
|
||||||
|
TabMenu(
|
||||||
|
controller: trippleDotMenuController,
|
||||||
|
selectedTabId: selectedTabId,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
if (trippleDotMenuController.isOpen) {
|
||||||
|
trippleDotMenuController.close();
|
||||||
|
} else {
|
||||||
|
trippleDotMenuController.open();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 8.0,
|
||||||
|
vertical: 15.0,
|
||||||
|
),
|
||||||
|
child: Icon(MdiIcons.dotsHorizontal),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
TabCreationMenu(
|
TabCreationMenu(
|
||||||
controller: tabMenuController,
|
controller: tabMenuController,
|
||||||
selectedTabId: selectedTabId,
|
selectedTabId: selectedTabId,
|
||||||
@@ -187,7 +249,7 @@ class BrowserBottomAppBar extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
MenuAnchor(
|
MenuAnchor(
|
||||||
controller: trippleDotMenuController,
|
controller: hamburgerMenuController,
|
||||||
builder: (context, controller, child) {
|
builder: (context, controller, child) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(right: 4.0),
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
@@ -204,7 +266,7 @@ class BrowserBottomAppBar extends HookConsumerWidget {
|
|||||||
vertical: 15.0,
|
vertical: 15.0,
|
||||||
horizontal: 8.0,
|
horizontal: 8.0,
|
||||||
),
|
),
|
||||||
child: Icon(Icons.more_vert),
|
child: Icon(Icons.menu),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -377,80 +439,6 @@ class BrowserBottomAppBar extends HookConsumerWidget {
|
|||||||
child: const Text('Feeds'),
|
child: const Text('Feeds'),
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
if (selectedTabId != null)
|
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () async {
|
|
||||||
final tabState = ref.read(
|
|
||||||
tabStateProvider(selectedTabId),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tabState?.url case final Uri url) {
|
|
||||||
await ui_helper.launchUrlFeedback(context, url);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
leadingIcon: const Icon(Icons.open_in_browser),
|
|
||||||
child: const Text('Launch External'),
|
|
||||||
),
|
|
||||||
if (selectedTabId != null)
|
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () async {
|
|
||||||
final tabState = ref.read(
|
|
||||||
tabStateProvider(selectedTabId),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tabState?.url case final Uri url) {
|
|
||||||
await SharePlus.instance.share(ShareParams(uri: url));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
leadingIcon: const Icon(Icons.share),
|
|
||||||
child: const Text('Share'),
|
|
||||||
),
|
|
||||||
if (selectedTabId != null) const Divider(),
|
|
||||||
if (selectedTabId != null)
|
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () {
|
|
||||||
final tabId = ref.read(selectedTabProvider);
|
|
||||||
if (tabId != null) {
|
|
||||||
ref
|
|
||||||
.read(findInPageControllerProvider(tabId).notifier)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
leadingIcon: const Icon(Icons.search),
|
|
||||||
child: const Text('Find in page'),
|
|
||||||
),
|
|
||||||
if (selectedTabId != null)
|
|
||||||
Consumer(
|
|
||||||
builder: (context, childRef, child) {
|
|
||||||
final enabled = childRef.watch(
|
|
||||||
desktopModeProvider(selectedTabId),
|
|
||||||
);
|
|
||||||
|
|
||||||
return MenuItemButton(
|
|
||||||
onPressed: () {
|
|
||||||
ref
|
|
||||||
.read(desktopModeProvider(selectedTabId).notifier)
|
|
||||||
.toggle();
|
|
||||||
},
|
|
||||||
leadingIcon: const Icon(MdiIcons.monitor),
|
|
||||||
trailingIcon: Checkbox(
|
|
||||||
value: enabled,
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
ref
|
|
||||||
.read(
|
|
||||||
desktopModeProvider(selectedTabId).notifier,
|
|
||||||
)
|
|
||||||
.enabled(value);
|
|
||||||
trippleDotMenuController.close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
child: const Text('Desktop Mode'),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
if (selectedTabId != null) const Divider(),
|
|
||||||
if (selectedTabId != null)
|
if (selectedTabId != null)
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@@ -459,7 +447,7 @@ class BrowserBottomAppBar extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await controller.reload();
|
await controller.reload();
|
||||||
trippleDotMenuController.close();
|
hamburgerMenuController.close();
|
||||||
},
|
},
|
||||||
leadingIcon: const Icon(Icons.refresh),
|
leadingIcon: const Icon(Icons.refresh),
|
||||||
child: const Text('Reload'),
|
child: const Text('Reload'),
|
||||||
@@ -513,7 +501,7 @@ class BrowserBottomAppBar extends HookConsumerWidget {
|
|||||||
await controller.goBack();
|
await controller.goBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
trippleDotMenuController.close();
|
hamburgerMenuController.close();
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.arrow_back),
|
icon: const Icon(Icons.arrow_back),
|
||||||
)
|
)
|
||||||
@@ -523,7 +511,7 @@ class BrowserBottomAppBar extends HookConsumerWidget {
|
|||||||
.read(tabRepositoryProvider.notifier)
|
.read(tabRepositoryProvider.notifier)
|
||||||
.closeTab(selectedTabId);
|
.closeTab(selectedTabId);
|
||||||
|
|
||||||
trippleDotMenuController.close();
|
hamburgerMenuController.close();
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
ui_helper.showTabUndoClose(
|
ui_helper.showTabUndoClose(
|
||||||
@@ -551,7 +539,7 @@ class BrowserBottomAppBar extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await controller.goForward();
|
await controller.goForward();
|
||||||
trippleDotMenuController.close();
|
hamburgerMenuController.close();
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
icon: const Icon(Icons.arrow_forward),
|
icon: const Icon(Icons.arrow_forward),
|
||||||
|
|||||||
-197
@@ -17,33 +17,18 @@
|
|||||||
* 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 'dart:ui' as ui;
|
|
||||||
|
|
||||||
import 'package:fading_scroll/fading_scroll.dart';
|
import 'package:fading_scroll/fading_scroll.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.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:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
|
||||||
import 'package:weblibre/features/bangs/domain/providers/bangs.dart';
|
import 'package:weblibre/features/bangs/domain/providers/bangs.dart';
|
||||||
import 'package:weblibre/features/bangs/presentation/widgets/site_search.dart';
|
import 'package:weblibre/features/bangs/presentation/widgets/site_search.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.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/tab_session.dart';
|
|
||||||
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/qr_code.dart';
|
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/certificate_tile.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/certificate_tile.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/draggable_scrollable_header.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/draggable_scrollable_header.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.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/presentation/hooks/listenable_callback.dart';
|
import 'package:weblibre/presentation/hooks/listenable_callback.dart';
|
||||||
import 'package:weblibre/presentation/widgets/share_tile.dart';
|
|
||||||
import 'package:weblibre/presentation/widgets/website_feed_tile.dart';
|
|
||||||
import 'package:weblibre/presentation/widgets/website_title_tile.dart';
|
import 'package:weblibre/presentation/widgets/website_title_tile.dart';
|
||||||
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
|
||||||
|
|
||||||
class ClampingScrollPhysicsWithoutImplicit extends ClampingScrollPhysics {
|
class ClampingScrollPhysicsWithoutImplicit extends ClampingScrollPhysics {
|
||||||
const ClampingScrollPhysicsWithoutImplicit({super.parent});
|
const ClampingScrollPhysicsWithoutImplicit({super.parent});
|
||||||
@@ -220,188 +205,6 @@ class ViewTabSheetWidget extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
|
||||||
leading: const Icon(MdiIcons.contentCopy),
|
|
||||||
title: const Text('Copy address'),
|
|
||||||
onTap: () async {
|
|
||||||
await Clipboard.setData(
|
|
||||||
ClipboardData(text: initialTabState.url.toString()),
|
|
||||||
);
|
|
||||||
|
|
||||||
ref
|
|
||||||
.read(bottomSheetControllerProvider.notifier)
|
|
||||||
.requestDismiss();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
onTap: () async {
|
|
||||||
await ui_helper.launchUrlFeedback(
|
|
||||||
context,
|
|
||||||
initialTabState.url,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
leading: const Icon(Icons.open_in_browser),
|
|
||||||
title: const Text('Launch External'),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(MdiIcons.tabPlus),
|
|
||||||
title: const Text('Clone tab'),
|
|
||||||
onTap: () async {
|
|
||||||
final tabId = await ref
|
|
||||||
.read(tabRepositoryProvider.notifier)
|
|
||||||
.addTab(
|
|
||||||
url: initialTabState.url,
|
|
||||||
private: false,
|
|
||||||
selectTab: false,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (context.mounted) {
|
|
||||||
//save reference before pop `ref` gets disposed
|
|
||||||
final repo = ref.read(tabRepositoryProvider.notifier);
|
|
||||||
|
|
||||||
ui_helper.showTabSwitchMessage(
|
|
||||||
context,
|
|
||||||
onSwitch: () async {
|
|
||||||
await repo.selectTab(tabId);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
ref
|
|
||||||
.read(bottomSheetControllerProvider.notifier)
|
|
||||||
.requestDismiss();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(MdiIcons.tabUnselected),
|
|
||||||
title: const Text('Clone as private tab'),
|
|
||||||
onTap: () async {
|
|
||||||
final tabId = await ref
|
|
||||||
.read(tabRepositoryProvider.notifier)
|
|
||||||
.addTab(
|
|
||||||
url: initialTabState.url,
|
|
||||||
private: true,
|
|
||||||
selectTab: false,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (context.mounted) {
|
|
||||||
//save reference before pop `ref` gets disposed
|
|
||||||
final repo = ref.read(tabRepositoryProvider.notifier);
|
|
||||||
|
|
||||||
ui_helper.showTabSwitchMessage(
|
|
||||||
context,
|
|
||||||
onSwitch: () async {
|
|
||||||
await repo.selectTab(tabId);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
ref
|
|
||||||
.read(bottomSheetControllerProvider.notifier)
|
|
||||||
.requestDismiss();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(MdiIcons.folderArrowUpDownOutline),
|
|
||||||
title: const Text('Assign container'),
|
|
||||||
onTap: () async {
|
|
||||||
final targetContainerId = await ContainerSelectionRoute()
|
|
||||||
.push<String?>(context);
|
|
||||||
|
|
||||||
if (targetContainerId != null) {
|
|
||||||
final containerData = await ref
|
|
||||||
.read(containerRepositoryProvider.notifier)
|
|
||||||
.getContainerData(targetContainerId);
|
|
||||||
|
|
||||||
if (containerData != null) {
|
|
||||||
await ref
|
|
||||||
.read(tabDataRepositoryProvider.notifier)
|
|
||||||
.assignContainer(initialTabState.id, containerData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ref
|
|
||||||
.read(bottomSheetControllerProvider.notifier)
|
|
||||||
.requestDismiss();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Consumer(
|
|
||||||
child: ListTile(
|
|
||||||
leading: const Icon(MdiIcons.folderCancelOutline),
|
|
||||||
title: const Text('Unassign container'),
|
|
||||||
onTap: () async {
|
|
||||||
await ref
|
|
||||||
.read(tabDataRepositoryProvider.notifier)
|
|
||||||
.unassignContainer(initialTabState.id);
|
|
||||||
|
|
||||||
ref
|
|
||||||
.read(bottomSheetControllerProvider.notifier)
|
|
||||||
.requestDismiss();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
builder: (context, ref, child) {
|
|
||||||
final containerId = ref.watch(
|
|
||||||
watchContainerTabIdProvider(
|
|
||||||
initialTabState.id,
|
|
||||||
).select((value) => value.value),
|
|
||||||
);
|
|
||||||
|
|
||||||
return Visibility(
|
|
||||||
visible: containerId != null,
|
|
||||||
child: child!,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ShareTile(
|
|
||||||
onTap: () async {
|
|
||||||
await SharePlus.instance.share(
|
|
||||||
ShareParams(uri: initialTabState.url),
|
|
||||||
);
|
|
||||||
|
|
||||||
ref
|
|
||||||
.read(bottomSheetControllerProvider.notifier)
|
|
||||||
.requestDismiss();
|
|
||||||
},
|
|
||||||
onTapQr: () async {
|
|
||||||
await showQrCode(context, initialTabState.url.toString());
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.mobile_screen_share),
|
|
||||||
title: const Text('Share screenshot'),
|
|
||||||
onTap: () async {
|
|
||||||
final screenshot = await ref
|
|
||||||
.read(selectedTabSessionProvider)
|
|
||||||
.requestScreenshot();
|
|
||||||
|
|
||||||
if (screenshot != null) {
|
|
||||||
ui.decodeImageFromList(screenshot, (result) async {
|
|
||||||
final png = await result.toByteData(
|
|
||||||
format: ui.ImageByteFormat.png,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (png != null) {
|
|
||||||
final file = XFile.fromData(
|
|
||||||
png.buffer.asUint8List(),
|
|
||||||
mimeType: 'image/png',
|
|
||||||
);
|
|
||||||
|
|
||||||
await SharePlus.instance.share(
|
|
||||||
ShareParams(
|
|
||||||
files: [file],
|
|
||||||
subject: initialTabState.title,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ref
|
|
||||||
.read(bottomSheetControllerProvider.notifier)
|
|
||||||
.requestDismiss();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
WebsiteFeedTile(initialTabState),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,292 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024-2025 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:ui' as ui;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/domain/providers/desktop_mode.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/domain/providers/tab_session.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/presentation/dialogs/qr_code.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/readerview/presentation/controllers/readerable.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/readerview/presentation/widgets/reader_button.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.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/presentation/widgets/website_feed_menu_button.dart';
|
||||||
|
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
||||||
|
|
||||||
|
class TabMenu extends HookConsumerWidget {
|
||||||
|
final Widget child;
|
||||||
|
final MenuController controller;
|
||||||
|
final String selectedTabId;
|
||||||
|
|
||||||
|
const TabMenu({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
required this.controller,
|
||||||
|
required this.selectedTabId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return MenuAnchor(
|
||||||
|
controller: controller,
|
||||||
|
builder: (context, controller, child) {
|
||||||
|
return child!;
|
||||||
|
},
|
||||||
|
menuChildren: [
|
||||||
|
MenuItemButton(
|
||||||
|
onPressed: () {
|
||||||
|
ref.read(bottomSheetControllerProvider.notifier).requestDismiss();
|
||||||
|
|
||||||
|
ref
|
||||||
|
.read(findInPageControllerProvider(selectedTabId).notifier)
|
||||||
|
.show();
|
||||||
|
},
|
||||||
|
leadingIcon: const Icon(Icons.search),
|
||||||
|
child: const Text('Find in page'),
|
||||||
|
),
|
||||||
|
ReaderButton(
|
||||||
|
buttonBuilder: (isLoading, readerActive, icon) => MenuItemButton(
|
||||||
|
onPressed: isLoading
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
await ref
|
||||||
|
.read(readerableScreenControllerProvider.notifier)
|
||||||
|
.toggleReaderView(!readerActive);
|
||||||
|
},
|
||||||
|
leadingIcon: icon,
|
||||||
|
trailingIcon: Checkbox(
|
||||||
|
value: readerActive,
|
||||||
|
onChanged: (value) async {
|
||||||
|
if (value != null && !isLoading) {
|
||||||
|
await ref
|
||||||
|
.read(readerableScreenControllerProvider.notifier)
|
||||||
|
.toggleReaderView(!readerActive);
|
||||||
|
controller.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
child: const Text('Reader Mode'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Consumer(
|
||||||
|
builder: (context, childRef, child) {
|
||||||
|
final enabled = childRef.watch(desktopModeProvider(selectedTabId));
|
||||||
|
|
||||||
|
return MenuItemButton(
|
||||||
|
onPressed: () {
|
||||||
|
ref.read(desktopModeProvider(selectedTabId).notifier).toggle();
|
||||||
|
},
|
||||||
|
leadingIcon: const Icon(MdiIcons.monitor),
|
||||||
|
trailingIcon: Checkbox(
|
||||||
|
value: enabled,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
ref
|
||||||
|
.read(desktopModeProvider(selectedTabId).notifier)
|
||||||
|
.enabled(value);
|
||||||
|
controller.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
child: const Text('Desktop Mode'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: const Icon(MdiIcons.contentCopy),
|
||||||
|
child: const Text('Copy address'),
|
||||||
|
onPressed: () async {
|
||||||
|
final tabState = ref.read(tabStateProvider(selectedTabId))!;
|
||||||
|
|
||||||
|
await Clipboard.setData(
|
||||||
|
ClipboardData(text: tabState.url.toString()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: const Icon(Icons.open_in_browser),
|
||||||
|
child: const Text('Launch External'),
|
||||||
|
onPressed: () async {
|
||||||
|
final tabState = ref.read(tabStateProvider(selectedTabId))!;
|
||||||
|
|
||||||
|
await ui_helper.launchUrlFeedback(context, tabState.url);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: const Icon(MdiIcons.tabPlus),
|
||||||
|
child: const Text('Clone tab'),
|
||||||
|
onPressed: () async {
|
||||||
|
final tabState = ref.read(tabStateProvider(selectedTabId))!;
|
||||||
|
|
||||||
|
final tabId = await ref
|
||||||
|
.read(tabRepositoryProvider.notifier)
|
||||||
|
.addTab(url: tabState.url, private: false, selectTab: false);
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
//save reference before pop `ref` gets disposed
|
||||||
|
final repo = ref.read(tabRepositoryProvider.notifier);
|
||||||
|
|
||||||
|
ui_helper.showTabSwitchMessage(
|
||||||
|
context,
|
||||||
|
onSwitch: () async {
|
||||||
|
await repo.selectTab(tabId);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: const Icon(MdiIcons.tabUnselected),
|
||||||
|
child: const Text('Clone as private tab'),
|
||||||
|
onPressed: () async {
|
||||||
|
final tabState = ref.read(tabStateProvider(selectedTabId))!;
|
||||||
|
|
||||||
|
final tabId = await ref
|
||||||
|
.read(tabRepositoryProvider.notifier)
|
||||||
|
.addTab(url: tabState.url, private: true, selectTab: false);
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
//save reference before pop `ref` gets disposed
|
||||||
|
final repo = ref.read(tabRepositoryProvider.notifier);
|
||||||
|
|
||||||
|
ui_helper.showTabSwitchMessage(
|
||||||
|
context,
|
||||||
|
onSwitch: () async {
|
||||||
|
await repo.selectTab(tabId);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: const Icon(MdiIcons.folderArrowUpDownOutline),
|
||||||
|
child: const Text('Assign container'),
|
||||||
|
onPressed: () async {
|
||||||
|
final targetContainerId = await ContainerSelectionRoute()
|
||||||
|
.push<String?>(context);
|
||||||
|
|
||||||
|
if (targetContainerId != null) {
|
||||||
|
final containerData = await ref
|
||||||
|
.read(containerRepositoryProvider.notifier)
|
||||||
|
.getContainerData(targetContainerId);
|
||||||
|
|
||||||
|
if (containerData != null) {
|
||||||
|
final tabState = ref.read(tabStateProvider(selectedTabId))!;
|
||||||
|
|
||||||
|
await ref
|
||||||
|
.read(tabDataRepositoryProvider.notifier)
|
||||||
|
.assignContainer(tabState.id, containerData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Consumer(
|
||||||
|
child: MenuItemButton(
|
||||||
|
leadingIcon: const Icon(MdiIcons.folderCancelOutline),
|
||||||
|
child: const Text('Unassign container'),
|
||||||
|
onPressed: () async {
|
||||||
|
final tabState = ref.read(tabStateProvider(selectedTabId))!;
|
||||||
|
|
||||||
|
await ref
|
||||||
|
.read(tabDataRepositoryProvider.notifier)
|
||||||
|
.unassignContainer(tabState.id);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
builder: (context, ref, child) {
|
||||||
|
final containerId = ref.watch(
|
||||||
|
watchContainerTabIdProvider(
|
||||||
|
selectedTabId,
|
||||||
|
).select((value) => value.value),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Visibility(visible: containerId != null, child: child!);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: const Icon(Icons.share),
|
||||||
|
onPressed: () async {
|
||||||
|
final tabState = ref.read(tabStateProvider(selectedTabId))!;
|
||||||
|
|
||||||
|
await SharePlus.instance.share(ShareParams(uri: tabState.url));
|
||||||
|
},
|
||||||
|
trailingIcon: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const VerticalDivider(indent: 4, endIndent: 4),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.qr_code),
|
||||||
|
onPressed: () async {
|
||||||
|
final tabState = ref.read(tabStateProvider(selectedTabId))!;
|
||||||
|
|
||||||
|
await showQrCode(context, tabState.url.toString());
|
||||||
|
controller.close();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: const Text('Share link'),
|
||||||
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: const Icon(Icons.mobile_screen_share),
|
||||||
|
child: const Text('Share screenshot'),
|
||||||
|
onPressed: () async {
|
||||||
|
final screenshot = await ref
|
||||||
|
.read(selectedTabSessionProvider)
|
||||||
|
.requestScreenshot();
|
||||||
|
|
||||||
|
final tabState = ref.read(tabStateProvider(selectedTabId))!;
|
||||||
|
|
||||||
|
if (screenshot != null) {
|
||||||
|
ui.decodeImageFromList(screenshot, (result) async {
|
||||||
|
final png = await result.toByteData(
|
||||||
|
format: ui.ImageByteFormat.png,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (png != null) {
|
||||||
|
final file = XFile.fromData(
|
||||||
|
png.buffer.asUint8List(),
|
||||||
|
mimeType: 'image/png',
|
||||||
|
);
|
||||||
|
|
||||||
|
await SharePlus.instance.share(
|
||||||
|
ShareParams(files: [file], subject: tabState.title),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
WebsiteFeedMenuButton(selectedTabId),
|
||||||
|
],
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-22
@@ -27,6 +27,11 @@ import 'package:weblibre/features/user/domain/repositories/general_settings.dart
|
|||||||
import 'package:weblibre/presentation/widgets/animate_gradient_shader.dart';
|
import 'package:weblibre/presentation/widgets/animate_gradient_shader.dart';
|
||||||
|
|
||||||
class ReaderButton extends HookConsumerWidget {
|
class ReaderButton extends HookConsumerWidget {
|
||||||
|
final Widget Function(bool isLoading, bool readerActive, Widget icon)
|
||||||
|
buttonBuilder;
|
||||||
|
|
||||||
|
const ReaderButton({super.key, required this.buttonBuilder});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
@@ -65,21 +70,10 @@ class ReaderButton extends HookConsumerWidget {
|
|||||||
(enforceReadability && enableReadability),
|
(enforceReadability && enableReadability),
|
||||||
child: readerChanging.when(
|
child: readerChanging.when(
|
||||||
skipLoadingOnReload: true,
|
skipLoadingOnReload: true,
|
||||||
data: (_) => InkWell(
|
data: (_) => buttonBuilder(
|
||||||
onTap: readerChanging.isLoading
|
readerChanging.isLoading,
|
||||||
? null
|
readerabilityState.active,
|
||||||
: () async {
|
icon,
|
||||||
await ref
|
|
||||||
.read(readerableScreenControllerProvider.notifier)
|
|
||||||
.toggleReaderView(!readerabilityState.active);
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 15.0,
|
|
||||||
horizontal: 8.0,
|
|
||||||
),
|
|
||||||
child: icon,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
error: (error, stackTrace) => SizedBox.shrink(),
|
error: (error, stackTrace) => SizedBox.shrink(),
|
||||||
loading: () => AnimateGradientShader(
|
loading: () => AnimateGradientShader(
|
||||||
@@ -91,13 +85,7 @@ class ReaderButton extends HookConsumerWidget {
|
|||||||
colorScheme.secondary,
|
colorScheme.secondary,
|
||||||
colorScheme.secondaryContainer,
|
colorScheme.secondaryContainer,
|
||||||
],
|
],
|
||||||
child: Padding(
|
child: buttonBuilder(true, readerabilityState.active, icon),
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 15.0,
|
|
||||||
horizontal: 8.0,
|
|
||||||
),
|
|
||||||
child: icon,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-1
@@ -10,8 +10,8 @@ import 'package:skeletonizer/skeletonizer.dart';
|
|||||||
import 'package:weblibre/core/logger.dart';
|
import 'package:weblibre/core/logger.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
import 'package:weblibre/core/uuid.dart';
|
import 'package:weblibre/core/uuid.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/view_tabs.dart';
|
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_preview.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_preview.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/view_tabs.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.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/gecko_inference.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/gecko_inference.dart';
|
||||||
|
|||||||
@@ -346,6 +346,22 @@ class GeneralSettingsScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
|
SwitchListTile.adaptive(
|
||||||
|
title: const Text('Reader Mode in Tab Bar'),
|
||||||
|
subtitle: const Text(
|
||||||
|
'Show reader mode button in the tab bar instead of only in the tab menu',
|
||||||
|
),
|
||||||
|
secondary: const Icon(MdiIcons.bookHeart),
|
||||||
|
value: generalSettings.tabBarReaderView,
|
||||||
|
onChanged: (value) async {
|
||||||
|
await ref
|
||||||
|
.read(saveGeneralSettingsControllerProvider.notifier)
|
||||||
|
.save(
|
||||||
|
(currentSettings) =>
|
||||||
|
currentSettings.copyWith.tabBarReaderView(value),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
SwitchListTile.adaptive(
|
SwitchListTile.adaptive(
|
||||||
title: const Text('Create Child Tabs'),
|
title: const Text('Create Child Tabs'),
|
||||||
subtitle: const Text(
|
subtitle: const Text(
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class GeneralSettings with FastEquatable {
|
|||||||
final TabBarSwipeAction tabBarSwipeAction;
|
final TabBarSwipeAction tabBarSwipeAction;
|
||||||
final Duration historyAutoCleanInterval;
|
final Duration historyAutoCleanInterval;
|
||||||
final bool tabViewBottomSheet;
|
final bool tabViewBottomSheet;
|
||||||
|
final bool tabBarReaderView;
|
||||||
|
|
||||||
GeneralSettings({
|
GeneralSettings({
|
||||||
required this.themeMode,
|
required this.themeMode,
|
||||||
@@ -88,6 +89,7 @@ class GeneralSettings with FastEquatable {
|
|||||||
required this.tabBarSwipeAction,
|
required this.tabBarSwipeAction,
|
||||||
required this.historyAutoCleanInterval,
|
required this.historyAutoCleanInterval,
|
||||||
required this.tabViewBottomSheet,
|
required this.tabViewBottomSheet,
|
||||||
|
required this.tabBarReaderView,
|
||||||
});
|
});
|
||||||
|
|
||||||
GeneralSettings.withDefaults({
|
GeneralSettings.withDefaults({
|
||||||
@@ -106,6 +108,7 @@ class GeneralSettings with FastEquatable {
|
|||||||
TabBarSwipeAction? tabBarSwipeAction,
|
TabBarSwipeAction? tabBarSwipeAction,
|
||||||
Duration? historyAutoCleanInterval,
|
Duration? historyAutoCleanInterval,
|
||||||
bool? tabViewBottomSheet,
|
bool? tabViewBottomSheet,
|
||||||
|
bool? tabBarReaderView,
|
||||||
}) : themeMode = themeMode ?? ThemeMode.dark,
|
}) : themeMode = themeMode ?? ThemeMode.dark,
|
||||||
enableReadability = enableReadability ?? true,
|
enableReadability = enableReadability ?? true,
|
||||||
enforceReadability = enforceReadability ?? false,
|
enforceReadability = enforceReadability ?? false,
|
||||||
@@ -122,7 +125,8 @@ class GeneralSettings with FastEquatable {
|
|||||||
tabBarSwipeAction ?? TabBarSwipeAction.switchLastOpened,
|
tabBarSwipeAction ?? TabBarSwipeAction.switchLastOpened,
|
||||||
historyAutoCleanInterval =
|
historyAutoCleanInterval =
|
||||||
historyAutoCleanInterval ?? const Duration(days: 90),
|
historyAutoCleanInterval ?? const Duration(days: 90),
|
||||||
tabViewBottomSheet = tabViewBottomSheet ?? false;
|
tabViewBottomSheet = tabViewBottomSheet ?? false,
|
||||||
|
tabBarReaderView = tabBarReaderView ?? false;
|
||||||
|
|
||||||
factory GeneralSettings.fromJson(Map<String, dynamic> json) =>
|
factory GeneralSettings.fromJson(Map<String, dynamic> json) =>
|
||||||
_$GeneralSettingsFromJson(json);
|
_$GeneralSettingsFromJson(json);
|
||||||
@@ -146,5 +150,6 @@ class GeneralSettings with FastEquatable {
|
|||||||
tabBarSwipeAction,
|
tabBarSwipeAction,
|
||||||
historyAutoCleanInterval,
|
historyAutoCleanInterval,
|
||||||
tabViewBottomSheet,
|
tabViewBottomSheet,
|
||||||
|
tabBarReaderView,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ abstract class _$GeneralSettingsCWProxy {
|
|||||||
|
|
||||||
GeneralSettings tabViewBottomSheet(bool tabViewBottomSheet);
|
GeneralSettings tabViewBottomSheet(bool tabViewBottomSheet);
|
||||||
|
|
||||||
|
GeneralSettings tabBarReaderView(bool tabBarReaderView);
|
||||||
|
|
||||||
/// Creates a new instance with the provided field values.
|
/// Creates a new instance with the provided field values.
|
||||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`.
|
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`.
|
||||||
///
|
///
|
||||||
@@ -66,6 +68,7 @@ abstract class _$GeneralSettingsCWProxy {
|
|||||||
TabBarSwipeAction tabBarSwipeAction,
|
TabBarSwipeAction tabBarSwipeAction,
|
||||||
Duration historyAutoCleanInterval,
|
Duration historyAutoCleanInterval,
|
||||||
bool tabViewBottomSheet,
|
bool tabViewBottomSheet,
|
||||||
|
bool tabBarReaderView,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +141,10 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
|||||||
GeneralSettings tabViewBottomSheet(bool tabViewBottomSheet) =>
|
GeneralSettings tabViewBottomSheet(bool tabViewBottomSheet) =>
|
||||||
call(tabViewBottomSheet: tabViewBottomSheet);
|
call(tabViewBottomSheet: tabViewBottomSheet);
|
||||||
|
|
||||||
|
@override
|
||||||
|
GeneralSettings tabBarReaderView(bool tabBarReaderView) =>
|
||||||
|
call(tabBarReaderView: tabBarReaderView);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
/// Creates a new instance with the provided field values.
|
/// Creates a new instance with the provided field values.
|
||||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`.
|
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`.
|
||||||
@@ -162,6 +169,7 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
|||||||
Object? tabBarSwipeAction = const $CopyWithPlaceholder(),
|
Object? tabBarSwipeAction = const $CopyWithPlaceholder(),
|
||||||
Object? historyAutoCleanInterval = const $CopyWithPlaceholder(),
|
Object? historyAutoCleanInterval = const $CopyWithPlaceholder(),
|
||||||
Object? tabViewBottomSheet = const $CopyWithPlaceholder(),
|
Object? tabViewBottomSheet = const $CopyWithPlaceholder(),
|
||||||
|
Object? tabBarReaderView = const $CopyWithPlaceholder(),
|
||||||
}) {
|
}) {
|
||||||
return GeneralSettings(
|
return GeneralSettings(
|
||||||
themeMode: themeMode == const $CopyWithPlaceholder() || themeMode == null
|
themeMode: themeMode == const $CopyWithPlaceholder() || themeMode == null
|
||||||
@@ -250,6 +258,12 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
|||||||
? _value.tabViewBottomSheet
|
? _value.tabViewBottomSheet
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: tabViewBottomSheet as bool,
|
: tabViewBottomSheet as bool,
|
||||||
|
tabBarReaderView:
|
||||||
|
tabBarReaderView == const $CopyWithPlaceholder() ||
|
||||||
|
tabBarReaderView == null
|
||||||
|
? _value.tabBarReaderView
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: tabBarReaderView as bool,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,6 +317,7 @@ GeneralSettings _$GeneralSettingsFromJson(Map<String, dynamic> json) =>
|
|||||||
microseconds: (json['historyAutoCleanInterval'] as num).toInt(),
|
microseconds: (json['historyAutoCleanInterval'] as num).toInt(),
|
||||||
),
|
),
|
||||||
tabViewBottomSheet: json['tabViewBottomSheet'] as bool?,
|
tabViewBottomSheet: json['tabViewBottomSheet'] as bool?,
|
||||||
|
tabBarReaderView: json['tabBarReaderView'] as bool?,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$GeneralSettingsToJson(
|
Map<String, dynamic> _$GeneralSettingsToJson(
|
||||||
@@ -330,6 +345,7 @@ Map<String, dynamic> _$GeneralSettingsToJson(
|
|||||||
'tabBarSwipeAction': _$TabBarSwipeActionEnumMap[instance.tabBarSwipeAction]!,
|
'tabBarSwipeAction': _$TabBarSwipeActionEnumMap[instance.tabBarSwipeAction]!,
|
||||||
'historyAutoCleanInterval': instance.historyAutoCleanInterval.inMicroseconds,
|
'historyAutoCleanInterval': instance.historyAutoCleanInterval.inMicroseconds,
|
||||||
'tabViewBottomSheet': instance.tabViewBottomSheet,
|
'tabViewBottomSheet': instance.tabViewBottomSheet,
|
||||||
|
'tabBarReaderView': instance.tabBarReaderView,
|
||||||
};
|
};
|
||||||
|
|
||||||
const _$ThemeModeEnumMap = {
|
const _$ThemeModeEnumMap = {
|
||||||
|
|||||||
@@ -104,6 +104,10 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository {
|
|||||||
DriftSqlType.bool,
|
DriftSqlType.bool,
|
||||||
db.typeMapping,
|
db.typeMapping,
|
||||||
),
|
),
|
||||||
|
'tabBarReaderView': settings['tabBarReaderView']?.readAs(
|
||||||
|
DriftSqlType.bool,
|
||||||
|
db.typeMapping,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ final class GeneralSettingsRepositoryProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$generalSettingsRepositoryHash() =>
|
String _$generalSettingsRepositoryHash() =>
|
||||||
r'7ab8eb72c43ed9486bf9fa8b13a6cce83e2b3d66';
|
r'e929fd7a8bf315b08ea7f5a4df2bd5bf066bc84f';
|
||||||
|
|
||||||
abstract class _$GeneralSettingsRepository
|
abstract class _$GeneralSettingsRepository
|
||||||
extends $StreamNotifier<GeneralSettings> {
|
extends $StreamNotifier<GeneralSettings> {
|
||||||
|
|||||||
Reference in New Issue
Block a user