Merge pull request #835 from ente-io/menuitem_states

Execution states for MenuItemWidget
This commit is contained in:
Neeraj Gupta 2023-02-02 09:05:20 +05:30 committed by GitHub
commit 685b10dd11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 347 additions and 175 deletions

View file

@ -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",
);

View file

@ -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<AdvancedSettingsScreen> {
),
borderRadius: 8,
alignCaptionedTextToLeft: true,
onTap: () {
onTap: () async {
routeToPage(context, const AppStorageViewer());
},
),

View file

@ -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';

View file

@ -37,7 +37,7 @@ class AlbumListItemWidget extends StatelessWidget {
? ThumbnailWidget(
item.thumbnail,
showFavForAlbumOnly: true,
shouldShowOwnerAvatar: true,
shouldShowOwnerAvatar: false,
)
: const NoThumbnailWidget(
addBorder: false,

View file

@ -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';

View file

@ -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<TrailingWidget> createState() => _TrailingWidgetState();
}
class _TrailingWidgetState extends State<TrailingWidget> {
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,
),
),
),
);
}
}

View file

@ -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<void> Function();
class MenuItemWidget extends StatefulWidget {
final Widget captionedTextWidget;
@ -25,7 +35,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;
@ -38,6 +48,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,
@ -60,6 +82,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);
@ -68,6 +93,10 @@ class MenuItemWidget extends StatefulWidget {
}
class _MenuItemWidgetState extends State<MenuItemWidget> {
final _debouncer = Debouncer(const Duration(milliseconds: 300));
ValueNotifier<ExecutionState> executionStateNotifier =
ValueNotifier(ExecutionState.idle);
Color? menuItemColor;
@override
@ -92,6 +121,7 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
if (widget.expandableController != null) {
widget.expandableController!.dispose();
}
executionStateNotifier.dispose();
super.dispose();
}
@ -100,7 +130,7 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
return widget.isExpandable || widget.isGestureDetectorDisabled
? menuItemWidget(context)
: GestureDetector(
onTap: widget.onTap,
onTap: _onTap,
onDoubleTap: widget.onDoubleTap,
onTapDown: _onTapDown,
onTapUp: _onTapUp,
@ -110,7 +140,6 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
}
Widget menuItemWidget(BuildContext context) {
final enteColorScheme = Theme.of(context).colorScheme.enteTheme.colorScheme;
final borderRadius = Radius.circular(widget.borderRadius);
final isExpanded = widget.expandableController?.value;
final bottomBorderRadius =
@ -138,66 +167,70 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
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<void> _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) {

View file

@ -9,12 +9,12 @@ enum ExecutionState {
successful,
}
typedef FutureVoidCallBack = Future<void> Function();
typedef FutureVoidCallback = Future<void> 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,

View file

@ -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) {

View file

@ -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());
},
),

View file

@ -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<BackupSectionWidget> {
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<BackupSectionWidget> {
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<BackupSectionWidget> {
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<BackupSectionWidget> {
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
showOnlyLoadingState: true,
onTap: () async {
final dialog = createProgressDialog(context, "Calculating...");
await dialog.show();
List<DuplicateFiles> duplicates;
try {
duplicates =
await DeduplicationService.instance.getDuplicateFiles();
} catch (e) {
await dialog.hide();
showGenericErrorDialog(context: context);
return;
}
await dialog.hide();
if (duplicates.isEmpty) {
showErrorDialog(
context,

View file

@ -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';

View file

@ -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<void> _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);
}

View file

@ -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<SecuritySectionWidget> {
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
showOnlyLoadingState: true,
onTap: () async {
final hasAuthenticated = await LocalAuthenticationService.instance
.requestLocalAuthentication(

View file

@ -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);
},
);

View file

@ -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) {

View file

@ -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 {

View file

@ -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';

View file

@ -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<AlbumParticipantsPage> {
onTap: isOwner
? () async {
if (isOwner) {
await _navigateToManageUser(currentUser);
_navigateToManageUser(currentUser);
}
}
: null,
@ -185,7 +185,7 @@ class _AlbumParticipantsPageState extends State<AlbumParticipantsPage> {
leadingIcon: Icons.add_outlined,
menuItemColor: getEnteColorScheme(context).fillFaint,
onTap: () async {
await _navigateToAddUser(false);
_navigateToAddUser(false);
},
isTopBorderRadiusRemoved: collaborators.isNotEmpty,
borderRadius: 8,
@ -259,7 +259,7 @@ class _AlbumParticipantsPageState extends State<AlbumParticipantsPage> {
leadingIcon: Icons.add_outlined,
menuItemColor: getEnteColorScheme(context).fillFaint,
onTap: () async {
await _navigateToAddUser(true);
_navigateToAddUser(true);
},
isTopBorderRadiusRemoved: viewers.isNotEmpty,
borderRadius: 8,

View file

@ -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,

View file

@ -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<ManageSharedLinkWidget> {
),
trailingIcon: Icons.chevron_right,
menuItemColor: enteColorScheme.fillFaint,
surfaceExecutionStates: false,
onTap: () async {
await showPicker();
},
@ -139,6 +140,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
onTap: () async {
await _showDeviceLimitPicker();
},
surfaceExecutionStates: false,
),
DividerWidget(
dividerType: DividerType.menuNoIcon,
@ -221,6 +223,7 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
leadingIcon: Icons.remove_circle_outline,
leadingIconColor: warning500,
menuItemColor: getEnteColorScheme(context).fillFaint,
surfaceExecutionStates: false,
onTap: () async {
final bool result = await sharingActions.disableUrl(
context,

View file

@ -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';
@ -164,6 +164,7 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
),
leadingIcon: Icons.copy,
menuItemColor: getEnteColorScheme(context).fillFaint,
showOnlyLoadingState: true,
onTap: () async {
await Clipboard.setData(ClipboardData(text: url));
showShortToast(context, "Link copied to clipboard");
@ -230,6 +231,7 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
leadingIcon: Icons.link,
menuItemColor: getEnteColorScheme(context).fillFaint,
isBottomBorderRadiusRemoved: true,
showOnlyLoadingState: true,
onTap: () async {
final bool result =
await collectionActions.enableUrl(context, widget.collection);
@ -257,6 +259,7 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
),
leadingIcon: Icons.link,
menuItemColor: getEnteColorScheme(context).fillFaint,
showOnlyLoadingState: true,
onTap: () async {
final bool result = await collectionActions.enableUrl(
context,

View file

@ -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<AppStorageViewer> {
menuItemColor:
getEnteColorScheme(context).fillFaint,
borderRadius: 8,
alwaysShowSuccessState: true,
onTap: () async {
for (var pathItem in paths) {
if (pathItem.allowCacheClear) {

View file

@ -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<PathStorageViewer> {
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<PathStorageViewer> {
menuItemColor: getEnteColorScheme(context).fillFaint,
isBottomBorderRadiusRemoved: widget.removeBottomRadius,
isTopBorderRadiusRemoved: widget.removeTopRadius,
showOnlyLoadingState: true,
onTap: () async {
if (kDebugMode) {
await Clipboard.setData(ClipboardData(text: widget.item.path));

View file

@ -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<ResetIgnoredFilesWidget> {
borderRadius: 8.0,
menuItemColor: getEnteColorScheme(context).fillFaint,
leadingIcon: Icons.cloud_off_outlined,
alwaysShowSuccessState: true,
onTap: () async {
await _removeFilesFromIgnoredFiles(
widget.filesInDeviceCollection,