Fix SlideshowViewer not working with shared files in NcAlbum

This commit is contained in:
Ming Ming 2024-12-12 23:52:44 +08:00
parent 71201dab74
commit 8483cccb35
7 changed files with 124 additions and 26 deletions

View file

@ -10,7 +10,9 @@ import 'package:nc_photos/account.dart';
import 'package:nc_photos/app_localizations.dart'; import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/bloc_util.dart'; import 'package:nc_photos/bloc_util.dart';
import 'package:nc_photos/controller/account_controller.dart'; import 'package:nc_photos/controller/account_controller.dart';
import 'package:nc_photos/controller/collections_controller.dart';
import 'package:nc_photos/controller/files_controller.dart'; import 'package:nc_photos/controller/files_controller.dart';
import 'package:nc_photos/entity/collection_item.dart';
import 'package:nc_photos/entity/file_descriptor.dart'; import 'package:nc_photos/entity/file_descriptor.dart';
import 'package:nc_photos/entity/file_util.dart' as file_util; import 'package:nc_photos/entity/file_util.dart' as file_util;
import 'package:nc_photos/k.dart' as k; import 'package:nc_photos/k.dart' as k;
@ -25,6 +27,7 @@ import 'package:nc_photos/widget/video_viewer.dart';
import 'package:nc_photos/widget/viewer_mixin.dart'; import 'package:nc_photos/widget/viewer_mixin.dart';
import 'package:nc_photos/widget/wakelock_util.dart'; import 'package:nc_photos/widget/wakelock_util.dart';
import 'package:np_codegen/np_codegen.dart'; import 'package:np_codegen/np_codegen.dart';
import 'package:np_collection/np_collection.dart';
import 'package:np_common/object_util.dart'; import 'package:np_common/object_util.dart';
import 'package:np_ui/np_ui.dart'; import 'package:np_ui/np_ui.dart';
import 'package:to_string/to_string.dart'; import 'package:to_string/to_string.dart';
@ -37,18 +40,19 @@ part 'slideshow_viewer/view.dart';
class SlideshowViewerArguments { class SlideshowViewerArguments {
const SlideshowViewerArguments( const SlideshowViewerArguments(
this.account,
this.fileIds, this.fileIds,
this.startIndex, this.startIndex,
this.collectionId,
this.config, this.config,
); );
final Account account;
final List<int> fileIds; final List<int> fileIds;
final int startIndex; final int startIndex;
final String? collectionId;
final SlideshowConfig config; final SlideshowConfig config;
} }
// fix for shared files
class SlideshowViewer extends StatelessWidget { class SlideshowViewer extends StatelessWidget {
static const routeName = "/slideshow-viewer"; static const routeName = "/slideshow-viewer";
@ -61,38 +65,41 @@ class SlideshowViewer extends StatelessWidget {
const SlideshowViewer({ const SlideshowViewer({
super.key, super.key,
required this.account,
required this.fileIds, required this.fileIds,
required this.startIndex, required this.startIndex,
required this.collectionId,
required this.config, required this.config,
}); });
SlideshowViewer.fromArgs(SlideshowViewerArguments args, {Key? key}) SlideshowViewer.fromArgs(SlideshowViewerArguments args, {Key? key})
: this( : this(
key: key, key: key,
account: args.account,
fileIds: args.fileIds, fileIds: args.fileIds,
startIndex: args.startIndex, startIndex: args.startIndex,
collectionId: args.collectionId,
config: args.config, config: args.config,
); );
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final accountController = context.read<AccountController>();
return BlocProvider( return BlocProvider(
create: (context) => _Bloc( create: (context) => _Bloc(
filesController: context.read<AccountController>().filesController, account: accountController.account,
account: context.read<AccountController>().account, filesController: accountController.filesController,
collectionsController: accountController.collectionsController,
fileIds: fileIds, fileIds: fileIds,
startIndex: startIndex, startIndex: startIndex,
collectionId: collectionId,
config: config, config: config,
)..add(const _Init()), )..add(const _Init()),
child: const _WrappedSlideshowViewer(), child: const _WrappedSlideshowViewer(),
); );
} }
final Account account;
final List<int> fileIds; final List<int> fileIds;
final int startIndex; final int startIndex;
final String? collectionId;
final SlideshowConfig config; final SlideshowConfig config;
} }
@ -166,6 +173,7 @@ class _WrappedSlideshowViewerState extends State<_WrappedSlideshowViewer>
typedef _BlocListener = BlocListener<_Bloc, _State>; typedef _BlocListener = BlocListener<_Bloc, _State>;
typedef _BlocListenerT<T> = BlocListenerT<_Bloc, _State, T>; typedef _BlocListenerT<T> = BlocListenerT<_Bloc, _State, T>;
typedef _BlocSelector<T> = BlocSelector<_Bloc, _State, T>; typedef _BlocSelector<T> = BlocSelector<_Bloc, _State, T>;
typedef _Emitter = Emitter<_State>;
extension on BuildContext { extension on BuildContext {
_Bloc get bloc => read<_Bloc>(); _Bloc get bloc => read<_Bloc>();

View file

@ -18,6 +18,8 @@ abstract class $_StateCopyWithWorker {
int? page, int? page,
int? nextPage, int? nextPage,
bool? shouldAnimateNextPage, bool? shouldAnimateNextPage,
Map<int, FileDescriptor>? rawFiles,
Map<int, CollectionFileItem>? collectionItems,
List<FileDescriptor?>? files, List<FileDescriptor?>? files,
FileDescriptor? currentFile, FileDescriptor? currentFile,
bool? isShowUi, bool? isShowUi,
@ -39,6 +41,8 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
dynamic page, dynamic page,
dynamic nextPage, dynamic nextPage,
dynamic shouldAnimateNextPage, dynamic shouldAnimateNextPage,
dynamic rawFiles,
dynamic collectionItems = copyWithNull,
dynamic files, dynamic files,
dynamic currentFile = copyWithNull, dynamic currentFile = copyWithNull,
dynamic isShowUi, dynamic isShowUi,
@ -55,6 +59,10 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
nextPage: nextPage as int? ?? that.nextPage, nextPage: nextPage as int? ?? that.nextPage,
shouldAnimateNextPage: shouldAnimateNextPage:
shouldAnimateNextPage as bool? ?? that.shouldAnimateNextPage, shouldAnimateNextPage as bool? ?? that.shouldAnimateNextPage,
rawFiles: rawFiles as Map<int, FileDescriptor>? ?? that.rawFiles,
collectionItems: collectionItems == copyWithNull
? that.collectionItems
: collectionItems as Map<int, CollectionFileItem>?,
files: files as List<FileDescriptor?>? ?? that.files, files: files as List<FileDescriptor?>? ?? that.files,
currentFile: currentFile == copyWithNull currentFile: currentFile == copyWithNull
? that.currentFile ? that.currentFile
@ -109,7 +117,7 @@ extension _$_PageViewNpLog on _PageView {
extension _$_StateToString on _State { extension _$_StateToString on _State {
String _$toString() { String _$toString() {
// ignore: unnecessary_string_interpolations // ignore: unnecessary_string_interpolations
return "_State {hasInit: $hasInit, page: $page, nextPage: $nextPage, shouldAnimateNextPage: $shouldAnimateNextPage, files: [length: ${files.length}], currentFile: ${currentFile == null ? null : "${currentFile!.fdPath}"}, isShowUi: $isShowUi, isPlay: $isPlay, isVideoCompleted: $isVideoCompleted, hasPrev: $hasPrev, hasNext: $hasNext, isShowTimeline: $isShowTimeline, hasShownTimeline: $hasShownTimeline, hasRequestExit: $hasRequestExit}"; return "_State {hasInit: $hasInit, page: $page, nextPage: $nextPage, shouldAnimateNextPage: $shouldAnimateNextPage, rawFiles: {length: ${rawFiles.length}}, collectionItems: ${collectionItems == null ? null : "{length: ${collectionItems!.length}}"}, files: [length: ${files.length}], currentFile: ${currentFile == null ? null : "${currentFile!.fdPath}"}, isShowUi: $isShowUi, isPlay: $isPlay, isVideoCompleted: $isVideoCompleted, hasPrev: $hasPrev, hasNext: $hasNext, isShowTimeline: $isShowTimeline, hasShownTimeline: $hasShownTimeline, hasRequestExit: $hasRequestExit}";
} }
} }
@ -120,10 +128,10 @@ extension _$_InitToString on _Init {
} }
} }
extension _$_SetFilesToString on _SetFiles { extension _$_MergeFilesToString on _MergeFiles {
String _$toString() { String _$toString() {
// ignore: unnecessary_string_interpolations // ignore: unnecessary_string_interpolations
return "_SetFiles {dataMap: {length: ${dataMap.length}}}"; return "_MergeFiles {}";
} }
} }
@ -210,3 +218,10 @@ extension _$_RequestExitToString on _RequestExit {
return "_RequestExit {}"; return "_RequestExit {}";
} }
} }
extension _$_SetCollectionItemsToString on _SetCollectionItems {
String _$toString() {
// ignore: unnecessary_string_interpolations
return "_SetCollectionItems {value: ${value == null ? null : "[length: ${value!.length}]"}}";
}
}

