diff --git a/mobile/packages/ui/lib/src/components/column_button.dart b/mobile/packages/ui/lib/src/components/column_button.dart index bf0023c1bc..f990dbc065 100644 --- a/mobile/packages/ui/lib/src/components/column_button.dart +++ b/mobile/packages/ui/lib/src/components/column_button.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:core'; import 'package:flutter/material.dart'; import 'package:immich_ui/src/constants.dart'; @@ -8,6 +9,7 @@ class ImmichColumnButton extends StatefulWidget { final IconData icon; final String label; final FutureOr Function() onPressed; + final FutureOr Function()? onLongPress; final bool disabled; final bool? loading; @@ -16,6 +18,7 @@ class ImmichColumnButton extends StatefulWidget { required this.icon, required this.label, required this.onPressed, + this.onLongPress, this.disabled = false, this.loading, }); @@ -25,26 +28,44 @@ class ImmichColumnButton extends StatefulWidget { } class _ImmichColumnButtonState extends State { - bool _loading = false; - bool get _isLoading => widget.loading ?? _loading; + bool _running = false; + bool get _isLoading => widget.loading ?? _running; + bool get _isDisabled => widget.disabled || _isLoading; - Future _onPressed() async { - setState(() => _loading = true); + Future _runAction(FutureOr Function() action) async { + setState(() => _running = true); try { - await widget.onPressed(); + await action(); } finally { if (mounted) { - setState(() => _loading = false); + setState(() => _running = false); } } } + Future? _onPressed() { + if (_isDisabled) { + return null; + } + + return _runAction(widget.onPressed); + } + + Future? _onLongPress() { + if (_isDisabled || widget.onLongPress == null) { + return null; + } + + return _runAction(widget.onLongPress!); + } + @override Widget build(BuildContext context) { final foreground = context.colorOverride ?? Theme.of(context).colorScheme.onSurface; return TextButton( - onPressed: widget.disabled || _isLoading ? null : _onPressed, + onPressed: _onPressed, + onLongPress: _onLongPress, style: TextButton.styleFrom( foregroundColor: foreground, padding: const .symmetric(horizontal: ImmichSpacing.sm, vertical: ImmichSpacing.md), diff --git a/mobile/packages/ui/lib/src/components/icon_button.dart b/mobile/packages/ui/lib/src/components/icon_button.dart index b9bdd3a406..1c02f30ed3 100644 --- a/mobile/packages/ui/lib/src/components/icon_button.dart +++ b/mobile/packages/ui/lib/src/components/icon_button.dart @@ -7,6 +7,7 @@ import 'package:immich_ui/src/internal.dart'; class ImmichIconButton extends StatefulWidget { final IconData icon; final FutureOr Function() onPressed; + final FutureOr Function()? onLongPress; final ImmichVariant variant; final ImmichColor color; final bool disabled; @@ -16,6 +17,7 @@ class ImmichIconButton extends StatefulWidget { super.key, required this.icon, required this.onPressed, + this.onLongPress, this.color = .primary, this.variant = .filled, this.disabled = false, @@ -27,20 +29,37 @@ class ImmichIconButton extends StatefulWidget { } class _ImmichIconButtonState extends State { - bool _loading = false; - bool get _isLoading => widget.loading ?? _loading; + bool _running = false; + bool get _isLoading => widget.loading ?? _running; + bool get _isDisabled => widget.disabled || _isLoading; - Future _onPressed() async { - setState(() => _loading = true); + Future _runAction(FutureOr Function() action) async { + setState(() => _running = true); try { - await widget.onPressed(); + await action(); } finally { if (mounted) { - setState(() => _loading = false); + setState(() => _running = false); } } } + Future? _onPressed() { + if (_isDisabled) { + return null; + } + + return _runAction(widget.onPressed); + } + + Future? _onLongPress() { + if (_isDisabled || widget.onLongPress == null) { + return null; + } + + return _runAction(widget.onLongPress!); + } + @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; @@ -73,7 +92,8 @@ class _ImmichIconButtonState extends State { child: CircularProgressIndicator(strokeWidth: ImmichBorderWidth.md), ) : Icon(widget.icon), - onPressed: widget.disabled || _isLoading ? null : _onPressed, + onPressed: _onPressed, + onLongPress: _onLongPress, style: IconButton.styleFrom(backgroundColor: background, foregroundColor: foreground), ); } diff --git a/mobile/packages/ui/lib/src/components/text_button.dart b/mobile/packages/ui/lib/src/components/text_button.dart index fb3459897c..bbcfe4a3a7 100644 --- a/mobile/packages/ui/lib/src/components/text_button.dart +++ b/mobile/packages/ui/lib/src/components/text_button.dart @@ -7,6 +7,7 @@ class ImmichTextButton extends StatefulWidget { final String labelText; final IconData? icon; final FutureOr Function() onPressed; + final FutureOr Function()? onLongPress; final ImmichVariant variant; final bool expanded; final bool disabled; @@ -17,6 +18,7 @@ class ImmichTextButton extends StatefulWidget { required this.labelText, this.icon, required this.onPressed, + this.onLongPress, this.variant = .filled, this.expanded = true, @@ -29,20 +31,37 @@ class ImmichTextButton extends StatefulWidget { } class _ImmichTextButtonState extends State { - bool _loading = false; - bool get _isLoading => widget.loading ?? _loading; + bool _running = false; + bool get _isLoading => widget.loading ?? _running; + bool get _isDisabled => widget.disabled || _isLoading; - Future _onPressed() async { - setState(() => _loading = true); + Future _runAction(FutureOr Function() action) async { + setState(() => _running = true); try { - await widget.onPressed(); + await action(); } finally { if (mounted) { - setState(() => _loading = false); + setState(() => _running = false); } } } + Future? _onPressed() { + if (_isDisabled) { + return null; + } + + return _runAction(widget.onPressed); + } + + Future? _onLongPress() { + if (_isDisabled || widget.onLongPress == null) { + return null; + } + + return _runAction(widget.onLongPress!); + } + @override Widget build(BuildContext context) { final Widget? icon = _isLoading @@ -59,11 +78,22 @@ class _ImmichTextButtonState extends State { style: const .new(fontSize: ImmichTextSize.body, fontWeight: .bold), ); final style = ElevatedButton.styleFrom(padding: const .symmetric(vertical: ImmichSpacing.md)); - final onPressed = widget.disabled || _isLoading ? null : _onPressed; final button = switch (widget.variant) { - ImmichVariant.filled => ElevatedButton.icon(style: style, onPressed: onPressed, icon: icon, label: label), - ImmichVariant.ghost => TextButton.icon(style: style, onPressed: onPressed, icon: icon, label: label), + ImmichVariant.filled => ElevatedButton.icon( + style: style, + onPressed: _onPressed, + onLongPress: _onLongPress, + icon: icon, + label: label, + ), + ImmichVariant.ghost => TextButton.icon( + style: style, + onPressed: _onPressed, + onLongPress: _onLongPress, + icon: icon, + label: label, + ), }; if (widget.expanded) {