improve the users bottom sheet and button placements
This commit is contained in:
+17
-176
@@ -31,7 +31,6 @@ 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/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/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_state.dart';
|
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/providers/web_extensions_state.dart';
|
import 'package:weblibre/features/geckoview/domain/providers/web_extensions_state.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
||||||
@@ -41,8 +40,8 @@ import 'package:weblibre/features/geckoview/features/browser/presentation/contro
|
|||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/browser_modules/app_bar_title.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/browser_modules/app_bar_title.dart';
|
||||||
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/history_menu.dart';
|
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/menu_item_buttons.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/menu_item_buttons.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/navigation_buttons.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/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';
|
||||||
@@ -54,6 +53,7 @@ import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart'
|
|||||||
import 'package:weblibre/features/geckoview/features/tabs/utils/container_colors.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/utils/container_colors.dart';
|
||||||
import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
|
import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
|
||||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||||
|
import 'package:weblibre/features/user/domain/presentation/dialogs/quit_browser_dialog.dart';
|
||||||
import 'package:weblibre/features/user/domain/providers.dart';
|
import 'package:weblibre/features/user/domain/providers.dart';
|
||||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||||
import 'package:weblibre/presentation/hooks/cached_future.dart';
|
import 'package:weblibre/presentation/hooks/cached_future.dart';
|
||||||
@@ -61,6 +61,7 @@ import 'package:weblibre/presentation/hooks/menu_controller.dart';
|
|||||||
import 'package:weblibre/presentation/icons/tor_icons.dart';
|
import 'package:weblibre/presentation/icons/tor_icons.dart';
|
||||||
import 'package:weblibre/presentation/widgets/selectable_chips.dart';
|
import 'package:weblibre/presentation/widgets/selectable_chips.dart';
|
||||||
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
||||||
|
import 'package:weblibre/utils/exit_app.dart';
|
||||||
|
|
||||||
class BrowserTopAppBar extends HookConsumerWidget {
|
class BrowserTopAppBar extends HookConsumerWidget {
|
||||||
final bool showMainToolbar;
|
final bool showMainToolbar;
|
||||||
@@ -372,10 +373,7 @@ class BrowserTabBar extends HookConsumerWidget {
|
|||||||
showLongPressMenu: true,
|
showLongPressMenu: true,
|
||||||
),
|
),
|
||||||
if (showMainToolbarNavigationButton)
|
if (showMainToolbarNavigationButton)
|
||||||
NavigationMenuButton(
|
NavigationMenuButton(selectedTabId: selectedTabId),
|
||||||
selectedTabId: selectedTabId,
|
|
||||||
showNavigationButtons: true,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -441,10 +439,7 @@ class ContextualToolbar extends HookConsumerWidget {
|
|||||||
displayedSheet: displayedSheet,
|
displayedSheet: displayedSheet,
|
||||||
showLongPressMenu: false,
|
showLongPressMenu: false,
|
||||||
),
|
),
|
||||||
NavigationMenuButton(
|
NavigationMenuButton(selectedTabId: selectedTabId),
|
||||||
selectedTabId: selectedTabId,
|
|
||||||
showNavigationButtons: false,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -623,13 +618,8 @@ class ShareMenuButton extends HookConsumerWidget {
|
|||||||
|
|
||||||
class NavigationMenuButton extends HookConsumerWidget {
|
class NavigationMenuButton extends HookConsumerWidget {
|
||||||
final String? selectedTabId;
|
final String? selectedTabId;
|
||||||
final bool showNavigationButtons;
|
|
||||||
|
|
||||||
const NavigationMenuButton({
|
const NavigationMenuButton({super.key, required this.selectedTabId});
|
||||||
super.key,
|
|
||||||
required this.selectedTabId,
|
|
||||||
required this.showNavigationButtons,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
@@ -670,6 +660,17 @@ class NavigationMenuButton extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await showQuitBrowserDialog(context);
|
||||||
|
|
||||||
|
if (result == true && context.mounted) {
|
||||||
|
await exitApp(ProviderScope.containerOf(context));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
leadingIcon: const Icon(MdiIcons.power),
|
||||||
|
child: const Text('Quit Browser'),
|
||||||
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
Consumer(
|
Consumer(
|
||||||
builder: (context, childRef, child) {
|
builder: (context, childRef, child) {
|
||||||
@@ -854,171 +855,11 @@ class NavigationMenuButton extends HookConsumerWidget {
|
|||||||
leadingIcon: const Icon(Icons.rss_feed),
|
leadingIcon: const Icon(Icons.rss_feed),
|
||||||
child: const Text('Feeds'),
|
child: const Text('Feeds'),
|
||||||
),
|
),
|
||||||
const Divider(),
|
|
||||||
if (selectedTabId != null)
|
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () async {
|
|
||||||
final controller = ref.read(
|
|
||||||
tabSessionProvider(tabId: selectedTabId).notifier,
|
|
||||||
);
|
|
||||||
|
|
||||||
await controller.reload();
|
|
||||||
hamburgerMenuController.close();
|
|
||||||
},
|
|
||||||
leadingIcon: const Icon(Icons.refresh),
|
|
||||||
child: const Text('Reload'),
|
|
||||||
),
|
|
||||||
if (selectedTabId != null && showNavigationButtons) ...[
|
|
||||||
const Divider(),
|
|
||||||
Consumer(
|
|
||||||
builder: (context, ref, child) {
|
|
||||||
final history = ref.watch(
|
|
||||||
tabStateProvider(
|
|
||||||
selectedTabId,
|
|
||||||
).select((value) => value?.historyState),
|
|
||||||
);
|
|
||||||
|
|
||||||
final isLoading = ref.watch(
|
|
||||||
selectedTabStateProvider.select(
|
|
||||||
(state) => state?.isLoading ?? false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: NavigateBackButton(
|
|
||||||
selectedTabId: selectedTabId,
|
|
||||||
isLoading: isLoading,
|
|
||||||
menuControllerToClose: hamburgerMenuController,
|
|
||||||
canGoBack: history?.canGoBack == true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 48, child: VerticalDivider()),
|
|
||||||
Expanded(
|
|
||||||
child: NavigateForwardButton(
|
|
||||||
selectedTabId: selectedTabId,
|
|
||||||
menuControllerToClose: hamburgerMenuController,
|
|
||||||
canGoForward: history?.canGoForward == true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NavigateForwardButton extends HookConsumerWidget {
|
|
||||||
const NavigateForwardButton({
|
|
||||||
super.key,
|
|
||||||
required this.selectedTabId,
|
|
||||||
this.menuControllerToClose,
|
|
||||||
this.canGoForward = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String? selectedTabId;
|
|
||||||
final MenuController? menuControllerToClose;
|
|
||||||
final bool canGoForward;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final historyMenuController = useMenuController();
|
|
||||||
|
|
||||||
return HistoryMenu(
|
|
||||||
selectedTabId: selectedTabId,
|
|
||||||
controller: historyMenuController,
|
|
||||||
direction: HistoryMenuDirection.forward,
|
|
||||||
child: IconButton(
|
|
||||||
onPressed: canGoForward
|
|
||||||
? () async {
|
|
||||||
final controller = ref.read(
|
|
||||||
tabSessionProvider(tabId: selectedTabId).notifier,
|
|
||||||
);
|
|
||||||
|
|
||||||
await controller.goForward();
|
|
||||||
menuControllerToClose?.close();
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
onLongPress: canGoForward
|
|
||||||
? () {
|
|
||||||
if (!historyMenuController.isOpen) {
|
|
||||||
historyMenuController.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
icon: const Icon(Icons.arrow_forward),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NavigateBackButton extends HookConsumerWidget {
|
|
||||||
const NavigateBackButton({
|
|
||||||
super.key,
|
|
||||||
required this.selectedTabId,
|
|
||||||
required this.isLoading,
|
|
||||||
this.menuControllerToClose,
|
|
||||||
this.canGoBack = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String? selectedTabId;
|
|
||||||
final bool isLoading;
|
|
||||||
final MenuController? menuControllerToClose;
|
|
||||||
final bool canGoBack;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final historyMenuController = useMenuController();
|
|
||||||
|
|
||||||
return HistoryMenu(
|
|
||||||
selectedTabId: selectedTabId,
|
|
||||||
controller: historyMenuController,
|
|
||||||
direction: HistoryMenuDirection.back,
|
|
||||||
child: IconButton(
|
|
||||||
onPressed: (canGoBack || isLoading)
|
|
||||||
? () async {
|
|
||||||
final controller = ref.read(
|
|
||||||
tabSessionProvider(tabId: selectedTabId).notifier,
|
|
||||||
);
|
|
||||||
|
|
||||||
final isReaderActive = ref.read(
|
|
||||||
selectedTabStateProvider.select(
|
|
||||||
(state) => state?.readerableState.active ?? false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
await controller.stopLoading();
|
|
||||||
} else if (isReaderActive) {
|
|
||||||
await ref
|
|
||||||
.read(readerableScreenControllerProvider.notifier)
|
|
||||||
.toggleReaderView(false);
|
|
||||||
} else {
|
|
||||||
await controller.goBack();
|
|
||||||
}
|
|
||||||
|
|
||||||
menuControllerToClose?.close();
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
onLongPress: (canGoBack && !isLoading)
|
|
||||||
? () {
|
|
||||||
if (!historyMenuController.isOpen) {
|
|
||||||
historyMenuController.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
icon: isLoading
|
|
||||||
? const Icon(Icons.close)
|
|
||||||
: const Icon(Icons.arrow_back),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AddTabButton extends HookConsumerWidget {
|
class AddTabButton extends HookConsumerWidget {
|
||||||
const AddTabButton();
|
const AddTabButton();
|
||||||
|
|
||||||
|
|||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.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/features/browser/presentation/widgets/history_menu.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/readerview/presentation/controllers/readerable.dart';
|
||||||
|
import 'package:weblibre/presentation/hooks/menu_controller.dart';
|
||||||
|
|
||||||
|
class NavigateForwardButton extends HookConsumerWidget {
|
||||||
|
const NavigateForwardButton({
|
||||||
|
super.key,
|
||||||
|
required this.selectedTabId,
|
||||||
|
this.menuControllerToClose,
|
||||||
|
this.canGoForward = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? selectedTabId;
|
||||||
|
final MenuController? menuControllerToClose;
|
||||||
|
final bool canGoForward;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final historyMenuController = useMenuController();
|
||||||
|
|
||||||
|
return HistoryMenu(
|
||||||
|
selectedTabId: selectedTabId,
|
||||||
|
controller: historyMenuController,
|
||||||
|
direction: HistoryMenuDirection.forward,
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: canGoForward
|
||||||
|
? () async {
|
||||||
|
final controller = ref.read(
|
||||||
|
tabSessionProvider(tabId: selectedTabId).notifier,
|
||||||
|
);
|
||||||
|
|
||||||
|
await controller.goForward();
|
||||||
|
menuControllerToClose?.close();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
onLongPress: canGoForward
|
||||||
|
? () {
|
||||||
|
if (!historyMenuController.isOpen) {
|
||||||
|
historyMenuController.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
icon: const Icon(Icons.arrow_forward),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NavigateBackButton extends HookConsumerWidget {
|
||||||
|
const NavigateBackButton({
|
||||||
|
super.key,
|
||||||
|
required this.selectedTabId,
|
||||||
|
required this.isLoading,
|
||||||
|
this.menuControllerToClose,
|
||||||
|
this.canGoBack = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? selectedTabId;
|
||||||
|
final bool isLoading;
|
||||||
|
final MenuController? menuControllerToClose;
|
||||||
|
final bool canGoBack;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final historyMenuController = useMenuController();
|
||||||
|
|
||||||
|
return HistoryMenu(
|
||||||
|
selectedTabId: selectedTabId,
|
||||||
|
controller: historyMenuController,
|
||||||
|
direction: HistoryMenuDirection.back,
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: (canGoBack || isLoading)
|
||||||
|
? () async {
|
||||||
|
final controller = ref.read(
|
||||||
|
tabSessionProvider(tabId: selectedTabId).notifier,
|
||||||
|
);
|
||||||
|
|
||||||
|
final isReaderActive = ref.read(
|
||||||
|
selectedTabStateProvider.select(
|
||||||
|
(state) => state?.readerableState.active ?? false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
await controller.stopLoading();
|
||||||
|
} else if (isReaderActive) {
|
||||||
|
await ref
|
||||||
|
.read(readerableScreenControllerProvider.notifier)
|
||||||
|
.toggleReaderView(false);
|
||||||
|
} else {
|
||||||
|
await controller.goBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
menuControllerToClose?.close();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
onLongPress: (canGoBack && !isLoading)
|
||||||
|
? () {
|
||||||
|
if (!historyMenuController.isOpen) {
|
||||||
|
historyMenuController.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
icon: isLoading
|
||||||
|
? const Icon(Icons.close)
|
||||||
|
: const Icon(Icons.arrow_back),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,9 +31,11 @@ import 'package:nullability/nullability.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/providers/desktop_mode.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/providers/tab_state.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/menu_item_buttons.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/menu_item_buttons.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/navigation_buttons.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.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';
|
||||||
@@ -451,6 +453,55 @@ class TabMenu extends HookConsumerWidget {
|
|||||||
leadingIcon: const Icon(MdiIcons.tabMinus),
|
leadingIcon: const Icon(MdiIcons.tabMinus),
|
||||||
child: const Text('Close Tab'),
|
child: const Text('Close Tab'),
|
||||||
),
|
),
|
||||||
|
const Divider(),
|
||||||
|
MenuItemButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final sessionController = ref.read(
|
||||||
|
tabSessionProvider(tabId: selectedTabId).notifier,
|
||||||
|
);
|
||||||
|
|
||||||
|
await sessionController.reload();
|
||||||
|
controller.close();
|
||||||
|
},
|
||||||
|
leadingIcon: const Icon(Icons.refresh),
|
||||||
|
child: const Text('Reload'),
|
||||||
|
),
|
||||||
|
Consumer(
|
||||||
|
builder: (context, ref, child) {
|
||||||
|
final history = ref.watch(
|
||||||
|
tabStateProvider(
|
||||||
|
selectedTabId,
|
||||||
|
).select((value) => value?.historyState),
|
||||||
|
);
|
||||||
|
|
||||||
|
final isLoading = ref.watch(
|
||||||
|
selectedTabStateProvider.select(
|
||||||
|
(state) => state?.isLoading ?? false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: NavigateBackButton(
|
||||||
|
selectedTabId: selectedTabId,
|
||||||
|
isLoading: isLoading,
|
||||||
|
menuControllerToClose: controller,
|
||||||
|
canGoBack: history?.canGoBack == true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 48, child: VerticalDivider()),
|
||||||
|
Expanded(
|
||||||
|
child: NavigateForwardButton(
|
||||||
|
selectedTabId: selectedTabId,
|
||||||
|
menuControllerToClose: controller,
|
||||||
|
canGoForward: history?.canGoForward == true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,16 +17,17 @@
|
|||||||
* 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:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.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:weblibre/core/filesystem.dart';
|
import 'package:weblibre/core/filesystem.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
import 'package:weblibre/features/user/domain/presentation/dialogs/quit_browser_dialog.dart';
|
import 'package:weblibre/domain/entities/profile.dart';
|
||||||
import 'package:weblibre/features/user/domain/presentation/utils/profile_switch_handler.dart';
|
import 'package:weblibre/features/user/domain/presentation/utils/profile_switch_handler.dart';
|
||||||
import 'package:weblibre/features/user/domain/repositories/profile.dart';
|
import 'package:weblibre/features/user/domain/repositories/profile.dart';
|
||||||
import 'package:weblibre/presentation/widgets/failure_widget.dart';
|
import 'package:weblibre/presentation/widgets/failure_widget.dart';
|
||||||
import 'package:weblibre/utils/exit_app.dart';
|
|
||||||
|
|
||||||
/// Bottom sheet widget to select a user profile.
|
/// Bottom sheet widget to select a user profile.
|
||||||
class SelectProfileDialog extends HookConsumerWidget {
|
class SelectProfileDialog extends HookConsumerWidget {
|
||||||
@@ -44,30 +45,38 @@ class SelectProfileDialog extends HookConsumerWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Users',
|
'Select user',
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 24),
|
||||||
usersAsync.when(
|
usersAsync.when(
|
||||||
skipLoadingOnReload: true,
|
skipLoadingOnReload: true,
|
||||||
data: (profiles) => Column(
|
data: (profiles) => Wrap(
|
||||||
mainAxisSize: MainAxisSize.min,
|
alignment: WrapAlignment.center,
|
||||||
children: profiles.map((profile) {
|
spacing: 24,
|
||||||
final isSelected =
|
runSpacing: 16,
|
||||||
filesystem.selectedProfile == profile.uuidValue;
|
children: [
|
||||||
|
...profiles.map(
|
||||||
return ListTile(
|
(profile) => _ProfileAvatar(
|
||||||
key: ValueKey(profile.id),
|
profile: profile,
|
||||||
enabled: !isSelected,
|
isActive: filesystem.selectedProfile == profile.uuidValue,
|
||||||
trailing:
|
onTap: () async {
|
||||||
!isSelected ? const Icon(MdiIcons.accountSwitch) : null,
|
await handleSwitchProfile(context, ref, profile);
|
||||||
title: Text(profile.name),
|
},
|
||||||
subtitle: isSelected ? const Text('Active') : null,
|
onLongPress: () async {
|
||||||
|
await EditProfileRoute(
|
||||||
|
profile: jsonEncode(profile.toJson()),
|
||||||
|
).push(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_AddProfileAvatar(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await handleSwitchProfile(context, ref, profile);
|
await CreateProfileRoute().push(context);
|
||||||
},
|
},
|
||||||
);
|
),
|
||||||
}).toList(),
|
],
|
||||||
),
|
),
|
||||||
error: (error, stackTrace) => Center(
|
error: (error, stackTrace) => Center(
|
||||||
child: FailureWidget(
|
child: FailureWidget(
|
||||||
@@ -77,31 +86,13 @@ class SelectProfileDialog extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
loading: () => const Center(child: CircularProgressIndicator()),
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 24),
|
||||||
Row(
|
TextButton.icon(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
onPressed: () async {
|
||||||
children: [
|
await ProfileListRoute().push(context);
|
||||||
TextButton.icon(
|
},
|
||||||
icon: const Icon(MdiIcons.power),
|
icon: const Icon(MdiIcons.accountGroup),
|
||||||
iconAlignment: IconAlignment.start,
|
label: const Text('Manage Profiles'),
|
||||||
label: const Text('Quit Browser'),
|
|
||||||
onPressed: () async {
|
|
||||||
final result = await showQuitBrowserDialog(context);
|
|
||||||
|
|
||||||
if (result == true) {
|
|
||||||
await exitApp(ref.container);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextButton.icon(
|
|
||||||
icon: const Icon(MdiIcons.accountGroup),
|
|
||||||
iconAlignment: IconAlignment.end,
|
|
||||||
label: const Text('Manage'),
|
|
||||||
onPressed: () async {
|
|
||||||
await ProfileListRoute().push(context);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -109,3 +100,105 @@ class SelectProfileDialog extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _ProfileAvatar extends StatelessWidget {
|
||||||
|
final Profile profile;
|
||||||
|
final bool isActive;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
final VoidCallback onLongPress;
|
||||||
|
|
||||||
|
const _ProfileAvatar({
|
||||||
|
required this.profile,
|
||||||
|
required this.isActive,
|
||||||
|
required this.onTap,
|
||||||
|
required this.onLongPress,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
onLongPress: onLongPress,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 28,
|
||||||
|
backgroundColor: isActive
|
||||||
|
? colorScheme.primary
|
||||||
|
: colorScheme.surfaceContainerHighest,
|
||||||
|
child: Icon(
|
||||||
|
Icons.person,
|
||||||
|
size: 24,
|
||||||
|
color: isActive
|
||||||
|
? colorScheme.onPrimary
|
||||||
|
: colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
SizedBox(
|
||||||
|
width: 72,
|
||||||
|
child: Text(
|
||||||
|
profile.name,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AddProfileAvatar extends StatelessWidget {
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
const _AddProfileAvatar({required this.onTap});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 28,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
foregroundColor: colorScheme.onSurfaceVariant,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(color: colorScheme.outline),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.add,
|
||||||
|
size: 24,
|
||||||
|
color: colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
SizedBox(
|
||||||
|
width: 72,
|
||||||
|
child: Text(
|
||||||
|
'Add user',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user