#243 snack bar action: fixed countdown reset, fixed trigger after navigation
This commit is contained in:
parent
4759fd52f2
commit
d358116219
7 changed files with 236 additions and 74 deletions
|
@ -43,6 +43,8 @@ import 'package:tuple/tuple.dart';
|
||||||
class AvesApp extends StatefulWidget {
|
class AvesApp extends StatefulWidget {
|
||||||
final AppFlavor flavor;
|
final AppFlavor flavor;
|
||||||
|
|
||||||
|
static final GlobalKey<NavigatorState> navigatorKey = GlobalKey(debugLabel: 'app-navigator');
|
||||||
|
|
||||||
const AvesApp({
|
const AvesApp({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.flavor,
|
required this.flavor,
|
||||||
|
@ -66,7 +68,6 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
||||||
final EventChannel _newIntentChannel = const EventChannel('deckers.thibault/aves/intent');
|
final EventChannel _newIntentChannel = const EventChannel('deckers.thibault/aves/intent');
|
||||||
final EventChannel _analysisCompletionChannel = const EventChannel('deckers.thibault/aves/analysis_events');
|
final EventChannel _analysisCompletionChannel = const EventChannel('deckers.thibault/aves/analysis_events');
|
||||||
final EventChannel _errorChannel = const EventChannel('deckers.thibault/aves/error');
|
final EventChannel _errorChannel = const EventChannel('deckers.thibault/aves/error');
|
||||||
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey(debugLabel: 'app-navigator');
|
|
||||||
|
|
||||||
Widget getFirstPage({Map? intentData}) => settings.hasAcceptedTerms ? HomePage(intentData: intentData) : const WelcomePage();
|
Widget getFirstPage({Map? intentData}) => settings.hasAcceptedTerms ? HomePage(intentData: intentData) : const WelcomePage();
|
||||||
|
|
||||||
|
@ -117,7 +118,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
||||||
final areAnimationsEnabled = s.item2;
|
final areAnimationsEnabled = s.item2;
|
||||||
final themeBrightness = s.item3;
|
final themeBrightness = s.item3;
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
navigatorKey: _navigatorKey,
|
navigatorKey: AvesApp.navigatorKey,
|
||||||
home: home,
|
home: home,
|
||||||
navigatorObservers: _navigatorObservers,
|
navigatorObservers: _navigatorObservers,
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
|
@ -294,7 +295,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
||||||
if (appModeNotifier.value == AppMode.main && (intentData == null || intentData.isEmpty == true)) return;
|
if (appModeNotifier.value == AppMode.main && (intentData == null || intentData.isEmpty == true)) return;
|
||||||
|
|
||||||
reportService.log('New intent');
|
reportService.log('New intent');
|
||||||
_navigatorKey.currentState!.pushReplacement(DirectMaterialPageRoute(
|
AvesApp.navigatorKey.currentState!.pushReplacement(DirectMaterialPageRoute(
|
||||||
settings: const RouteSettings(name: HomePage.routeName),
|
settings: const RouteSettings(name: HomePage.routeName),
|
||||||
builder: (_) => getFirstPage(intentData: intentData),
|
builder: (_) => getFirstPage(intentData: intentData),
|
||||||
));
|
));
|
||||||
|
|
|
@ -18,6 +18,7 @@ import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/services/media/enums.dart';
|
import 'package:aves/services/media/enums.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/utils/android_file_utils.dart';
|
import 'package:aves/utils/android_file_utils.dart';
|
||||||
|
import 'package:aves/widgets/aves_app.dart';
|
||||||
import 'package:aves/widgets/collection/collection_page.dart';
|
import 'package:aves/widgets/collection/collection_page.dart';
|
||||||
import 'package:aves/widgets/collection/entry_set_action_delegate.dart';
|
import 'package:aves/widgets/collection/entry_set_action_delegate.dart';
|
||||||
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
||||||
|
@ -161,18 +162,30 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
|
||||||
action = SnackBarAction(
|
action = SnackBarAction(
|
||||||
// TODO TLAD [l10n] key for "RESTORE"
|
// TODO TLAD [l10n] key for "RESTORE"
|
||||||
label: l10n.entryActionRestore.toUpperCase(),
|
label: l10n.entryActionRestore.toUpperCase(),
|
||||||
onPressed: () => move(
|
onPressed: () {
|
||||||
context,
|
// local context may be deactivated when action is triggered after navigation
|
||||||
moveType: MoveType.fromBin,
|
final context = AvesApp.navigatorKey.currentContext;
|
||||||
entries: movedEntries,
|
if (context != null) {
|
||||||
hideShowAction: true,
|
move(
|
||||||
),
|
context,
|
||||||
|
moveType: MoveType.fromBin,
|
||||||
|
entries: movedEntries,
|
||||||
|
hideShowAction: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (!hideShowAction) {
|
} else if (!hideShowAction) {
|
||||||
action = SnackBarAction(
|
action = SnackBarAction(
|
||||||
label: l10n.showButtonLabel,
|
label: l10n.showButtonLabel,
|
||||||
onPressed: () => _showMovedItems(context, destinationAlbums, movedOps),
|
onPressed: () {
|
||||||
|
// local context may be deactivated when action is triggered after navigation
|
||||||
|
final context = AvesApp.navigatorKey.currentContext;
|
||||||
|
if (context != null) {
|
||||||
|
_showMovedItems(context, destinationAlbums, movedOps);
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/services/accessibility_service.dart';
|
import 'package:aves/services/accessibility_service.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/theme/icons.dart';
|
import 'package:aves/theme/icons.dart';
|
||||||
|
import 'package:aves/widgets/common/basic/circle.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/viewer/entry_viewer_page.dart';
|
import 'package:aves/widgets/viewer/entry_viewer_page.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
@ -36,10 +37,12 @@ mixin FeedbackMixin {
|
||||||
// provide the messenger if feedback happens as the widget is disposed
|
// provide the messenger if feedback happens as the widget is disposed
|
||||||
void showFeedbackWithMessenger(BuildContext context, ScaffoldMessengerState messenger, String message, [SnackBarAction? action]) {
|
void showFeedbackWithMessenger(BuildContext context, ScaffoldMessengerState messenger, String message, [SnackBarAction? action]) {
|
||||||
_getSnackBarDuration(action != null).then((duration) {
|
_getSnackBarDuration(action != null).then((duration) {
|
||||||
|
final start = DateTime.now();
|
||||||
final snackBarContent = _FeedbackMessage(
|
final snackBarContent = _FeedbackMessage(
|
||||||
message: message,
|
message: message,
|
||||||
progressColor: Theme.of(context).colorScheme.secondary,
|
progressColor: Theme.of(context).colorScheme.secondary,
|
||||||
duration: action != null ? duration : null,
|
start: start,
|
||||||
|
stop: action != null ? start.add(duration) : null,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (context.currentRouteName == EntryViewerPage.routeName) {
|
if (context.currentRouteName == EntryViewerPage.routeName) {
|
||||||
|
@ -65,6 +68,7 @@ mixin FeedbackMixin {
|
||||||
// the regular snack bar dismiss behavior is confused
|
// the regular snack bar dismiss behavior is confused
|
||||||
// because it expects a `Scaffold` in context,
|
// because it expects a `Scaffold` in context,
|
||||||
// so we manually dimiss the overlay entry
|
// so we manually dimiss the overlay entry
|
||||||
|
// TODO TLAD [bug] after dismissing the overlay, tapping on the snack bar area makes the overlay visible again
|
||||||
notificationOverlayEntry?.dismiss();
|
notificationOverlayEntry?.dismiss();
|
||||||
action.onPressed();
|
action.onPressed();
|
||||||
},
|
},
|
||||||
|
@ -273,72 +277,84 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
|
||||||
|
|
||||||
class _FeedbackMessage extends StatefulWidget {
|
class _FeedbackMessage extends StatefulWidget {
|
||||||
final String message;
|
final String message;
|
||||||
final Duration? duration;
|
final DateTime? start, stop;
|
||||||
final Color progressColor;
|
final Color progressColor;
|
||||||
|
|
||||||
const _FeedbackMessage({
|
const _FeedbackMessage({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.message,
|
required this.message,
|
||||||
required this.progressColor,
|
required this.progressColor,
|
||||||
this.duration,
|
this.start,
|
||||||
|
this.stop,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_FeedbackMessage> createState() => _FeedbackMessageState();
|
State<_FeedbackMessage> createState() => _FeedbackMessageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FeedbackMessageState extends State<_FeedbackMessage> {
|
class _FeedbackMessageState extends State<_FeedbackMessage> with SingleTickerProviderStateMixin {
|
||||||
double _percent = 0;
|
AnimationController? _animationController;
|
||||||
late int _remainingSecs;
|
Animation<int>? _remainingDurationMillis;
|
||||||
Timer? _timer;
|
int? _totalDurationMillis;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final duration = widget.duration;
|
final start = widget.start;
|
||||||
if (duration != null) {
|
final stop = widget.stop;
|
||||||
_remainingSecs = duration.inSeconds;
|
if (start != null && stop != null) {
|
||||||
_timer = Timer.periodic(const Duration(seconds: 1), (_) {
|
_totalDurationMillis = stop.difference(start).inMilliseconds;
|
||||||
setState(() => _remainingSecs--);
|
final remainingDuration = stop.difference(DateTime.now());
|
||||||
});
|
_animationController = AnimationController(
|
||||||
WidgetsBinding.instance!.addPostFrameCallback((_) => setState(() => _percent = 1.0));
|
duration: remainingDuration,
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
_remainingDurationMillis = IntTween(
|
||||||
|
begin: remainingDuration.inMilliseconds,
|
||||||
|
end: 0,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: _animationController!,
|
||||||
|
curve: Curves.linear,
|
||||||
|
));
|
||||||
|
_animationController!.forward();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_timer?.cancel();
|
_animationController?.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final text = Text(widget.message);
|
final text = Text(widget.message);
|
||||||
final duration = widget.duration;
|
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final contentTextStyle = theme.snackBarTheme.contentTextStyle ?? ThemeData(brightness: theme.brightness).textTheme.subtitle1;
|
final contentTextStyle = theme.snackBarTheme.contentTextStyle ?? ThemeData(brightness: theme.brightness).textTheme.subtitle1;
|
||||||
return duration == null
|
return _remainingDurationMillis == null
|
||||||
? text
|
? text
|
||||||
: Row(
|
: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: text),
|
Expanded(child: text),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
CircularPercentIndicator(
|
AnimatedBuilder(
|
||||||
percent: _percent,
|
animation: _remainingDurationMillis!,
|
||||||
lineWidth: 2,
|
builder: (context, child) {
|
||||||
radius: 16,
|
final remainingDurationMillis = _remainingDurationMillis!.value;
|
||||||
// progress color is provided by the caller,
|
return CircularIndicator(
|
||||||
// because we cannot use the app context theme here
|
radius: 16,
|
||||||
backgroundColor: widget.progressColor,
|
lineWidth: 2,
|
||||||
progressColor: Colors.grey,
|
percent: remainingDurationMillis / _totalDurationMillis!,
|
||||||
animation: true,
|
background: Colors.grey,
|
||||||
animationDuration: duration.inMilliseconds,
|
// progress color is provided by the caller,
|
||||||
center: Text(
|
// because we cannot use the app context theme here
|
||||||
'$_remainingSecs',
|
foreground: widget.progressColor,
|
||||||
style: contentTextStyle,
|
center: Text(
|
||||||
),
|
'${(remainingDurationMillis / 1000).ceil()}',
|
||||||
animateFromLastPercent: true,
|
style: contentTextStyle,
|
||||||
reverse: true,
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
109
lib/widgets/common/basic/circle.dart
Normal file
109
lib/widgets/common/basic/circle.dart
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CircularIndicator extends StatefulWidget {
|
||||||
|
final double radius, lineWidth, percent;
|
||||||
|
final Color background, foreground;
|
||||||
|
final Widget center;
|
||||||
|
|
||||||
|
const CircularIndicator({
|
||||||
|
Key? key,
|
||||||
|
required this.radius,
|
||||||
|
required this.lineWidth,
|
||||||
|
required this.percent,
|
||||||
|
required this.background,
|
||||||
|
required this.foreground,
|
||||||
|
required this.center,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CircularIndicator> createState() => _CircularIndicatorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CircularIndicatorState extends State<CircularIndicator> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox.square(
|
||||||
|
dimension: widget.radius * 2,
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Circle(
|
||||||
|
radius: widget.radius,
|
||||||
|
lineWidth: widget.lineWidth,
|
||||||
|
percent: 1.0,
|
||||||
|
color: widget.background,
|
||||||
|
),
|
||||||
|
Circle(
|
||||||
|
radius: widget.radius,
|
||||||
|
lineWidth: widget.lineWidth,
|
||||||
|
percent: widget.percent,
|
||||||
|
color: widget.foreground,
|
||||||
|
),
|
||||||
|
widget.center,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Circle extends StatelessWidget {
|
||||||
|
final double radius, lineWidth, percent;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
const Circle({
|
||||||
|
Key? key,
|
||||||
|
required this.radius,
|
||||||
|
required this.lineWidth,
|
||||||
|
required this.percent,
|
||||||
|
required this.color,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return CustomPaint(
|
||||||
|
size: Size.square(radius),
|
||||||
|
painter: _CirclePainter(
|
||||||
|
lineWidth: lineWidth,
|
||||||
|
radius: radius - lineWidth / 2,
|
||||||
|
color: color,
|
||||||
|
percent: percent,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CirclePainter extends CustomPainter {
|
||||||
|
final double radius, lineWidth, percent;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
const _CirclePainter({
|
||||||
|
required this.radius,
|
||||||
|
required this.lineWidth,
|
||||||
|
required this.percent,
|
||||||
|
required this.color,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
final center = Offset(size.width / 2, size.height / 2);
|
||||||
|
final paint = Paint()
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..color = color
|
||||||
|
..strokeWidth = lineWidth;
|
||||||
|
|
||||||
|
canvas.translate(center.dx, center.dy);
|
||||||
|
canvas.rotate(-pi / 2);
|
||||||
|
canvas.drawArc(
|
||||||
|
Rect.fromCircle(center: Offset.zero, radius: radius),
|
||||||
|
0,
|
||||||
|
2 * pi * percent,
|
||||||
|
false,
|
||||||
|
paint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/services/media/enums.dart';
|
import 'package:aves/services/media/enums.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/utils/android_file_utils.dart';
|
import 'package:aves/utils/android_file_utils.dart';
|
||||||
|
import 'package:aves/widgets/aves_app.dart';
|
||||||
import 'package:aves/widgets/common/action_mixins/entry_storage.dart';
|
import 'package:aves/widgets/common/action_mixins/entry_storage.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
||||||
|
@ -173,9 +174,25 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with
|
||||||
final showAction = SnackBarAction(
|
final showAction = SnackBarAction(
|
||||||
label: context.l10n.showButtonLabel,
|
label: context.l10n.showButtonLabel,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
// assume Album page is still the current page when action is triggered
|
// local context may be deactivated when action is triggered after navigation
|
||||||
final filter = AlbumFilter(newAlbum, source.getAlbumDisplayName(context, newAlbum));
|
final context = AvesApp.navigatorKey.currentContext;
|
||||||
context.read<HighlightInfo>().trackItem(FilterGridItem(filter, null), highlightItem: filter);
|
if (context != null) {
|
||||||
|
final highlightInfo = context.read<HighlightInfo>();
|
||||||
|
final filter = AlbumFilter(newAlbum, source.getAlbumDisplayName(context, newAlbum));
|
||||||
|
if (context.currentRouteName == AlbumListPage.routeName) {
|
||||||
|
highlightInfo.trackItem(FilterGridItem(filter, null), highlightItem: filter);
|
||||||
|
} else {
|
||||||
|
highlightInfo.set(filter);
|
||||||
|
await Navigator.pushAndRemoveUntil(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
settings: const RouteSettings(name: AlbumListPage.routeName),
|
||||||
|
builder: (_) => const AlbumListPage(),
|
||||||
|
),
|
||||||
|
(route) => false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
showFeedback(context, context.l10n.genericSuccessFeedback, showAction);
|
showFeedback(context, context.l10n.genericSuccessFeedback, showAction);
|
||||||
|
|
|
@ -16,6 +16,7 @@ import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/services/media/enums.dart';
|
import 'package:aves/services/media/enums.dart';
|
||||||
import 'package:aves/services/media/media_file_service.dart';
|
import 'package:aves/services/media/media_file_service.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
|
import 'package:aves/widgets/aves_app.dart';
|
||||||
import 'package:aves/widgets/collection/collection_page.dart';
|
import 'package:aves/widgets/collection/collection_page.dart';
|
||||||
import 'package:aves/widgets/common/action_mixins/entry_storage.dart';
|
import 'package:aves/widgets/common/action_mixins/entry_storage.dart';
|
||||||
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
||||||
|
@ -259,24 +260,26 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
||||||
source.refreshUris(newUris);
|
source.refreshUris(newUris);
|
||||||
|
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final navigator = Navigator.of(context);
|
|
||||||
final showAction = isMainMode && newUris.isNotEmpty
|
final showAction = isMainMode && newUris.isNotEmpty
|
||||||
? SnackBarAction(
|
? SnackBarAction(
|
||||||
label: l10n.showButtonLabel,
|
label: l10n.showButtonLabel,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// `context` may be obsolete if the user navigated away before triggering the action
|
// local context may be deactivated when action is triggered after navigation
|
||||||
// so we reused the navigator retrieved before showing the snack bar
|
final context = AvesApp.navigatorKey.currentContext;
|
||||||
navigator.pushAndRemoveUntil(
|
if (context != null) {
|
||||||
MaterialPageRoute(
|
Navigator.pushAndRemoveUntil(
|
||||||
settings: const RouteSettings(name: CollectionPage.routeName),
|
context,
|
||||||
builder: (context) => CollectionPage(
|
MaterialPageRoute(
|
||||||
source: source,
|
settings: const RouteSettings(name: CollectionPage.routeName),
|
||||||
filters: {AlbumFilter(destinationAlbum, source.getAlbumDisplayName(context, destinationAlbum))},
|
builder: (context) => CollectionPage(
|
||||||
highlightTest: (entry) => newUris.contains(entry.uri),
|
source: source,
|
||||||
|
filters: {AlbumFilter(destinationAlbum, source.getAlbumDisplayName(context, destinationAlbum))},
|
||||||
|
highlightTest: (entry) => newUris.contains(entry.uri),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
(route) => false,
|
||||||
(route) => false,
|
);
|
||||||
);
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/services/media/enums.dart';
|
import 'package:aves/services/media/enums.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/utils/android_file_utils.dart';
|
import 'package:aves/utils/android_file_utils.dart';
|
||||||
|
import 'package:aves/widgets/aves_app.dart';
|
||||||
import 'package:aves/widgets/collection/collection_page.dart';
|
import 'package:aves/widgets/collection/collection_page.dart';
|
||||||
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
||||||
import 'package:aves/widgets/common/action_mixins/permission_aware.dart';
|
import 'package:aves/widgets/common/action_mixins/permission_aware.dart';
|
||||||
|
@ -110,26 +111,28 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
if (success) {
|
if (success) {
|
||||||
final _collection = collection;
|
final _collection = collection;
|
||||||
final navigator = Navigator.of(context);
|
|
||||||
final showAction = _collection != null
|
final showAction = _collection != null
|
||||||
? SnackBarAction(
|
? SnackBarAction(
|
||||||
label: l10n.showButtonLabel,
|
label: l10n.showButtonLabel,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final source = _collection.source;
|
// local context may be deactivated when action is triggered after navigation
|
||||||
final newUri = newFields['uri'] as String?;
|
final context = AvesApp.navigatorKey.currentContext;
|
||||||
// `context` may be obsolete if the user navigated away before triggering the action
|
if (context != null) {
|
||||||
// so we reused the navigator retrieved before showing the snack bar
|
final source = _collection.source;
|
||||||
navigator.pushAndRemoveUntil(
|
final newUri = newFields['uri'] as String?;
|
||||||
MaterialPageRoute(
|
Navigator.pushAndRemoveUntil(
|
||||||
settings: const RouteSettings(name: CollectionPage.routeName),
|
context,
|
||||||
builder: (context) => CollectionPage(
|
MaterialPageRoute(
|
||||||
source: source,
|
settings: const RouteSettings(name: CollectionPage.routeName),
|
||||||
filters: {AlbumFilter(destinationAlbum, source.getAlbumDisplayName(context, destinationAlbum))},
|
builder: (context) => CollectionPage(
|
||||||
highlightTest: (entry) => entry.uri == newUri,
|
source: source,
|
||||||
|
filters: {AlbumFilter(destinationAlbum, source.getAlbumDisplayName(context, destinationAlbum))},
|
||||||
|
highlightTest: (entry) => entry.uri == newUri,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
(route) => false,
|
||||||
(route) => false,
|
);
|
||||||
);
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
|
|
Loading…
Reference in a new issue