View file

@ -1,16 +1,21 @@
part of '../slideshow_viewer.dart'; part of '../slideshow_viewer.dart';
@npLog @npLog
class _Bloc extends Bloc<_Event, _State> with BlocLogger { class _Bloc extends Bloc<_Event, _State>
with BlocLogger, BlocForEachMixin<_Event, _State> {
_Bloc({ _Bloc({
required this.filesController,
required this.account, required this.account,
required this.filesController,
required this.collectionsController,
required this.fileIds, required this.fileIds,
required this.startIndex, required this.startIndex,
required this.collectionId,
required this.config, required this.config,
}) : super(_State.init()) { }) : super(_State.init()) {
on<_Init>(_onInit); on<_Init>(_onInit);
on<_SetFiles>(_onSetFiles); on<_SetCollectionItems>(_onSetCollectionItems);
on<_MergeFiles>(_onMergeFiles);
on<_ToggleShowUi>(_onToggleShowUi); on<_ToggleShowUi>(_onToggleShowUi);
on<_PreloadSidePages>(_onPreloadSidePages); on<_PreloadSidePages>(_onPreloadSidePages);
on<_VideoCompleted>(_onVideoCompleted); on<_VideoCompleted>(_onVideoCompleted);
@ -24,14 +29,35 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
on<_RequestPage>(_onRequestPage); on<_RequestPage>(_onRequestPage);
on<_RequestExit>(_onRequestExit); on<_RequestExit>(_onRequestExit);
_subscriptions.add(filesController.stream.listen((event) { if (collectionId != null) {
add(_SetFiles(event.dataMap)); _subscriptions.add(collectionsController.stream.listen((event) {
for (final c in event.data) {
if (c.collection.id == collectionId) {
_collectionItemsSubscription?.cancel();
_collectionItemsSubscription = c.controller.stream.listen((event) {
add(_SetCollectionItems(event.items));
});
return;
}
}
_log.warning("[_Bloc] Collection not found: $collectionId");
add(const _SetCollectionItems(null));
_collectionItemsSubscription?.cancel();
}));
}
_subscriptions.add(stream
.distinct((a, b) =>
identical(a.rawFiles, b.rawFiles) &&
identical(a.collectionItems, b.collectionItems))
.listen((event) {
add(const _MergeFiles());
})); }));
} }
@override @override
Future<void> close() { Future<void> close() {
_pageChangeTimer?.cancel(); _pageChangeTimer?.cancel();
_collectionItemsSubscription?.cancel();
for (final s in _subscriptions) { for (final s in _subscriptions) {
s.cancel(); s.cancel();
} }
@ -83,11 +109,40 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
)); ));
} }
unawaited(_prepareNextPage()); unawaited(_prepareNextPage());
await forEach(
emit,
filesController.stream,
onData: (data) => state.copyWith(
rawFiles: data.dataMap,
),
);
} }
void _onSetFiles(_SetFiles ev, Emitter<_State> emit) { void _onSetCollectionItems(_SetCollectionItems ev, _Emitter emit) {
_log.info(ev); _log.info(ev);
final files = fileIds.map((e) => ev.dataMap[e]).toList(); final itemMap = ev.value
?.whereType<CollectionFileItem>()
.map((e) => MapEntry(e.file.fdId, e))
.toMap();
emit(state.copyWith(collectionItems: itemMap));
}
void _onMergeFiles(_MergeFiles ev, _Emitter emit) {
_log.info(ev);
final Map<int, FileDescriptor> merged;
if (collectionId == null) {
// not collection, nothing to merge
merged = state.rawFiles;
} else {
if (state.collectionItems == null) {
// collection not ready
return;
}
merged = state.rawFiles.addedAll(state.collectionItems!
.map((key, value) => MapEntry(key, value.file)));
}
final files = fileIds.map((e) => merged[e]).toList();
emit(state.copyWith(files: files)); emit(state.copyWith(files: files));
if (state.hasInit) { if (state.hasInit) {
emit(state.copyWith( emit(state.copyWith(
@ -283,10 +338,12 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
add(_NextPage(nextPage)); add(_NextPage(nextPage));
} }
final FilesController filesController;
final Account account; final Account account;
final FilesController filesController;
final CollectionsController collectionsController;
final List<int> fileIds; final List<int> fileIds;
final int startIndex; final int startIndex;
final String? collectionId;
final SlideshowConfig config; final SlideshowConfig config;
late final Map<int, List<int>> _shuffledIndex; late final Map<int, List<int>> _shuffledIndex;
@ -295,4 +352,5 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
Timer? _pageChangeTimer; Timer? _pageChangeTimer;
final _subscriptions = <StreamSubscription>[]; final _subscriptions = <StreamSubscription>[];
StreamSubscription? _collectionItemsSubscription;
} }

View file

@ -8,6 +8,8 @@ class _State {
required this.page, required this.page,
required this.nextPage, required this.nextPage,
required this.shouldAnimateNextPage, required this.shouldAnimateNextPage,
required this.rawFiles,
this.collectionItems,
required this.files, required this.files,
this.currentFile, this.currentFile,
required this.isShowUi, required this.isShowUi,
@ -25,6 +27,7 @@ class _State {
page: 0, page: 0,
nextPage: 0, nextPage: 0,
shouldAnimateNextPage: true, shouldAnimateNextPage: true,
rawFiles: {},
files: [], files: [],
isShowUi: false, isShowUi: false,
isPlay: true, isPlay: true,
@ -43,8 +46,11 @@ class _State {
final int page; final int page;
final int nextPage; final int nextPage;
final bool shouldAnimateNextPage; final bool shouldAnimateNextPage;
final Map<int, FileDescriptor> rawFiles;
final Map<int, CollectionFileItem>? collectionItems;
final List<FileDescriptor?> files; final List<FileDescriptor?> files;
final FileDescriptor? currentFile; final FileDescriptor? currentFile;
final bool isShowUi; final bool isShowUi;
final bool isPlay; final bool isPlay;
final bool isVideoCompleted; final bool isVideoCompleted;
@ -66,13 +72,14 @@ class _Init implements _Event {
} }
@toString @toString
class _SetFiles implements _Event { /// Merge regular files with collection items. The point of doing this is to
const _SetFiles(this.dataMap); /// support shared files in an server side shared album, as these files do not
/// have a record in filesController
class _MergeFiles implements _Event {
const _MergeFiles();
@override @override
String toString() => _$toString(); String toString() => _$toString();
final Map<int, FileDescriptor> dataMap;
} }
@toString @toString
@ -178,3 +185,13 @@ class _RequestExit implements _Event {
@override @override
String toString() => _$toString(); String toString() => _$toString();
} }
@toString
class _SetCollectionItems implements _Event {
const _SetCollectionItems(this.value);
@override
String toString() => _$toString();
final List<CollectionItem>? value;
}

View file

@ -253,9 +253,9 @@ class _WrappedViewerState extends State<_WrappedViewer>
final newIndex = await Navigator.of(context).pushNamed<int>( final newIndex = await Navigator.of(context).pushNamed<int>(
SlideshowViewer.routeName, SlideshowViewer.routeName,
arguments: SlideshowViewerArguments( arguments: SlideshowViewerArguments(
slideshowRequest.value!.account,
slideshowRequest.value!.fileIds, slideshowRequest.value!.fileIds,
slideshowRequest.value!.startIndex, slideshowRequest.value!.startIndex,
slideshowRequest.value!.collectionId,
result, result,
), ),
); );

View file

@ -371,10 +371,10 @@ class _Bloc extends Bloc<_Event, _State>
void _onStartSlideshow(_StartSlideshow ev, _Emitter emit) { void _onStartSlideshow(_StartSlideshow ev, _Emitter emit) {
_log.info(ev); _log.info(ev);
final req = _SlideshowRequest( final req = _SlideshowRequest(
account: account,
fileIds: state.fileIdOrders, fileIds: state.fileIdOrders,
startIndex: startIndex:
state.fileIdOrders.indexOf(ev.fileId).let((i) => i == -1 ? 0 : i), state.fileIdOrders.indexOf(ev.fileId).let((i) => i == -1 ? 0 : i),
collectionId: collectionId,
); );
emit(state.copyWith(slideshowRequest: Unique(req))); emit(state.copyWith(slideshowRequest: Unique(req)));
} }

View file

@ -14,14 +14,14 @@ class _ShareRequest {
class _SlideshowRequest { class _SlideshowRequest {
const _SlideshowRequest({ const _SlideshowRequest({
required this.account,
required this.fileIds, required this.fileIds,
required this.startIndex, required this.startIndex,
required this.collectionId,
}); });
final Account account;
final List<int> fileIds; final List<int> fileIds;
final int startIndex; final int startIndex;
final String? collectionId;
} }
class _SetAsRequest { class _SetAsRequest {