mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 16:56:19 +01:00
Query memories on DB side instead
This commit is contained in:
parent
1c2892c6cd
commit
21dffd9a13
9 changed files with 239 additions and 29 deletions
|
@ -47,6 +47,7 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
on<_SetEnableMemoryCollection>(_onSetEnableMemoryCollection);
|
||||
on<_SetMemoriesRange>(_onSetMemoriesRange);
|
||||
on<_UpdateDateTimeGroup>(_onUpdateDateTimeGroup);
|
||||
on<_UpdateMemories>(_onUpdateMemories);
|
||||
|
||||
on<_SetError>(_onSetError);
|
||||
|
||||
|
@ -88,6 +89,15 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
.listen((event) {
|
||||
add(const _UpdateScrollDate());
|
||||
}));
|
||||
_subscriptions.add(stream
|
||||
.distinct(
|
||||
(previous, next) => previous.filesSummary == next.filesSummary)
|
||||
.listen((_) {
|
||||
add(const _UpdateMemories());
|
||||
}));
|
||||
_subscriptions.add(prefController.memoriesRangeChange.listen((_) {
|
||||
add(const _UpdateMemories());
|
||||
}));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -185,7 +195,6 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
_log.info(ev);
|
||||
emit(state.copyWith(
|
||||
transformedItems: ev.items,
|
||||
memoryCollections: ev.memoryCollections,
|
||||
isLoading: _itemTransformerQueue.isProcessing,
|
||||
queriedDates: ev.dates,
|
||||
));
|
||||
|
@ -431,6 +440,49 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
add(const _TransformMinimap());
|
||||
}
|
||||
|
||||
Future<void> _onUpdateMemories(
|
||||
_UpdateMemories ev, Emitter<_State> emit) async {
|
||||
_log.info(ev);
|
||||
final localToday = clock.now().toLocal().toDate();
|
||||
final dbMemories = await _c.npDb.getFilesMemories(
|
||||
account: account.toDb(),
|
||||
at: localToday,
|
||||
radius: prefController.memoriesRangeValue,
|
||||
includeRelativeRoots: account.roots
|
||||
.map((e) => File(path: file_util.unstripPath(account, e))
|
||||
.strippedPathWithEmpty)
|
||||
.toList(),
|
||||
excludeRelativeRoots: [remote_storage_util.remoteStorageDirRelativePath],
|
||||
mimes: file_util.supportedFormatMimes,
|
||||
);
|
||||
emit(state.copyWith(
|
||||
memoryCollections: dbMemories.memories.entries
|
||||
.sorted((a, b) => a.key.compareTo(b.key))
|
||||
.reversed
|
||||
.map((e) {
|
||||
final center = localToday
|
||||
.copyWith(year: e.key)
|
||||
.toLocalDateTime()
|
||||
.copyWith(hour: 12);
|
||||
return Collection(
|
||||
name: L10n.global().memoryAlbumName(localToday.year - e.key),
|
||||
contentProvider: CollectionMemoryProvider(
|
||||
account: account,
|
||||
year: e.key,
|
||||
month: localToday.month,
|
||||
day: localToday.day,
|
||||
cover: e.value
|
||||
.map((e) => Tuple2(e.bestDateTime.difference(center), e))
|
||||
.sorted((a, b) => a.item1.compareTo(b.item1))
|
||||
.firstOrNull
|
||||
?.let((e) => DbFileDescriptorConverter.fromDb(
|
||||
account.userId.toString(), e.item2)),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
));
|
||||
}
|
||||
|
||||
void _onSetError(_SetError ev, Emitter<_State> emit) {
|
||||
_log.info(ev);
|
||||
emit(state.copyWith(error: ExceptionEvent(ev.error, ev.stackTrace)));
|
||||
|
@ -447,15 +499,11 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
|
|||
itemPerRow: state.itemPerRow,
|
||||
itemSize: state.itemSize,
|
||||
isGroupByDay: prefController.homePhotosZoomLevelValue >= 0,
|
||||
memoriesDayRange: prefController.memoriesRangeValue,
|
||||
locale: language_util.getSelectedLocale() ??
|
||||
PlatformDispatcher.instance.locale,
|
||||
),
|
||||
_buildItem,
|
||||
(result) {
|
||||
if (!isClosed) {
|
||||
add(_OnItemTransformed(
|
||||
result.items, result.memoryCollections, result.dates));
|
||||
add(_OnItemTransformed(result.items, result.dates));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -694,13 +742,6 @@ _ItemTransformerResult _buildItem(_ItemTransformerArgument arg) {
|
|||
|
||||
final dateHelper =
|
||||
photo_list_util.DateGroupHelper(isMonthOnly: !arg.isGroupByDay);
|
||||
final today = Date.today();
|
||||
final memoryCollectionHelper = photo_list_util.MemoryCollectionHelper(
|
||||
arg.account,
|
||||
today: today,
|
||||
dayRange: arg.memoriesDayRange,
|
||||
);
|
||||
|
||||
final dateTimeSet = SplayTreeSet<Date>.of([
|
||||
...fileGroups.keys,
|
||||
if (arg.summary != null) ...arg.summary!.items.keys,
|
||||
|
@ -724,7 +765,6 @@ _ItemTransformerResult _buildItem(_ItemTransformerArgument arg) {
|
|||
continue;
|
||||
}
|
||||
transformed.add(item);
|
||||
memoryCollectionHelper.addFile(f, localDate: d);
|
||||
}
|
||||
} else if (arg.summary != null) {
|
||||
// summary
|
||||
|
@ -740,12 +780,8 @@ _ItemTransformerResult _buildItem(_ItemTransformerArgument arg) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
final memoryCollections = memoryCollectionHelper
|
||||
.build((year) => L10n.of(arg.locale).memoryAlbumName(today.year - year));
|
||||
return _ItemTransformerResult(
|
||||
items: transformed,
|
||||
memoryCollections: memoryCollections,
|
||||
dates: dates,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -114,13 +114,12 @@ class _TransformItems implements _Event {
|
|||
|
||||
@toString
|
||||
class _OnItemTransformed implements _Event {
|
||||
const _OnItemTransformed(this.items, this.memoryCollections, this.dates);
|
||||
const _OnItemTransformed(this.items, this.dates);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final List<_Item> items;
|
||||
final List<Collection> memoryCollections;
|
||||
final Set<Date> dates;
|
||||
}
|
||||
|
||||
|
@ -308,6 +307,14 @@ class _UpdateDateTimeGroup implements _Event {
|
|||
String toString() => _$toString();
|
||||
}
|
||||
|
||||
@toString
|
||||
class _UpdateMemories implements _Event {
|
||||
const _UpdateMemories();
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
}
|
||||
|
||||
@toString
|
||||
class _SetError implements _Event {
|
||||
const _SetError(this.error, [this.stackTrace]);
|
||||
|
|
|
@ -114,8 +114,6 @@ class _ItemTransformerArgument {
|
|||
this.itemPerRow,
|
||||
this.itemSize,
|
||||
required this.isGroupByDay,
|
||||
required this.memoriesDayRange,
|
||||
required this.locale,
|
||||
});
|
||||
|
||||
final Account account;
|
||||
|
@ -124,19 +122,15 @@ class _ItemTransformerArgument {
|
|||
final int? itemPerRow;
|
||||
final double? itemSize;
|
||||
final bool isGroupByDay;
|
||||
final int memoriesDayRange;
|
||||
final Locale locale;
|
||||
}
|
||||
|
||||
class _ItemTransformerResult {
|
||||
const _ItemTransformerResult({
|
||||
required this.items,
|
||||
required this.memoryCollections,
|
||||
required this.dates,
|
||||
});
|
||||
|
||||
final List<_Item> items;
|
||||
final List<Collection> memoryCollections;
|
||||
final Set<Date> dates;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import 'package:clock/clock.dart';
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:copy_with/copy_with.dart';
|
||||
import 'package:draggable_scrollbar/draggable_scrollbar.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
|
@ -23,9 +22,12 @@ import 'package:nc_photos/controller/metadata_controller.dart';
|
|||
import 'package:nc_photos/controller/persons_controller.dart';
|
||||
import 'package:nc_photos/controller/pref_controller.dart';
|
||||
import 'package:nc_photos/controller/sync_controller.dart';
|
||||
import 'package:nc_photos/db/entity_converter.dart';
|
||||
import 'package:nc_photos/di_container.dart';
|
||||
import 'package:nc_photos/download_handler.dart';
|
||||
import 'package:nc_photos/entity/collection.dart';
|
||||
import 'package:nc_photos/entity/collection/content_provider/memory.dart';
|
||||
import 'package:nc_photos/entity/file.dart';
|
||||
import 'package:nc_photos/entity/file_descriptor.dart';
|
||||
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
||||
import 'package:nc_photos/event/event.dart';
|
||||
|
@ -33,8 +35,8 @@ import 'package:nc_photos/exception_event.dart';
|
|||
import 'package:nc_photos/exception_util.dart' as exception_util;
|
||||
import 'package:nc_photos/flutter_util.dart' as flutter_util;
|
||||
import 'package:nc_photos/k.dart' as k;
|
||||
import 'package:nc_photos/language_util.dart' as language_util;
|
||||
import 'package:nc_photos/progress_util.dart';
|
||||
import 'package:nc_photos/remote_storage_util.dart' as remote_storage_util;
|
||||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
import 'package:nc_photos/stream_extension.dart';
|
||||
import 'package:nc_photos/theme.dart';
|
||||
|
@ -63,6 +65,7 @@ import 'package:np_db/np_db.dart';
|
|||
import 'package:np_platform_util/np_platform_util.dart';
|
||||
import 'package:np_ui/np_ui.dart';
|
||||
import 'package:to_string/to_string.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import 'package:visibility_detector/visibility_detector.dart';
|
||||
|
||||
part 'home_photos/app_bar.dart';
|
||||
|
|
|
@ -208,7 +208,7 @@ extension _$_TransformItemsToString on _TransformItems {
|
|||
extension _$_OnItemTransformedToString on _OnItemTransformed {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_OnItemTransformed {items: [length: ${items.length}], memoryCollections: [length: ${memoryCollections.length}], dates: {length: ${dates.length}}}";
|
||||
return "_OnItemTransformed {items: [length: ${items.length}], dates: {length: ${dates.length}}}";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -353,6 +353,13 @@ extension _$_UpdateDateTimeGroupToString on _UpdateDateTimeGroup {
|
|||
}
|
||||
}
|
||||
|
||||
extension _$_UpdateMemoriesToString on _UpdateMemories {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "_UpdateMemories {}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$_SetErrorToString on _SetError {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
|
|
|
@ -164,6 +164,20 @@ class DbFilesSummary {
|
|||
final Map<Date, DbFilesSummaryItem> items;
|
||||
}
|
||||
|
||||
@genCopyWith
|
||||
@toString
|
||||
class DbFilesMemory {
|
||||
const DbFilesMemory({
|
||||
required this.memories,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
/// Mapping year to files
|
||||
final Map<int, List<DbFileDescriptor>> memories;
|
||||
}
|
||||
|
||||
@npLog
|
||||
abstract class NpDb {
|
||||
factory NpDb() => NpDbSqlite();
|
||||
|
@ -378,6 +392,17 @@ abstract class NpDb {
|
|||
List<String>? mimes,
|
||||
});
|
||||
|
||||
/// Return [DbFileDescriptor]s whose date is lying in a specific month and day
|
||||
/// range
|
||||
Future<DbFilesMemory> getFilesMemories({
|
||||
required DbAccount account,
|
||||
required Date at,
|
||||
required int radius,
|
||||
List<String>? includeRelativeRoots,
|
||||
List<String>? excludeRelativeRoots,
|
||||
List<String>? mimes,
|
||||
});
|
||||
|
||||
Future<DbLocationGroupResult> groupLocations({
|
||||
required DbAccount account,
|
||||
List<String>? includeRelativeRoots,
|
||||
|
|
|
@ -57,6 +57,30 @@ extension $DbFilesSummaryCopyWith on DbFilesSummary {
|
|||
_$DbFilesSummaryCopyWithWorkerImpl(this);
|
||||
}
|
||||
|
||||
abstract class $DbFilesMemoryCopyWithWorker {
|
||||
DbFilesMemory call({Map<int, List<DbFileDescriptor>>? memories});
|
||||
}
|
||||
|
||||
class _$DbFilesMemoryCopyWithWorkerImpl
|
||||
implements $DbFilesMemoryCopyWithWorker {
|
||||
_$DbFilesMemoryCopyWithWorkerImpl(this.that);
|
||||
|
||||
@override
|
||||
DbFilesMemory call({dynamic memories}) {
|
||||
return DbFilesMemory(
|
||||
memories:
|
||||
memories as Map<int, List<DbFileDescriptor>>? ?? that.memories);
|
||||
}
|
||||
|
||||
final DbFilesMemory that;
|
||||
}
|
||||
|
||||
extension $DbFilesMemoryCopyWith on DbFilesMemory {
|
||||
$DbFilesMemoryCopyWithWorker get copyWith => _$copyWith;
|
||||
$DbFilesMemoryCopyWithWorker get _$copyWith =>
|
||||
_$DbFilesMemoryCopyWithWorkerImpl(this);
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// NpLogGenerator
|
||||
// **************************************************************************
|
||||
|
@ -113,3 +137,10 @@ extension _$DbFilesSummaryToString on DbFilesSummary {
|
|||
return "DbFilesSummary {items: {length: ${items.length}}}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$DbFilesMemoryToString on DbFilesMemory {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "DbFilesMemory {memories: $memories}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -677,6 +677,87 @@ extension SqliteDbFileExtension on SqliteDb {
|
|||
return CountFileGroupsByDateResult(dateCount: results.toMap());
|
||||
}
|
||||
|
||||
/// Query files descriptors whose date is lying in a specific month and day
|
||||
/// range
|
||||
Future<List<FileDescriptor>> queryFileDescriptorMemories({
|
||||
required ByAccount account,
|
||||
required Date at,
|
||||
required int radius,
|
||||
List<String>? includeRelativeRoots,
|
||||
List<String>? excludeRelativeRoots,
|
||||
List<String>? mimes,
|
||||
}) async {
|
||||
_log.info(
|
||||
"[queryFileDescriptorMemoryGroups] "
|
||||
"at: $at, "
|
||||
"radius: $radius, "
|
||||
"includeRelativeRoots: $includeRelativeRoots, "
|
||||
"excludeRelativeRoots: $excludeRelativeRoots, "
|
||||
"mimes: $mimes",
|
||||
);
|
||||
|
||||
final dates =
|
||||
List.generate(radius * 2 + 1, (index) => at.add(day: -radius + index));
|
||||
final localTimeColumn =
|
||||
accountFiles.bestDateTime.modify(const DateTimeModifier.localTime());
|
||||
final query = _queryFiles().let((q) {
|
||||
q
|
||||
..setQueryMode(
|
||||
FilesQueryMode.expression,
|
||||
expressions: [
|
||||
accountFiles.relativePath,
|
||||
files.fileId,
|
||||
files.contentType,
|
||||
accountFiles.isArchived,
|
||||
accountFiles.isFavorite,
|
||||
accountFiles.bestDateTime,
|
||||
],
|
||||
)
|
||||
..setAccount(account)
|
||||
..byArchived(false);
|
||||
if (includeRelativeRoots != null) {
|
||||
if (includeRelativeRoots.none((p) => p.isEmpty)) {
|
||||
for (final r in includeRelativeRoots) {
|
||||
q.byOrRelativePathPattern("$r/%");
|
||||
}
|
||||
}
|
||||
}
|
||||
return q.build();
|
||||
});
|
||||
if (excludeRelativeRoots != null) {
|
||||
for (final r in excludeRelativeRoots) {
|
||||
query.where(accountFiles.relativePath.like("$r/%").not());
|
||||
}
|
||||
}
|
||||
if (mimes != null) {
|
||||
query.where(files.contentType.isIn(mimes));
|
||||
} else {
|
||||
query.where(files.isCollection.isNotValue(true));
|
||||
}
|
||||
Expression<bool>? dateExp;
|
||||
for (final d in dates) {
|
||||
final thisExp = localTimeColumn.month.equals(d.month) &
|
||||
localTimeColumn.day.equals(d.day);
|
||||
if (dateExp == null) {
|
||||
dateExp = thisExp;
|
||||
} else {
|
||||
dateExp |= thisExp;
|
||||
}
|
||||
}
|
||||
query.where(dateExp!.isValue(true));
|
||||
final results = await query
|
||||
.map((r) => FileDescriptor(
|
||||
relativePath: r.read(accountFiles.relativePath)!,
|
||||
fileId: r.read(files.fileId)!,
|
||||
contentType: r.read(files.contentType),
|
||||
isArchived: r.read(accountFiles.isArchived),
|
||||
isFavorite: r.read(accountFiles.isFavorite),
|
||||
bestDateTime: r.read(accountFiles.bestDateTime)!.toUtc(),
|
||||
))
|
||||
.get();
|
||||
return results;
|
||||
}
|
||||
|
||||
/// Update Db files
|
||||
///
|
||||
/// Return a list of files that are not yet inserted to the DB (thus not
|
||||
|
|
|
@ -489,6 +489,32 @@ class NpDbSqlite implements NpDb {
|
|||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DbFilesMemory> getFilesMemories({
|
||||
required DbAccount account,
|
||||
required Date at,
|
||||
required int radius,
|
||||
List<String>? includeRelativeRoots,
|
||||
List<String>? excludeRelativeRoots,
|
||||
List<String>? mimes,
|
||||
}) async {
|
||||
final result = await _db.use((db) async {
|
||||
return await db.queryFileDescriptorMemories(
|
||||
account: ByAccount.db(account),
|
||||
at: at,
|
||||
radius: radius,
|
||||
includeRelativeRoots: includeRelativeRoots,
|
||||
excludeRelativeRoots: excludeRelativeRoots,
|
||||
mimes: mimes,
|
||||
);
|
||||
});
|
||||
final memories = <int, List<DbFileDescriptor>>{};
|
||||
for (final r in result.map(FileDescriptorConverter.fromSql)) {
|
||||
(memories[r.bestDateTime.year] ??= <DbFileDescriptor>[]).add(r);
|
||||
}
|
||||
return DbFilesMemory(memories: memories);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DbLocationGroupResult> groupLocations({
|
||||
required DbAccount account,
|
||||
|
|
Loading…
Reference in a new issue