mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-23 17:26:18 +01:00
3631abaf7d
- Lazy loading - Minimap
218 lines
5.8 KiB
Dart
218 lines
5.8 KiB
Dart
part of '../archive_browser.dart';
|
|
|
|
@npLog
|
|
class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|
_Bloc({
|
|
required this.account,
|
|
required this.filesController,
|
|
required this.prefController,
|
|
}) : super(_State.init(
|
|
zoom: prefController.albumBrowserZoomLevelValue,
|
|
)) {
|
|
on<_LoadItems>(_onLoad);
|
|
on<_TransformItems>(_onTransformItems);
|
|
on<_OnItemTransformed>(_onOnItemTransformed);
|
|
|
|
on<_SetSelectedItems>(_onSetSelectedItems);
|
|
on<_UnarchiveSelectedItems>(_onUnarchiveSelectedItems);
|
|
|
|
on<_StartScaling>(_onStartScaling);
|
|
on<_EndScaling>(_onEndScaling);
|
|
on<_SetScale>(_onSetScale);
|
|
|
|
on<_SetError>(_onSetError);
|
|
}
|
|
|
|
@override
|
|
Future<void> close() {
|
|
for (final s in _subscriptions) {
|
|
s.cancel();
|
|
}
|
|
return super.close();
|
|
}
|
|
|
|
@override
|
|
String get tag => _log.fullName;
|
|
|
|
@override
|
|
bool Function(dynamic, dynamic)? get shouldLog => (currentState, nextState) {
|
|
currentState = currentState as _State;
|
|
nextState = nextState as _State;
|
|
return currentState.scale == nextState.scale &&
|
|
currentState.visibleItems == nextState.visibleItems;
|
|
};
|
|
|
|
@override
|
|
void onError(Object error, StackTrace stackTrace) {
|
|
// we need this to prevent onError being triggered recursively
|
|
if (!isClosed && !_isHandlingError) {
|
|
_isHandlingError = true;
|
|
try {
|
|
add(_SetError(error, stackTrace));
|
|
} catch (_) {}
|
|
_isHandlingError = false;
|
|
}
|
|
super.onError(error, stackTrace);
|
|
}
|
|
|
|
Future<void> _onLoad(_LoadItems ev, Emitter<_State> emit) {
|
|
_log.info(ev);
|
|
unawaited(filesController.queryByArchived());
|
|
return emit.forEach<FilesStreamEvent>(
|
|
filesController.stream,
|
|
onData: (data) => state.copyWith(
|
|
files: data.data,
|
|
isLoading: data.hasNext || _itemTransformerQueue.isProcessing,
|
|
),
|
|
onError: (e, stackTrace) {
|
|
_log.severe("[_onLoad] Uncaught exception", e, stackTrace);
|
|
return state.copyWith(
|
|
isLoading: _itemTransformerQueue.isProcessing,
|
|
error: ExceptionEvent(e, stackTrace),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
void _onTransformItems(_TransformItems ev, Emitter<_State> emit) {
|
|
_log.info(ev);
|
|
_transformItems(ev.items);
|
|
emit(state.copyWith(isLoading: true));
|
|
}
|
|
|
|
void _onOnItemTransformed(_OnItemTransformed ev, Emitter<_State> emit) {
|
|
_log.info(ev);
|
|
emit(state.copyWith(
|
|
transformedItems: ev.items,
|
|
isLoading: _itemTransformerQueue.isProcessing,
|
|
));
|
|
}
|
|
|
|
void _onSetSelectedItems(_SetSelectedItems ev, Emitter<_State> emit) {
|
|
_log.info(ev);
|
|
emit(state.copyWith(selectedItems: ev.items));
|
|
}
|
|
|
|
void _onUnarchiveSelectedItems(
|
|
_UnarchiveSelectedItems ev, Emitter<_State> emit) {
|
|
_log.info(ev);
|
|
final selected = state.selectedItems;
|
|
_clearSelection(emit);
|
|
final selectedFiles =
|
|
selected.whereType<_FileItem>().map((e) => e.file).toList();
|
|
if (selectedFiles.isNotEmpty) {
|
|
filesController.updateProperty(
|
|
selectedFiles,
|
|
isArchived: const OrNull(false),
|
|
errorBuilder: (fileIds) => _UnarchiveFailedError(fileIds.length),
|
|
);
|
|
}
|
|
}
|
|
|
|
void _onStartScaling(_StartScaling ev, Emitter<_State> emit) {
|
|
_log.info(ev);
|
|
}
|
|
|
|
void _onEndScaling(_EndScaling ev, Emitter<_State> emit) {
|
|
_log.info(ev);
|
|
if (state.scale == null) {
|
|
return;
|
|
}
|
|
final int newZoom;
|
|
if (state.scale! >= 1.25) {
|
|
// scale up
|
|
newZoom = (state.zoom + 1).clamp(-1, 2);
|
|
} else if (state.scale! <= 0.75) {
|
|
newZoom = (state.zoom - 1).clamp(-1, 2);
|
|
} else {
|
|
newZoom = state.zoom;
|
|
}
|
|
emit(state.copyWith(
|
|
zoom: newZoom,
|
|
scale: null,
|
|
));
|
|
unawaited(prefController.setAlbumBrowserZoomLevel(newZoom));
|
|
}
|
|
|
|
void _onSetScale(_SetScale ev, Emitter<_State> emit) {
|
|
// _log.info(ev);
|
|
emit(state.copyWith(scale: ev.scale));
|
|
}
|
|
|
|
void _onSetError(_SetError ev, Emitter<_State> emit) {
|
|
_log.info(ev);
|
|
emit(state.copyWith(error: ExceptionEvent(ev.error, ev.stackTrace)));
|
|
}
|
|
|
|
Future _transformItems(List<FileDescriptor> files) async {
|
|
_log.info("[_transformItems] Queue ${files.length} items");
|
|
_itemTransformerQueue.addJob(
|
|
_ItemTransformerArgument(
|
|
account: account,
|
|
files: files,
|
|
),
|
|
_buildItem,
|
|
(result) {
|
|
safeAdd(_OnItemTransformed(result.items));
|
|
},
|
|
);
|
|
}
|
|
|
|
void _clearSelection(Emitter<_State> emit) {
|
|
emit(state.copyWith(selectedItems: const {}));
|
|
}
|
|
|
|
final Account account;
|
|
final FilesController filesController;
|
|
final PrefController prefController;
|
|
|
|
final _itemTransformerQueue =
|
|
ComputeQueue<_ItemTransformerArgument, _ItemTransformerResult>();
|
|
final _subscriptions = <StreamSubscription>[];
|
|
var _isHandlingError = false;
|
|
}
|
|
|
|
@toString
|
|
class _UnarchiveFailedError implements Exception {
|
|
const _UnarchiveFailedError(this.count);
|
|
|
|
@override
|
|
String toString() => _$toString();
|
|
|
|
final int count;
|
|
}
|
|
|
|
_ItemTransformerResult _buildItem(_ItemTransformerArgument arg) {
|
|
final sortedFiles = arg.files
|
|
.where((f) => f.fdIsArchived == true)
|
|
.sorted(compareFileDescriptorDateTimeDescending);
|
|
|
|
final transformed = <_Item>[];
|
|
for (int i = 0; i < sortedFiles.length; ++i) {
|
|
final file = sortedFiles[i];
|
|
final item = _buildSingleItem(arg.account, file);
|
|
if (item == null) {
|
|
continue;
|
|
}
|
|
transformed.add(item);
|
|
}
|
|
return _ItemTransformerResult(items: transformed);
|
|
}
|
|
|
|
_Item? _buildSingleItem(Account account, FileDescriptor file) {
|
|
if (file_util.isSupportedImageFormat(file)) {
|
|
return _PhotoItem(
|
|
file: file,
|
|
account: account,
|
|
);
|
|
} else if (file_util.isSupportedVideoFormat(file)) {
|
|
return _VideoItem(
|
|
file: file,
|
|
account: account,
|
|
);
|
|
} else {
|
|
_$__NpLog.log
|
|
.shout("[_buildSingleItem] Unsupported file format: ${file.fdMime}");
|
|
return null;
|
|
}
|
|
}
|