feat: actions undo handling (#30481)
* feat: actions undo handling # Conflicts: # mobile/lib/main.dart * address pr reviews --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
co-authored by
shenlong-tanwen
parent
1539ae8b07
commit
ea3fa92776
@@ -1,8 +1,19 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_ui/immich_ui.dart';
|
||||
import 'package:immich_ui/src/internal.dart';
|
||||
|
||||
final scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||
|
||||
class SnackbarAction {
|
||||
// Optional label for the action button. Falls back to the localized "Undo" label when null
|
||||
final String? label;
|
||||
final FutureOr<void> Function() onPressed;
|
||||
|
||||
const SnackbarAction({this.label, required this.onPressed});
|
||||
}
|
||||
|
||||
class SnackbarManager {
|
||||
const SnackbarManager();
|
||||
|
||||
@@ -10,6 +21,7 @@ class SnackbarManager {
|
||||
String message,
|
||||
SnackbarType type, {
|
||||
Duration? duration,
|
||||
SnackbarAction? action,
|
||||
}) {
|
||||
final messenger = scaffoldMessengerKey.currentState;
|
||||
final context = scaffoldMessengerKey.currentContext;
|
||||
@@ -19,10 +31,10 @@ class SnackbarManager {
|
||||
|
||||
duration ??= const .new(seconds: 4);
|
||||
messenger.hideCurrentSnackBar();
|
||||
return messenger.showSnackBar(_build(context, message, type, duration));
|
||||
return messenger.showSnackBar(_build(context, message, type, duration, action));
|
||||
}
|
||||
|
||||
SnackBar _build(BuildContext context, String message, SnackbarType type, Duration duration) {
|
||||
SnackBar _build(BuildContext context, String message, SnackbarType type, Duration duration, SnackbarAction? action) {
|
||||
final theme = Theme.of(context);
|
||||
final colors = theme.extension<ImmichColors>() ?? ImmichColors.harmonized(theme.colorScheme);
|
||||
final (IconData icon, Color background, Color foreground) = switch (type) {
|
||||
@@ -31,11 +43,22 @@ class SnackbarManager {
|
||||
.error => (Icons.warning_rounded, colors.error, colors.onError),
|
||||
};
|
||||
|
||||
SnackBarAction? snackAction;
|
||||
if (action != null) {
|
||||
snackAction = .new(
|
||||
label: action.label ?? context.translations.undo,
|
||||
onPressed: action.onPressed,
|
||||
textColor: foreground,
|
||||
);
|
||||
}
|
||||
|
||||
return SnackBar(
|
||||
behavior: .floating,
|
||||
backgroundColor: background,
|
||||
duration: duration,
|
||||
shape: const RoundedRectangleBorder(borderRadius: .all(.circular(ImmichRadius.sm))),
|
||||
// A snackbar carrying an action stays up until it is tapped by default; our actions are optional
|
||||
persist: false,
|
||||
content: Row(
|
||||
children: [
|
||||
Icon(icon, color: foreground, size: ImmichIconSize.sm),
|
||||
@@ -50,17 +73,27 @@ class SnackbarManager {
|
||||
),
|
||||
],
|
||||
),
|
||||
action: snackAction,
|
||||
);
|
||||
}
|
||||
|
||||
ScaffoldFeatureController<SnackBar, SnackBarClosedReason>? info(String message, {Duration? duration}) =>
|
||||
show(message, .info, duration: duration);
|
||||
ScaffoldFeatureController<SnackBar, SnackBarClosedReason>? info(
|
||||
String message, {
|
||||
Duration? duration,
|
||||
SnackbarAction? action,
|
||||
}) => show(message, .info, duration: duration, action: action);
|
||||
|
||||
ScaffoldFeatureController<SnackBar, SnackBarClosedReason>? success(String message, {Duration? duration}) =>
|
||||
show(message, .success, duration: duration);
|
||||
ScaffoldFeatureController<SnackBar, SnackBarClosedReason>? success(
|
||||
String message, {
|
||||
Duration? duration,
|
||||
SnackbarAction? action,
|
||||
}) => show(message, .success, duration: duration, action: action);
|
||||
|
||||
ScaffoldFeatureController<SnackBar, SnackBarClosedReason>? error(String message, {Duration? duration}) =>
|
||||
show(message, .error, duration: duration);
|
||||
ScaffoldFeatureController<SnackBar, SnackBarClosedReason>? error(
|
||||
String message, {
|
||||
Duration? duration,
|
||||
SnackbarAction? action,
|
||||
}) => show(message, .error, duration: duration, action: action);
|
||||
}
|
||||
|
||||
const snackbar = SnackbarManager();
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ImmichTranslations {
|
||||
late String submit;
|
||||
late String password;
|
||||
final String submit;
|
||||
final String password;
|
||||
final String undo;
|
||||
|
||||
ImmichTranslations({String? submit, String? password}) {
|
||||
this.submit = submit ?? 'Submit';
|
||||
this.password = password ?? 'Password';
|
||||
}
|
||||
const ImmichTranslations({String? submit, String? password, String? undo})
|
||||
: submit = submit ?? 'Submit',
|
||||
password = password ?? 'Password',
|
||||
undo = undo ?? 'Undo';
|
||||
}
|
||||
|
||||
class ImmichTranslationProvider extends InheritedWidget {
|
||||
final ImmichTranslations? translations;
|
||||
|
||||
const ImmichTranslationProvider({
|
||||
super.key,
|
||||
this.translations,
|
||||
required super.child,
|
||||
});
|
||||
const ImmichTranslationProvider({super.key, this.translations, required super.child});
|
||||
|
||||
static ImmichTranslations of(BuildContext context) {
|
||||
final provider = context.dependOnInheritedWidgetOfExactType<ImmichTranslationProvider>();
|
||||
return provider?.translations ?? ImmichTranslations();
|
||||
return provider?.translations ?? const .new();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
Reference in New Issue
Block a user