import 'package:aves/app_mode.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/empty.dart'; import 'package:aves/widgets/settings/viewer/slideshow.dart'; import 'package:aves/widgets/viewer/controls/controller.dart'; import 'package:aves/widgets/viewer/entry_viewer_stack.dart'; import 'package:aves/widgets/viewer/providers.dart'; import 'package:aves_magnifier/aves_magnifier.dart'; import 'package:aves_model/aves_model.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class SlideshowPage extends StatefulWidget { static const routeName = '/collection/slideshow'; final CollectionLens collection; const SlideshowPage({ super.key, required this.collection, }); @override State createState() => _SlideshowPageState(); } class _SlideshowPageState extends State { final ValueNotifier _appModeNotifier = ValueNotifier(AppMode.slideshow); late ViewerController _viewerController; late CollectionLens _slideshowCollection; AvesEntry? _initialEntry; CollectionSource get source => widget.collection.source; @override void initState() { super.initState(); _initViewerController(autopilot: true); _initSlideshowCollection(); _initialEntry = _slideshowCollection.sortedEntries.firstOrNull; } @override void dispose() { _appModeNotifier.dispose(); _disposeViewerController(); super.dispose(); } @override Widget build(BuildContext context) { final initialEntry = _initialEntry; return ListenableProvider>.value( value: _appModeNotifier, child: AvesScaffold( body: initialEntry == null ? EmptyContent( icon: AIcons.image, text: context.l10n.collectionEmptyImages, alignment: Alignment.center, ) : MultiProvider( providers: [ ViewStateConductorProvider(), VideoConductorProvider(), MultiPageConductorProvider(), ], child: NotificationListener( onNotification: (notification) { _onActionSelected(notification.action); return true; }, child: EntryViewerStack( key: ValueKey(_viewerController), collection: _slideshowCollection, initialEntry: initialEntry, viewerController: _viewerController, ), ), ), ), ); } void _initViewerController({required bool autopilot}) { _viewerController = ViewerController( initialScale: ScaleLevel(ref: settings.slideshowFillScreen ? ScaleReference.covered : ScaleReference.contained), transition: settings.slideshowTransition, repeat: settings.slideshowRepeat, autopilot: autopilot, autopilotInterval: Duration(seconds: settings.slideshowInterval), autopilotAnimatedZoom: settings.slideshowAnimatedZoomEffect, ); } void _disposeViewerController() => _viewerController.dispose(); void _initSlideshowCollection() { var entries = List.of(widget.collection.sortedEntries); if (settings.slideshowVideoPlayback == SlideshowVideoPlayback.skip) { entries = entries.where((entry) => !MimeFilter.video.test(entry)).toList(); } if (settings.slideshowShuffle) { entries.shuffle(); } _slideshowCollection = CollectionLens( source: source, listenToSource: false, fixedSort: true, fixedSelection: entries, ); } void _onActionSelected(SlideshowAction action) { switch (action) { case SlideshowAction.resume: _viewerController.autopilot = true; case SlideshowAction.showInCollection: _showInCollection(); case SlideshowAction.cast: // ignore, as it should be handled at the viewer level break; case SlideshowAction.settings: _showSettings(context); } } void _showInCollection() { final currentEntry = _viewerController.entryNotifier.value; if (currentEntry == null) return; final album = currentEntry.directory; final uri = currentEntry.uri; Navigator.maybeOf(context)?.pushAndRemoveUntil( MaterialPageRoute( settings: const RouteSettings(name: CollectionPage.routeName), builder: (context) => CollectionPage( source: source, filters: album != null ? {AlbumFilter(album, source.getAlbumDisplayName(context, album))} : null, highlightTest: (entry) => entry.uri == uri, ), ), (route) => false, ); } (bool, bool) get collectionSettings => (settings.slideshowShuffle, settings.slideshowVideoPlayback == SlideshowVideoPlayback.skip); Future _showSettings(BuildContext context) async { final oldCollectionSettings = collectionSettings; final currentEntry = _viewerController.entryNotifier.value; await Navigator.maybeOf(context)?.push( MaterialPageRoute( settings: const RouteSettings(name: ViewerSlideshowPage.routeName), builder: (context) => const ViewerSlideshowPage(), ), ); _disposeViewerController(); _initViewerController(autopilot: false); if (oldCollectionSettings != collectionSettings) { _initSlideshowCollection(); } final slideshowEntries = _slideshowCollection.sortedEntries; _initialEntry = slideshowEntries.contains(currentEntry) ? currentEntry : slideshowEntries.firstOrNull; setState(() {}); } } class SlideshowActionNotification extends Notification { final SlideshowAction action; SlideshowActionNotification(this.action); }