diff --git a/lib/ui/actions/collection/collection_sharing_actions.dart b/lib/ui/actions/collection/collection_sharing_actions.dart index 68bb6e212..b2a92437d 100644 --- a/lib/ui/actions/collection/collection_sharing_actions.dart +++ b/lib/ui/actions/collection/collection_sharing_actions.dart @@ -36,21 +36,13 @@ class CollectionActions { Collection collection, { bool enableCollect = false, }) async { - final dialog = createProgressDialog( - context, - "Creating link...", - isDismissible: true, - ); try { - await dialog.show(); await CollectionsService.instance.createShareUrl( collection, enableCollect: enableCollect, ); - dialog.hide(); return true; } catch (e) { - dialog.hide(); if (e is SharingNotPermittedForFreeAccountsError) { _showUnSupportedAlert(context); } else { @@ -372,25 +364,11 @@ class CollectionActions { BuildContext context, Collection collection, ) async { - final ButtonAction? result = await showActionSheet( - context: context, - buttons: [ - const ButtonWidget( - buttonType: ButtonType.critical, - isInAlert: true, - shouldStickToDarkTheme: true, - buttonAction: ButtonAction.first, - labelText: "Delete album", - ), - const ButtonWidget( - buttonType: ButtonType.secondary, - buttonAction: ButtonAction.cancel, - isInAlert: true, - shouldStickToDarkTheme: true, - labelText: "Cancel", - ) - ], + final ButtonAction? result = await showChoiceActionSheet( + context, + isCritical: true, title: "Delete shared album?", + firstButtonLabel: "Delete album", body: "The album will be deleted for everyone\n\nYou will lose access to " "shared photos in this album that are owned by others", ); diff --git a/lib/ui/advanced_settings_screen.dart b/lib/ui/advanced_settings_screen.dart index 63ac319b2..e63ed4f00 100644 --- a/lib/ui/advanced_settings_screen.dart +++ b/lib/ui/advanced_settings_screen.dart @@ -6,7 +6,7 @@ import 'package:photos/events/force_reload_home_gallery_event.dart'; import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/icon_button_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/components/title_bar_title_widget.dart'; import 'package:photos/ui/components/title_bar_widget.dart'; import 'package:photos/ui/tools/debug/app_storage_viewer.dart'; @@ -98,7 +98,7 @@ class _AdvancedSettingsScreenState extends State { ), singleBorderRadius: 8, alignCaptionedTextToLeft: true, - onTap: () { + onTap: () async { routeToPage(context, const AppStorageViewer()); }, ), diff --git a/lib/ui/backup_settings_screen.dart b/lib/ui/backup_settings_screen.dart index 729b0232e..a908b21d9 100644 --- a/lib/ui/backup_settings_screen.dart +++ b/lib/ui/backup_settings_screen.dart @@ -6,7 +6,7 @@ import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/divider_widget.dart'; import 'package:photos/ui/components/icon_button_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/components/menu_section_description_widget.dart'; import 'package:photos/ui/components/title_bar_title_widget.dart'; import 'package:photos/ui/components/title_bar_widget.dart'; diff --git a/lib/ui/components/album_list_item_widget.dart b/lib/ui/components/album_list_item_widget.dart index 6ad75e392..ba99a0883 100644 --- a/lib/ui/components/album_list_item_widget.dart +++ b/lib/ui/components/album_list_item_widget.dart @@ -37,7 +37,7 @@ class AlbumListItemWidget extends StatelessWidget { ? ThumbnailWidget( item.thumbnail, showFavForAlbumOnly: true, - shouldShowOwnerAvatar: true, + shouldShowOwnerAvatar: false, ) : const NoThumbnailWidget( addBorder: false, diff --git a/lib/ui/components/expandable_menu_item_widget.dart b/lib/ui/components/expandable_menu_item_widget.dart index fdd6e0dbd..6bc959063 100644 --- a/lib/ui/components/expandable_menu_item_widget.dart +++ b/lib/ui/components/expandable_menu_item_widget.dart @@ -2,7 +2,7 @@ import 'package:expandable/expandable.dart'; import 'package:flutter/material.dart'; import 'package:photos/ente_theme_data.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/settings/common_settings.dart'; import 'package:photos/ui/settings/inherited_settings_state.dart'; diff --git a/lib/ui/components/menu_item_widget/menu_item_child_widgets.dart b/lib/ui/components/menu_item_widget/menu_item_child_widgets.dart new file mode 100644 index 000000000..59f7f69a5 --- /dev/null +++ b/lib/ui/components/menu_item_widget/menu_item_child_widgets.dart @@ -0,0 +1,173 @@ +import 'package:flutter/material.dart'; +import 'package:photos/theme/ente_theme.dart'; +import 'package:photos/ui/common/loading_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; + +class TrailingWidget extends StatefulWidget { + final ValueNotifier executionStateNotifier; + final IconData? trailingIcon; + final Color? trailingIconColor; + final Widget? trailingWidget; + final bool trailingIconIsMuted; + final double trailingExtraMargin; + final bool showExecutionStates; + const TrailingWidget({ + required this.executionStateNotifier, + this.trailingIcon, + this.trailingIconColor, + this.trailingWidget, + required this.trailingIconIsMuted, + required this.trailingExtraMargin, + required this.showExecutionStates, + super.key, + }); + @override + State createState() => _TrailingWidgetState(); +} + +class _TrailingWidgetState extends State { + Widget? trailingWidget; + @override + void initState() { + widget.showExecutionStates + ? widget.executionStateNotifier.addListener(_executionStateListener) + : null; + super.initState(); + } + + @override + void dispose() { + widget.executionStateNotifier.removeListener(_executionStateListener); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (trailingWidget == null || !widget.showExecutionStates) { + _setTrailingIcon(); + } + return AnimatedSwitcher( + duration: const Duration(milliseconds: 175), + switchInCurve: Curves.easeInExpo, + switchOutCurve: Curves.easeOutExpo, + child: trailingWidget, + ); + } + + void _executionStateListener() { + final colorScheme = getEnteColorScheme(context); + setState(() { + if (widget.executionStateNotifier.value == ExecutionState.idle) { + _setTrailingIcon(); + } else if (widget.executionStateNotifier.value == + ExecutionState.inProgress) { + trailingWidget = EnteLoadingWidget( + color: colorScheme.strokeMuted, + ); + } else if (widget.executionStateNotifier.value == + ExecutionState.successful) { + trailingWidget = Icon( + Icons.check_outlined, + size: 22, + color: colorScheme.primary500, + ); + } else { + trailingWidget = const SizedBox.shrink(); + } + }); + } + + void _setTrailingIcon() { + if (widget.trailingIcon != null) { + trailingWidget = Padding( + padding: EdgeInsets.only( + right: widget.trailingExtraMargin, + ), + child: Icon( + widget.trailingIcon, + color: widget.trailingIconIsMuted + ? getEnteColorScheme(context).strokeMuted + : widget.trailingIconColor, + ), + ); + } else { + trailingWidget = widget.trailingWidget ?? const SizedBox.shrink(); + } + } +} + +class ExpansionTrailingIcon extends StatelessWidget { + final bool isExpanded; + final IconData? trailingIcon; + final Color? trailingIconColor; + const ExpansionTrailingIcon({ + required this.isExpanded, + this.trailingIcon, + this.trailingIconColor, + super.key, + }); + + @override + Widget build(BuildContext context) { + return AnimatedOpacity( + duration: const Duration(milliseconds: 100), + curve: Curves.easeInOut, + opacity: isExpanded ? 0 : 1, + child: AnimatedSwitcher( + transitionBuilder: (child, animation) { + return ScaleTransition(scale: animation, child: child); + }, + duration: const Duration(milliseconds: 200), + switchInCurve: Curves.easeOut, + child: isExpanded + ? const SizedBox.shrink() + : Icon( + trailingIcon, + color: trailingIconColor, + ), + ), + ); + } +} + +class LeadingWidget extends StatelessWidget { + final IconData? leadingIcon; + final Color? leadingIconColor; + + final Widget? leadingIconWidget; + // leadIconSize deafult value is 20. + final double leadingIconSize; + const LeadingWidget({ + required this.leadingIconSize, + this.leadingIcon, + this.leadingIconColor, + this.leadingIconWidget, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(right: 10), + child: SizedBox( + height: leadingIconSize, + width: leadingIconSize, + child: leadingIcon == null + ? (leadingIconWidget != null + ? FittedBox( + fit: BoxFit.contain, + child: leadingIconWidget, + ) + : const SizedBox.shrink()) + : FittedBox( + fit: BoxFit.contain, + child: Icon( + leadingIcon, + color: leadingIconColor ?? + getEnteColorScheme(context).strokeBase, + ), + ), + ), + ); + } +} diff --git a/lib/ui/components/menu_item_widget.dart b/lib/ui/components/menu_item_widget/menu_item_widget.dart similarity index 63% rename from lib/ui/components/menu_item_widget.dart rename to lib/ui/components/menu_item_widget/menu_item_widget.dart index 8b2518122..4317dca96 100644 --- a/lib/ui/components/menu_item_widget.dart +++ b/lib/ui/components/menu_item_widget/menu_item_widget.dart @@ -1,7 +1,17 @@ import 'package:expandable/expandable.dart'; import 'package:flutter/material.dart'; -import 'package:photos/ente_theme_data.dart'; import 'package:photos/theme/ente_theme.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_child_widgets.dart'; +import 'package:photos/utils/debouncer.dart'; + +enum ExecutionState { + idle, + inProgress, + error, + successful; +} + +typedef FutureVoidCallback = Future Function(); class MenuItemWidget extends StatefulWidget { final Widget captionedTextWidget; @@ -26,7 +36,7 @@ class MenuItemWidget extends StatefulWidget { /// If provided, add this much extra spacing to the right of the trailing icon. final double trailingExtraMargin; - final VoidCallback? onTap; + final FutureVoidCallback? onTap; final VoidCallback? onDoubleTap; final Color? menuItemColor; final bool alignCaptionedTextToLeft; @@ -45,6 +55,18 @@ class MenuItemWidget extends StatefulWidget { /// disable gesture detector if not used final bool isGestureDetectorDisabled; + ///Success state will not be shown if this flag is set to true, only idle and + ///loading state + final bool showOnlyLoadingState; + + final bool surfaceExecutionStates; + + ///To show success state even when execution time < debouce time, set this + ///flag to true. If the loading state needs to be shown and success state not, + ///set the showOnlyLoadingState flag to true, setting this flag to false won't + ///help. + final bool alwaysShowSuccessState; + const MenuItemWidget({ required this.captionedTextWidget, this.isExpandable = false, @@ -68,6 +90,9 @@ class MenuItemWidget extends StatefulWidget { this.isBottomBorderRadiusRemoved = false, this.isTopBorderRadiusRemoved = false, this.isGestureDetectorDisabled = false, + this.showOnlyLoadingState = false, + this.surfaceExecutionStates = true, + this.alwaysShowSuccessState = false, Key? key, }) : super(key: key); @@ -76,6 +101,10 @@ class MenuItemWidget extends StatefulWidget { } class _MenuItemWidgetState extends State { + final _debouncer = Debouncer(const Duration(milliseconds: 300)); + ValueNotifier executionStateNotifier = + ValueNotifier(ExecutionState.idle); + Color? menuItemColor; late double borderRadius; @@ -105,6 +134,7 @@ class _MenuItemWidgetState extends State { if (widget.expandableController != null) { widget.expandableController!.dispose(); } + executionStateNotifier.dispose(); super.dispose(); } @@ -113,7 +143,7 @@ class _MenuItemWidgetState extends State { return widget.isExpandable || widget.isGestureDetectorDisabled ? menuItemWidget(context) : GestureDetector( - onTap: widget.onTap, + onTap: _onTap, onDoubleTap: widget.onDoubleTap, onTapDown: _onTapDown, onTapUp: _onTapUp, @@ -123,7 +153,6 @@ class _MenuItemWidgetState extends State { } Widget menuItemWidget(BuildContext context) { - final enteColorScheme = Theme.of(context).colorScheme.enteTheme.colorScheme; final circularRadius = Radius.circular(borderRadius); final isExpanded = widget.expandableController?.value; final bottomBorderRadius = @@ -151,66 +180,70 @@ class _MenuItemWidgetState extends State { children: [ widget.alignCaptionedTextToLeft && widget.leadingIcon == null ? const SizedBox.shrink() - : Padding( - padding: const EdgeInsets.only(right: 10), - child: SizedBox( - height: widget.leadingIconSize, - width: widget.leadingIconSize, - child: widget.leadingIcon == null - ? (widget.leadingIconWidget != null - ? FittedBox( - fit: BoxFit.contain, - child: widget.leadingIconWidget, - ) - : const SizedBox.shrink()) - : FittedBox( - fit: BoxFit.contain, - child: Icon( - widget.leadingIcon, - color: widget.leadingIconColor ?? - enteColorScheme.strokeBase, - ), - ), - ), + : LeadingWidget( + leadingIconSize: widget.leadingIconSize, + leadingIcon: widget.leadingIcon, + leadingIconColor: widget.leadingIconColor, + leadingIconWidget: widget.leadingIconWidget, ), widget.captionedTextWidget, - widget.expandableController != null - ? AnimatedOpacity( - duration: const Duration(milliseconds: 100), - curve: Curves.easeInOut, - opacity: isExpanded! ? 0 : 1, - child: AnimatedSwitcher( - transitionBuilder: (child, animation) { - return ScaleTransition(scale: animation, child: child); - }, - duration: const Duration(milliseconds: 200), - switchInCurve: Curves.easeOut, - child: isExpanded - ? const SizedBox.shrink() - : Icon( - widget.trailingIcon, - color: widget.trailingIconColor, - ), - ), - ) - : widget.trailingIcon != null - ? Padding( - padding: EdgeInsets.only( - right: widget.trailingExtraMargin, - ), - child: Icon( - widget.trailingIcon, - color: widget.trailingIconIsMuted - ? enteColorScheme.strokeMuted - : widget.trailingIconColor, - ), - ) - : widget.trailingWidget ?? const SizedBox.shrink(), + if (widget.expandableController != null) + ExpansionTrailingIcon( + isExpanded: isExpanded!, + trailingIcon: widget.trailingIcon, + trailingIconColor: widget.trailingIconColor, + ) + else + TrailingWidget( + executionStateNotifier: executionStateNotifier, + trailingIcon: widget.trailingIcon, + trailingIconColor: widget.trailingIconColor, + trailingWidget: widget.trailingWidget, + trailingIconIsMuted: widget.trailingIconIsMuted, + trailingExtraMargin: widget.trailingExtraMargin, + showExecutionStates: widget.surfaceExecutionStates, + key: ValueKey(widget.trailingIcon.hashCode), + ), ], ), ); } + Future _onTap() async { + _debouncer.run( + () => Future( + () { + executionStateNotifier.value = ExecutionState.inProgress; + }, + ), + ); + await widget.onTap?.call().then( + (value) { + widget.alwaysShowSuccessState + ? executionStateNotifier.value = ExecutionState.successful + : null; + }, + onError: (error, stackTrace) => _debouncer.cancelDebounce(), + ); + _debouncer.cancelDebounce(); + if (widget.alwaysShowSuccessState) { + Future.delayed(const Duration(seconds: 2), () { + executionStateNotifier.value = ExecutionState.idle; + }); + return; + } + if (executionStateNotifier.value == ExecutionState.inProgress) { + if (widget.showOnlyLoadingState) { + executionStateNotifier.value = ExecutionState.idle; + } else { + executionStateNotifier.value = ExecutionState.successful; + Future.delayed(const Duration(seconds: 2), () { + executionStateNotifier.value = ExecutionState.idle; + }); + } + } + } + void _onTapDown(details) { setState(() { if (widget.pressedColor == null) { diff --git a/lib/ui/components/toggle_switch_widget.dart b/lib/ui/components/toggle_switch_widget.dart index 8968a9f78..7b6a82084 100644 --- a/lib/ui/components/toggle_switch_widget.dart +++ b/lib/ui/components/toggle_switch_widget.dart @@ -9,12 +9,12 @@ enum ExecutionState { successful, } -typedef FutureVoidCallBack = Future Function(); +typedef FutureVoidCallback = Future Function(); typedef BoolCallBack = bool Function(); class ToggleSwitchWidget extends StatefulWidget { final BoolCallBack value; - final FutureVoidCallBack onChanged; + final FutureVoidCallback onChanged; const ToggleSwitchWidget({ required this.value, required this.onChanged, diff --git a/lib/ui/settings/about_section_widget.dart b/lib/ui/settings/about_section_widget.dart index 3faae4985..297b558e6 100644 --- a/lib/ui/settings/about_section_widget.dart +++ b/lib/ui/settings/about_section_widget.dart @@ -4,7 +4,7 @@ import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/common/web_page.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/expandable_menu_item_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/settings/app_update_dialog.dart'; import 'package:photos/ui/settings/common_settings.dart'; import 'package:photos/utils/dialog_util.dart'; @@ -113,7 +113,7 @@ class AboutMenuItemWidget extends StatelessWidget { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, - onTap: () { + onTap: () async { Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { diff --git a/lib/ui/settings/account_section_widget.dart b/lib/ui/settings/account_section_widget.dart index 759ffd9b2..9a965100c 100644 --- a/lib/ui/settings/account_section_widget.dart +++ b/lib/ui/settings/account_section_widget.dart @@ -11,7 +11,7 @@ import 'package:photos/ui/account/password_entry_page.dart'; import 'package:photos/ui/account/recovery_key_page.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/expandable_menu_item_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/settings/common_settings.dart'; import 'package:photos/utils/dialog_util.dart'; import 'package:photos/utils/navigation_util.dart'; @@ -39,6 +39,7 @@ class AccountSectionWidget extends StatelessWidget { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, + showOnlyLoadingState: true, onTap: () async { final hasAuthenticated = await LocalAuthenticationService.instance .requestLocalAuthentication( @@ -75,6 +76,7 @@ class AccountSectionWidget extends StatelessWidget { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, + showOnlyLoadingState: true, onTap: () async { final hasAuthenticated = await LocalAuthenticationService.instance .requestLocalAuthentication( @@ -101,6 +103,7 @@ class AccountSectionWidget extends StatelessWidget { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, + showOnlyLoadingState: true, onTap: () async { final hasAuthenticated = await LocalAuthenticationService.instance .requestLocalAuthentication( @@ -128,7 +131,7 @@ class AccountSectionWidget extends StatelessWidget { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, - onTap: () { + onTap: () async { _onLogoutTapped(context); }, ), @@ -140,7 +143,7 @@ class AccountSectionWidget extends StatelessWidget { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, - onTap: () { + onTap: () async { routeToPage(context, const DeleteAccountPage()); }, ), diff --git a/lib/ui/settings/backup_section_widget.dart b/lib/ui/settings/backup_section_widget.dart index f29764e4c..415d01c93 100644 --- a/lib/ui/settings/backup_section_widget.dart +++ b/lib/ui/settings/backup_section_widget.dart @@ -12,7 +12,7 @@ import 'package:photos/ui/backup_settings_screen.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/dialog_widget.dart'; import 'package:photos/ui/components/expandable_menu_item_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/components/models/button_type.dart'; import 'package:photos/ui/settings/common_settings.dart'; import 'package:photos/ui/tools/deduplicate_page.dart'; @@ -49,7 +49,7 @@ class BackupSectionWidgetState extends State { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, - onTap: () { + onTap: () async { routeToPage( context, const BackupFolderSelectionPage( @@ -66,7 +66,7 @@ class BackupSectionWidgetState extends State { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, - onTap: () { + onTap: () async { routeToPage( context, const BackupSettingsScreen(), @@ -85,19 +85,16 @@ class BackupSectionWidgetState extends State { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, + showOnlyLoadingState: true, onTap: () async { - final dialog = createProgressDialog(context, "Calculating..."); - await dialog.show(); BackupStatus status; try { status = await SyncService.instance.getBackupStatus(); } catch (e) { - await dialog.hide(); showGenericErrorDialog(context: context); return; } - await dialog.hide(); if (status.localIDs.isEmpty) { showErrorDialog( context, @@ -121,20 +118,17 @@ class BackupSectionWidgetState extends State { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, + showOnlyLoadingState: true, onTap: () async { - final dialog = createProgressDialog(context, "Calculating..."); - await dialog.show(); List duplicates; try { duplicates = await DeduplicationService.instance.getDuplicateFiles(); } catch (e) { - await dialog.hide(); showGenericErrorDialog(context: context); return; } - await dialog.hide(); if (duplicates.isEmpty) { showErrorDialog( context, diff --git a/lib/ui/settings/debug_section_widget.dart b/lib/ui/settings/debug_section_widget.dart index c5b950dac..60f270661 100644 --- a/lib/ui/settings/debug_section_widget.dart +++ b/lib/ui/settings/debug_section_widget.dart @@ -8,7 +8,7 @@ import 'package:photos/services/update_service.dart'; import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/expandable_menu_item_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/settings/common_settings.dart'; import 'package:photos/utils/toast_util.dart'; diff --git a/lib/ui/settings/general_section_widget.dart b/lib/ui/settings/general_section_widget.dart index a3e73bf34..23b9eff89 100644 --- a/lib/ui/settings/general_section_widget.dart +++ b/lib/ui/settings/general_section_widget.dart @@ -5,10 +5,9 @@ import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/advanced_settings_screen.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/expandable_menu_item_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/payment/subscription.dart'; import 'package:photos/ui/settings/common_settings.dart'; -import 'package:photos/utils/dialog_util.dart'; import 'package:photos/utils/navigation_util.dart'; class GeneralSectionWidget extends StatelessWidget { @@ -34,7 +33,7 @@ class GeneralSectionWidget extends StatelessWidget { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, - onTap: () { + onTap: () async { _onManageSubscriptionTapped(context); }, ), @@ -46,8 +45,9 @@ class GeneralSectionWidget extends StatelessWidget { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, - onTap: () { - _onFamilyPlansTapped(context); + showOnlyLoadingState: true, + onTap: () async { + await _onFamilyPlansTapped(context); }, ), sectionOptionSpacing, @@ -58,7 +58,7 @@ class GeneralSectionWidget extends StatelessWidget { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, - onTap: () { + onTap: () async { _onAdvancedTapped(context); }, ), @@ -78,11 +78,8 @@ class GeneralSectionWidget extends StatelessWidget { } Future _onFamilyPlansTapped(BuildContext context) async { - final dialog = createProgressDialog(context, "Please wait..."); - await dialog.show(); final userDetails = await UserService.instance.getUserDetailsV2(memoryCount: false); - await dialog.hide(); BillingService.instance.launchFamilyPortal(context, userDetails); } diff --git a/lib/ui/settings/security_section_widget.dart b/lib/ui/settings/security_section_widget.dart index e4d7b746e..e5c16452b 100644 --- a/lib/ui/settings/security_section_widget.dart +++ b/lib/ui/settings/security_section_widget.dart @@ -11,7 +11,7 @@ import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/account/sessions_page.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/expandable_menu_item_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/components/toggle_switch_widget.dart'; import 'package:photos/ui/settings/common_settings.dart'; @@ -119,6 +119,7 @@ class _SecuritySectionWidgetState extends State { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, + showOnlyLoadingState: true, onTap: () async { final hasAuthenticated = await LocalAuthenticationService.instance .requestLocalAuthentication( diff --git a/lib/ui/settings/social_section_widget.dart b/lib/ui/settings/social_section_widget.dart index 801718d00..43572c7a3 100644 --- a/lib/ui/settings/social_section_widget.dart +++ b/lib/ui/settings/social_section_widget.dart @@ -3,7 +3,7 @@ import 'package:photos/services/update_service.dart'; import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/expandable_menu_item_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/settings/common_settings.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -69,7 +69,7 @@ class SocialsMenuItemWidget extends StatelessWidget { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, - onTap: () { + onTap: () async { launchUrlString(urlSring); }, ); diff --git a/lib/ui/settings/support_section_widget.dart b/lib/ui/settings/support_section_widget.dart index cbd2458a5..dd26ab124 100644 --- a/lib/ui/settings/support_section_widget.dart +++ b/lib/ui/settings/support_section_widget.dart @@ -7,7 +7,7 @@ import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/common/web_page.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/expandable_menu_item_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/settings/about_section_widget.dart'; import 'package:photos/ui/settings/common_settings.dart'; import 'package:photos/utils/email_util.dart'; @@ -54,7 +54,7 @@ class SupportSectionWidget extends StatelessWidget { pressedColor: getEnteColorScheme(context).fillFaint, trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, - onTap: () { + onTap: () async { Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { diff --git a/lib/ui/settings/theme_switch_widget.dart b/lib/ui/settings/theme_switch_widget.dart index 2b84a0e54..a63ca37b0 100644 --- a/lib/ui/settings/theme_switch_widget.dart +++ b/lib/ui/settings/theme_switch_widget.dart @@ -5,7 +5,7 @@ import 'package:photos/ente_theme_data.dart'; import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/expandable_menu_item_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/settings/common_settings.dart'; class ThemeSwitchWidget extends StatefulWidget { diff --git a/lib/ui/sharing/add_partipant_page.dart b/lib/ui/sharing/add_partipant_page.dart index ff2c5c5df..5c1ae2554 100644 --- a/lib/ui/sharing/add_partipant_page.dart +++ b/lib/ui/sharing/add_partipant_page.dart @@ -8,7 +8,7 @@ import 'package:photos/ui/actions/collection/collection_sharing_actions.dart'; import 'package:photos/ui/components/button_widget.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/divider_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/components/menu_section_description_widget.dart'; import 'package:photos/ui/components/menu_section_title.dart'; import 'package:photos/ui/components/models/button_type.dart'; diff --git a/lib/ui/sharing/album_participants_page.dart b/lib/ui/sharing/album_participants_page.dart index 9ed71239a..bf7b79908 100644 --- a/lib/ui/sharing/album_participants_page.dart +++ b/lib/ui/sharing/album_participants_page.dart @@ -5,7 +5,7 @@ import 'package:photos/models/collection.dart'; import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/divider_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/components/menu_section_title.dart'; import 'package:photos/ui/components/title_bar_title_widget.dart'; import 'package:photos/ui/components/title_bar_widget.dart'; @@ -160,7 +160,7 @@ class _AlbumParticipantsPageState extends State { onTap: isOwner ? () async { if (isOwner) { - await _navigateToManageUser(currentUser); + _navigateToManageUser(currentUser); } } : null, @@ -185,7 +185,7 @@ class _AlbumParticipantsPageState extends State { leadingIcon: Icons.add_outlined, menuItemColor: getEnteColorScheme(context).fillFaint, onTap: () async { - await _navigateToAddUser(false); + _navigateToAddUser(false); }, isTopBorderRadiusRemoved: collaborators.isNotEmpty, singleBorderRadius: 8, @@ -259,7 +259,7 @@ class _AlbumParticipantsPageState extends State { leadingIcon: Icons.add_outlined, menuItemColor: getEnteColorScheme(context).fillFaint, onTap: () async { - await _navigateToAddUser(true); + _navigateToAddUser(true); }, isTopBorderRadiusRemoved: viewers.isNotEmpty, singleBorderRadius: 8, diff --git a/lib/ui/sharing/manage_album_participant.dart b/lib/ui/sharing/manage_album_participant.dart index 86aaae546..e3c8340a9 100644 --- a/lib/ui/sharing/manage_album_participant.dart +++ b/lib/ui/sharing/manage_album_participant.dart @@ -4,14 +4,12 @@ import 'package:photos/services/collections_service.dart'; import 'package:photos/theme/colors.dart'; import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/actions/collection/collection_sharing_actions.dart'; -import 'package:photos/ui/components/action_sheet_widget.dart'; import 'package:photos/ui/components/button_widget.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/divider_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/components/menu_section_description_widget.dart'; import 'package:photos/ui/components/menu_section_title.dart'; -import 'package:photos/ui/components/models/button_type.dart'; import 'package:photos/ui/components/title_bar_title_widget.dart'; import 'package:photos/utils/dialog_util.dart'; @@ -84,7 +82,6 @@ class _ManageIndividualParticipantState widget.collection, widget.user.email, CollectionParticipantRole.collaborator, - showProgress: true, ); if (result && mounted) { widget.user.role = CollectionParticipantRole @@ -107,53 +104,38 @@ class _ManageIndividualParticipantState leadingIconColor: getEnteColorScheme(context).strokeBase, menuItemColor: getEnteColorScheme(context).fillFaint, trailingIcon: widget.user.isViewer ? Icons.check : null, + showOnlyLoadingState: true, onTap: widget.user.isViewer ? null : () async { - final ButtonAction? result = await showActionSheet( - context: context, - buttons: [ - ButtonWidget( - buttonType: ButtonType.critical, - isInAlert: true, - shouldStickToDarkTheme: true, - buttonAction: ButtonAction.first, - shouldSurfaceExecutionStates: true, - labelText: "Yes, convert to viewer", - onTap: () async { - isConvertToViewSuccess = - await collectionActions.addEmailToCollection( - context, - widget.collection, - widget.user.email, - CollectionParticipantRole.viewer, - ); - }, - ), - const ButtonWidget( - buttonType: ButtonType.secondary, - buttonAction: ButtonAction.cancel, - isInAlert: true, - shouldStickToDarkTheme: true, - labelText: "Cancel", - ) - ], + final ButtonAction? result = await showChoiceActionSheet( + context, title: "Change permissions?", + firstButtonLabel: "Yes, convert to viewer", body: '${widget.user.email} will not be able to add more photos to this album\n\nThey will still be able to remove existing photos added by them', + isCritical: true, ); if (result != null) { - if (result == ButtonAction.error) { - showGenericErrorDialog(context: context); - } - if (result == ButtonAction.first && - isConvertToViewSuccess && - mounted) { - // reset value - isConvertToViewSuccess = false; - widget.user.role = - CollectionParticipantRole.viewer.toStringVal(); - setState(() => {}); + if (result == ButtonAction.first) { + try { + isConvertToViewSuccess = + await collectionActions.addEmailToCollection( + context, + widget.collection, + widget.user.email, + CollectionParticipantRole.viewer, + ); + } catch (e) { + showGenericErrorDialog(context: context); + } + if (isConvertToViewSuccess && mounted) { + // reset value + isConvertToViewSuccess = false; + widget.user.role = + CollectionParticipantRole.viewer.toStringVal(); + setState(() => {}); + } } } }, @@ -174,6 +156,7 @@ class _ManageIndividualParticipantState leadingIcon: Icons.not_interested_outlined, leadingIconColor: warning500, menuItemColor: getEnteColorScheme(context).fillFaint, + surfaceExecutionStates: false, onTap: () async { final result = await collectionActions.removeParticipant( context, diff --git a/lib/ui/sharing/manage_links_widget.dart b/lib/ui/sharing/manage_links_widget.dart index 65e5365d4..ce87b8f29 100644 --- a/lib/ui/sharing/manage_links_widget.dart +++ b/lib/ui/sharing/manage_links_widget.dart @@ -14,7 +14,7 @@ import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/actions/collection/collection_sharing_actions.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/divider_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/components/menu_section_description_widget.dart'; import 'package:photos/utils/crypto_util.dart'; import 'package:photos/utils/date_time_util.dart'; @@ -112,6 +112,7 @@ class _ManageSharedLinkWidgetState extends State { ), trailingIcon: Icons.chevron_right, menuItemColor: enteColorScheme.fillFaint, + surfaceExecutionStates: false, onTap: () async { await showPicker(); }, @@ -139,6 +140,7 @@ class _ManageSharedLinkWidgetState extends State { onTap: () async { await _showDeviceLimitPicker(); }, + surfaceExecutionStates: false, ), DividerWidget( dividerType: DividerType.menuNoIcon, @@ -221,6 +223,7 @@ class _ManageSharedLinkWidgetState extends State { leadingIcon: Icons.remove_circle_outline, leadingIconColor: warning500, menuItemColor: getEnteColorScheme(context).fillFaint, + surfaceExecutionStates: false, onTap: () async { final bool result = await sharingActions.disableUrl( context, diff --git a/lib/ui/sharing/share_collection_page.dart b/lib/ui/sharing/share_collection_page.dart index d270dd1e5..473c0dd6a 100644 --- a/lib/ui/sharing/share_collection_page.dart +++ b/lib/ui/sharing/share_collection_page.dart @@ -8,7 +8,7 @@ import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/actions/collection/collection_sharing_actions.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/divider_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/components/menu_section_description_widget.dart'; import 'package:photos/ui/components/menu_section_title.dart'; import 'package:photos/ui/sharing/add_partipant_page.dart'; @@ -162,6 +162,7 @@ class _ShareCollectionPageState extends State { ), leadingIcon: Icons.copy, menuItemColor: getEnteColorScheme(context).fillFaint, + showOnlyLoadingState: true, onTap: () async { await Clipboard.setData(ClipboardData(text: url)); showShortToast(context, "Link copied to clipboard"); @@ -228,6 +229,7 @@ class _ShareCollectionPageState extends State { leadingIcon: Icons.link, menuItemColor: getEnteColorScheme(context).fillFaint, isBottomBorderRadiusRemoved: true, + showOnlyLoadingState: true, onTap: () async { final bool result = await collectionActions.enableUrl(context, widget.collection); @@ -255,6 +257,7 @@ class _ShareCollectionPageState extends State { ), leadingIcon: Icons.link, menuItemColor: getEnteColorScheme(context).fillFaint, + showOnlyLoadingState: true, onTap: () async { final bool result = await collectionActions.enableUrl( context, diff --git a/lib/ui/tools/debug/app_storage_viewer.dart b/lib/ui/tools/debug/app_storage_viewer.dart index 2695d8e2e..7022ff388 100644 --- a/lib/ui/tools/debug/app_storage_viewer.dart +++ b/lib/ui/tools/debug/app_storage_viewer.dart @@ -9,7 +9,7 @@ import 'package:photos/services/feature_flag_service.dart'; import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; import 'package:photos/ui/components/icon_button_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/components/menu_section_title.dart'; import 'package:photos/ui/components/title_bar_title_widget.dart'; import 'package:photos/ui/components/title_bar_widget.dart'; @@ -162,6 +162,7 @@ class _AppStorageViewerState extends State { menuItemColor: getEnteColorScheme(context).fillFaint, singleBorderRadius: 8, + alwaysShowSuccessState: true, onTap: () async { for (var pathItem in paths) { if (pathItem.allowCacheClear) { diff --git a/lib/ui/tools/debug/path_storage_viewer.dart b/lib/ui/tools/debug/path_storage_viewer.dart index 9306a8158..28de6ad1d 100644 --- a/lib/ui/tools/debug/path_storage_viewer.dart +++ b/lib/ui/tools/debug/path_storage_viewer.dart @@ -6,7 +6,7 @@ import 'package:flutter/services.dart'; import 'package:logging/logging.dart'; import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/utils/data_util.dart'; import 'package:photos/utils/directory_content.dart'; @@ -76,6 +76,7 @@ class _PathStorageViewerState extends State { Widget _buildMenuItemWidget(DirectoryStat? stat, Object? err) { return MenuItemWidget( + key: UniqueKey(), alignCaptionedTextToLeft: true, captionedTextWidget: CaptionedTextWidget( title: widget.item.title, @@ -102,6 +103,7 @@ class _PathStorageViewerState extends State { menuItemColor: getEnteColorScheme(context).fillFaint, isBottomBorderRadiusRemoved: widget.removeBottomRadius, isTopBorderRadiusRemoved: widget.removeTopRadius, + showOnlyLoadingState: true, onTap: () async { if (kDebugMode) { await Clipboard.setData(ClipboardData(text: widget.item.path)); diff --git a/lib/ui/viewer/gallery/device_folder_page.dart b/lib/ui/viewer/gallery/device_folder_page.dart index 8dcae2eaf..2c755a781 100644 --- a/lib/ui/viewer/gallery/device_folder_page.dart +++ b/lib/ui/viewer/gallery/device_folder_page.dart @@ -15,7 +15,7 @@ import 'package:photos/services/ignored_files_service.dart'; import 'package:photos/services/remote_sync_service.dart'; import 'package:photos/theme/ente_theme.dart'; import 'package:photos/ui/components/captioned_text_widget.dart'; -import 'package:photos/ui/components/menu_item_widget.dart'; +import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart'; import 'package:photos/ui/components/menu_section_description_widget.dart'; import 'package:photos/ui/components/toggle_switch_widget.dart'; import 'package:photos/ui/viewer/actions/file_selection_overlay_bar.dart'; @@ -238,6 +238,7 @@ class _ResetIgnoredFilesWidgetState extends State { singleBorderRadius: 8.0, menuItemColor: getEnteColorScheme(context).fillFaint, leadingIcon: Icons.cloud_off_outlined, + alwaysShowSuccessState: true, onTap: () async { await _removeFilesFromIgnoredFiles( widget.filesInDeviceCollection,