move: update source, DB, lenses
This commit is contained in:
parent
cae7e6570d
commit
a437c2fe9a
10 changed files with 156 additions and 71 deletions
|
@ -25,6 +25,7 @@ import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import deckers.thibault.aves.model.ImageEntry;
|
import deckers.thibault.aves.model.ImageEntry;
|
||||||
|
@ -263,7 +264,14 @@ public class MediaStoreImageProvider extends ImageProvider {
|
||||||
DocumentFileCompat destination = DocumentFileCompat.fromSingleUri(activity, destinationUri);
|
DocumentFileCompat destination = DocumentFileCompat.fromSingleUri(activity, destinationUri);
|
||||||
source.copyTo(destination);
|
source.copyTo(destination);
|
||||||
|
|
||||||
// TODO TLAD delete source when it is a `move`
|
if (!copy) {
|
||||||
|
// delete original entry
|
||||||
|
try {
|
||||||
|
delete(activity, sourcePath, sourceUri).get();
|
||||||
|
} catch (ExecutionException | InterruptedException e) {
|
||||||
|
Log.w(LOG_TAG, "failed to delete entry with path=" + sourcePath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, Object> newFields = new HashMap<>();
|
Map<String, Object> newFields = new HashMap<>();
|
||||||
newFields.put("uri", destinationUri.toString());
|
newFields.put("uri", destinationUri.toString());
|
||||||
|
|
|
@ -31,10 +31,11 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
|
||||||
}) : filters = {if (filters != null) ...filters.where((f) => f != null)},
|
}) : filters = {if (filters != null) ...filters.where((f) => f != null)},
|
||||||
groupFactor = groupFactor ?? GroupFactor.month,
|
groupFactor = groupFactor ?? GroupFactor.month,
|
||||||
sortFactor = sortFactor ?? SortFactor.date {
|
sortFactor = sortFactor ?? SortFactor.date {
|
||||||
_subscriptions.add(source.eventBus.on<EntryAddedEvent>().listen((e) => onEntryAdded()));
|
_subscriptions.add(source.eventBus.on<EntryAddedEvent>().listen((e) => _refresh()));
|
||||||
_subscriptions.add(source.eventBus.on<EntryRemovedEvent>().listen((e) => onEntryRemoved(e.entries)));
|
_subscriptions.add(source.eventBus.on<EntryRemovedEvent>().listen((e) => onEntryRemoved(e.entries)));
|
||||||
_subscriptions.add(source.eventBus.on<CatalogMetadataChangedEvent>().listen((e) => onMetadataChanged()));
|
_subscriptions.add(source.eventBus.on<EntryMovedEvent>().listen((e) => _refresh()));
|
||||||
onEntryAdded();
|
_subscriptions.add(source.eventBus.on<CatalogMetadataChangedEvent>().listen((e) => _refresh()));
|
||||||
|
_refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -107,9 +108,7 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
|
||||||
}
|
}
|
||||||
|
|
||||||
void onFilterChanged() {
|
void onFilterChanged() {
|
||||||
_applyFilters();
|
_refresh();
|
||||||
_applySort();
|
|
||||||
_applyGroup();
|
|
||||||
filterChangeNotifier.notifyListeners();
|
filterChangeNotifier.notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +179,9 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onEntryAdded() {
|
// metadata change should also trigger a full refresh
|
||||||
|
// as dates impact sorting and grouping
|
||||||
|
void _refresh() {
|
||||||
_applyFilters();
|
_applyFilters();
|
||||||
_applySort();
|
_applySort();
|
||||||
_applyGroup();
|
_applyGroup();
|
||||||
|
@ -194,13 +195,6 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
|
||||||
selection.removeAll(entries);
|
selection.removeAll(entries);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onMetadataChanged() {
|
|
||||||
_applyFilters();
|
|
||||||
// metadata dates impact sorting and grouping
|
|
||||||
_applySort();
|
|
||||||
_applyGroup();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SortFactor { date, size, name }
|
enum SortFactor { date, size, name }
|
||||||
|
|
|
@ -146,9 +146,24 @@ class CollectionSource {
|
||||||
void removeEntries(Iterable<ImageEntry> entries) async {
|
void removeEntries(Iterable<ImageEntry> entries) async {
|
||||||
entries.forEach((entry) => entry.removeFromFavourites());
|
entries.forEach((entry) => entry.removeFromFavourites());
|
||||||
_rawEntries.removeWhere(entries.contains);
|
_rawEntries.removeWhere(entries.contains);
|
||||||
|
cleanEmptyAlbums(entries.map((entry) => entry.directory).toSet());
|
||||||
eventBus.fire(EntryRemovedEvent(entries));
|
eventBus.fire(EntryRemovedEvent(entries));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void notifyMovedEntries(Iterable<ImageEntry> movedEntries) {
|
||||||
|
eventBus.fire(EntryMovedEvent(entries));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanEmptyAlbums(Set<String> albums) {
|
||||||
|
final emptyAlbums = albums.where(_isEmptyAlbum);
|
||||||
|
if (emptyAlbums.isNotEmpty) {
|
||||||
|
_folderPaths.removeAll(emptyAlbums);
|
||||||
|
updateAlbums();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isEmptyAlbum(String album) => !_rawEntries.any((entry) => entry.directory == album);
|
||||||
|
|
||||||
String getUniqueAlbumName(String album) {
|
String getUniqueAlbumName(String album) {
|
||||||
final otherAlbums = _folderPaths.where((item) => item != album);
|
final otherAlbums = _folderPaths.where((item) => item != album);
|
||||||
final parts = album.split(separator);
|
final parts = album.split(separator);
|
||||||
|
@ -231,3 +246,9 @@ class EntryRemovedEvent {
|
||||||
|
|
||||||
const EntryRemovedEvent(this.entries);
|
const EntryRemovedEvent(this.entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EntryMovedEvent {
|
||||||
|
final Iterable<ImageEntry> entries;
|
||||||
|
|
||||||
|
const EntryMovedEvent(this.entries);
|
||||||
|
}
|
||||||
|
|
|
@ -17,8 +17,9 @@ import 'mime_types.dart';
|
||||||
|
|
||||||
class ImageEntry {
|
class ImageEntry {
|
||||||
String uri;
|
String uri;
|
||||||
String path;
|
String _path;
|
||||||
String directory;
|
String _directory;
|
||||||
|
String _filename;
|
||||||
int contentId;
|
int contentId;
|
||||||
final String mimeType;
|
final String mimeType;
|
||||||
int width;
|
int width;
|
||||||
|
@ -38,7 +39,7 @@ class ImageEntry {
|
||||||
|
|
||||||
ImageEntry({
|
ImageEntry({
|
||||||
this.uri,
|
this.uri,
|
||||||
this.path,
|
String path,
|
||||||
this.contentId,
|
this.contentId,
|
||||||
this.mimeType,
|
this.mimeType,
|
||||||
this.width,
|
this.width,
|
||||||
|
@ -49,7 +50,8 @@ class ImageEntry {
|
||||||
this.dateModifiedSecs,
|
this.dateModifiedSecs,
|
||||||
this.sourceDateTakenMillis,
|
this.sourceDateTakenMillis,
|
||||||
this.durationMillis,
|
this.durationMillis,
|
||||||
}) : directory = path != null ? dirname(path) : null {
|
}) {
|
||||||
|
this.path = path;
|
||||||
isFavouriteNotifier.value = isFavourite;
|
isFavouriteNotifier.value = isFavourite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +128,23 @@ class ImageEntry {
|
||||||
return 'ImageEntry{uri=$uri, path=$path}';
|
return 'ImageEntry{uri=$uri, path=$path}';
|
||||||
}
|
}
|
||||||
|
|
||||||
String get filename => basenameWithoutExtension(path);
|
set path(String path) {
|
||||||
|
_path = path;
|
||||||
|
_directory = null;
|
||||||
|
_filename = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get path => _path;
|
||||||
|
|
||||||
|
String get directory {
|
||||||
|
_directory ??= path != null ? dirname(path) : null;
|
||||||
|
return _directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get filenameWithoutExtension {
|
||||||
|
_filename ??= path != null ? basenameWithoutExtension(path) : null;
|
||||||
|
return _filename;
|
||||||
|
}
|
||||||
|
|
||||||
String get mimeTypeAnySubtype => mimeType.replaceAll(RegExp('/.*'), '/*');
|
String get mimeTypeAnySubtype => mimeType.replaceAll(RegExp('/.*'), '/*');
|
||||||
|
|
||||||
|
@ -286,7 +304,7 @@ class ImageEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> rename(String newName) async {
|
Future<bool> rename(String newName) async {
|
||||||
if (newName == filename) return true;
|
if (newName == filenameWithoutExtension) return true;
|
||||||
|
|
||||||
final newFields = await ImageFileService.rename(this, '$newName${extension(this.path)}');
|
final newFields = await ImageFileService.rename(this, '$newName${extension(this.path)}');
|
||||||
if (newFields.isEmpty) return false;
|
if (newFields.isEmpty) return false;
|
||||||
|
|
|
@ -106,24 +106,35 @@ class MetadataDb {
|
||||||
final stopwatch = Stopwatch()..start();
|
final stopwatch = Stopwatch()..start();
|
||||||
final db = await _database;
|
final db = await _database;
|
||||||
final batch = db.batch();
|
final batch = db.batch();
|
||||||
metadataEntries.where((metadata) => metadata != null).forEach((metadata) {
|
metadataEntries.where((metadata) => metadata != null).forEach((metadata) => _batchInsertMetadata(batch, metadata));
|
||||||
if (metadata.dateMillis != 0) {
|
|
||||||
batch.insert(
|
|
||||||
dateTakenTable,
|
|
||||||
DateMetadata(contentId: metadata.contentId, dateMillis: metadata.dateMillis).toMap(),
|
|
||||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
batch.insert(
|
|
||||||
metadataTable,
|
|
||||||
metadata.toMap(boolAsInteger: true),
|
|
||||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
await batch.commit(noResult: true);
|
await batch.commit(noResult: true);
|
||||||
debugPrint('$runtimeType saveMetadata complete in ${stopwatch.elapsed.inMilliseconds}ms for ${metadataEntries.length} entries');
|
debugPrint('$runtimeType saveMetadata complete in ${stopwatch.elapsed.inMilliseconds}ms for ${metadataEntries.length} entries');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> updateMetadataId(int oldId, CatalogMetadata metadata) async {
|
||||||
|
final db = await _database;
|
||||||
|
final batch = db.batch();
|
||||||
|
batch.delete(dateTakenTable, where: 'contentId = ?', whereArgs: [oldId]);
|
||||||
|
batch.delete(metadataTable, where: 'contentId = ?', whereArgs: [oldId]);
|
||||||
|
_batchInsertMetadata(batch, metadata);
|
||||||
|
await batch.commit(noResult: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _batchInsertMetadata(Batch batch, CatalogMetadata metadata) {
|
||||||
|
if (metadata.dateMillis != 0) {
|
||||||
|
batch.insert(
|
||||||
|
dateTakenTable,
|
||||||
|
DateMetadata(contentId: metadata.contentId, dateMillis: metadata.dateMillis).toMap(),
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
batch.insert(
|
||||||
|
metadataTable,
|
||||||
|
metadata.toMap(boolAsInteger: true),
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// address
|
// address
|
||||||
|
|
||||||
Future<void> clearAddresses() async {
|
Future<void> clearAddresses() async {
|
||||||
|
@ -146,15 +157,27 @@ class MetadataDb {
|
||||||
final stopwatch = Stopwatch()..start();
|
final stopwatch = Stopwatch()..start();
|
||||||
final db = await _database;
|
final db = await _database;
|
||||||
final batch = db.batch();
|
final batch = db.batch();
|
||||||
addresses.where((address) => address != null).forEach((address) => batch.insert(
|
addresses.where((address) => address != null).forEach((address) => _batchInsertAddress(batch, address));
|
||||||
addressTable,
|
|
||||||
address.toMap(),
|
|
||||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
|
||||||
));
|
|
||||||
await batch.commit(noResult: true);
|
await batch.commit(noResult: true);
|
||||||
debugPrint('$runtimeType saveAddresses complete in ${stopwatch.elapsed.inMilliseconds}ms for ${addresses.length} entries');
|
debugPrint('$runtimeType saveAddresses complete in ${stopwatch.elapsed.inMilliseconds}ms for ${addresses.length} entries');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> updateAddressId(int oldId, AddressDetails address) async {
|
||||||
|
final db = await _database;
|
||||||
|
final batch = db.batch();
|
||||||
|
batch.delete(addressTable, where: 'contentId = ?', whereArgs: [oldId]);
|
||||||
|
_batchInsertAddress(batch, address);
|
||||||
|
await batch.commit(noResult: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _batchInsertAddress(Batch batch, AddressDetails address) {
|
||||||
|
batch.insert(
|
||||||
|
addressTable,
|
||||||
|
address.toMap(),
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// favourites
|
// favourites
|
||||||
|
|
||||||
Future<void> clearFavourites() async {
|
Future<void> clearFavourites() async {
|
||||||
|
@ -177,15 +200,27 @@ class MetadataDb {
|
||||||
// final stopwatch = Stopwatch()..start();
|
// final stopwatch = Stopwatch()..start();
|
||||||
final db = await _database;
|
final db = await _database;
|
||||||
final batch = db.batch();
|
final batch = db.batch();
|
||||||
favouriteRows.where((row) => row != null).forEach((row) => batch.insert(
|
favouriteRows.where((row) => row != null).forEach((row) => _batchInsertFavourite(batch, row));
|
||||||
favouriteTable,
|
|
||||||
row.toMap(),
|
|
||||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
|
||||||
));
|
|
||||||
await batch.commit(noResult: true);
|
await batch.commit(noResult: true);
|
||||||
// debugPrint('$runtimeType addFavourites complete in ${stopwatch.elapsed.inMilliseconds}ms for ${favouriteRows.length} entries');
|
// debugPrint('$runtimeType addFavourites complete in ${stopwatch.elapsed.inMilliseconds}ms for ${favouriteRows.length} entries');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> updateFavouriteId(int oldId, FavouriteRow row) async {
|
||||||
|
final db = await _database;
|
||||||
|
final batch = db.batch();
|
||||||
|
batch.delete(favouriteTable, where: 'contentId = ?', whereArgs: [oldId]);
|
||||||
|
_batchInsertFavourite(batch, row);
|
||||||
|
await batch.commit(noResult: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _batchInsertFavourite(Batch batch, FavouriteRow row) {
|
||||||
|
batch.insert(
|
||||||
|
favouriteTable,
|
||||||
|
row.toMap(),
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> removeFavourites(Iterable<FavouriteRow> favouriteRows) async {
|
Future<void> removeFavourites(Iterable<FavouriteRow> favouriteRows) async {
|
||||||
if (favouriteRows == null || favouriteRows.isEmpty) return;
|
if (favouriteRows == null || favouriteRows.isEmpty) return;
|
||||||
final ids = favouriteRows.where((row) => row != null).map((row) => row.contentId);
|
final ids = favouriteRows.where((row) => row != null).map((row) => row.contentId);
|
||||||
|
@ -195,11 +230,7 @@ class MetadataDb {
|
||||||
// final stopwatch = Stopwatch()..start();
|
// final stopwatch = Stopwatch()..start();
|
||||||
final db = await _database;
|
final db = await _database;
|
||||||
final batch = db.batch();
|
final batch = db.batch();
|
||||||
ids.forEach((id) => batch.delete(
|
ids.forEach((id) => batch.delete(favouriteTable, where: 'contentId = ?', whereArgs: [id]));
|
||||||
favouriteTable,
|
|
||||||
where: 'contentId = ?',
|
|
||||||
whereArgs: [id],
|
|
||||||
));
|
|
||||||
await batch.commit(noResult: true);
|
await batch.commit(noResult: true);
|
||||||
// debugPrint('$runtimeType removeFavourites complete in ${stopwatch.elapsed.inMilliseconds}ms for ${favouriteRows.length} entries');
|
// debugPrint('$runtimeType removeFavourites complete in ${stopwatch.elapsed.inMilliseconds}ms for ${favouriteRows.length} entries');
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ class ImageFileService {
|
||||||
|
|
||||||
static Future<T> resumeThumbnail<T>(Object taskKey) => servicePolicy.resume<T>(taskKey, thumbnailPriority);
|
static Future<T> resumeThumbnail<T>(Object taskKey) => servicePolicy.resume<T>(taskKey, thumbnailPriority);
|
||||||
|
|
||||||
static Stream<ImageOpEvent> delete(List<ImageEntry> entries) {
|
static Stream<ImageOpEvent> delete(Iterable<ImageEntry> entries) {
|
||||||
try {
|
try {
|
||||||
return opChannel.receiveBroadcastStream(<String, dynamic>{
|
return opChannel.receiveBroadcastStream(<String, dynamic>{
|
||||||
'op': 'delete',
|
'op': 'delete',
|
||||||
|
@ -96,14 +96,14 @@ class ImageFileService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Stream<MoveOpEvent> move(List<ImageEntry> entries, {@required bool copy, @required String destinationPath}) {
|
static Stream<MoveOpEvent> move(Iterable<ImageEntry> entries, {@required bool copy, @required String destinationAlbum}) {
|
||||||
debugPrint('move ${entries.length} entries');
|
debugPrint('move ${entries.length} entries');
|
||||||
try {
|
try {
|
||||||
return opChannel.receiveBroadcastStream(<String, dynamic>{
|
return opChannel.receiveBroadcastStream(<String, dynamic>{
|
||||||
'op': 'move',
|
'op': 'move',
|
||||||
'entries': entries.map((e) => e.toMap()).toList(),
|
'entries': entries.map((e) => e.toMap()).toList(),
|
||||||
'copy': copy,
|
'copy': copy,
|
||||||
'destinationPath': destinationPath,
|
'destinationPath': destinationAlbum,
|
||||||
}).map((event) => MoveOpEvent.fromMap(event));
|
}).map((event) => MoveOpEvent.fromMap(event));
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
debugPrint('move failed with code=${e.code}, exception=${e.message}, details=${e.details}');
|
debugPrint('move failed with code=${e.code}, exception=${e.message}, details=${e.details}');
|
||||||
|
|
|
@ -82,7 +82,7 @@ class _LicensesState extends State<Licenses> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
tooltip: 'Sort',
|
tooltip: 'Sort',
|
||||||
icon: Icon(AIcons.sort),
|
icon: const Icon(AIcons.sort),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -188,8 +188,6 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
),
|
),
|
||||||
const PopupMenuItem(
|
const PopupMenuItem(
|
||||||
value: CollectionAction.move,
|
value: CollectionAction.move,
|
||||||
// TODO TLAD enable when handled on native side
|
|
||||||
enabled: false,
|
|
||||||
child: MenuRow(text: 'Move to album'),
|
child: MenuRow(text: 'Move to album'),
|
||||||
),
|
),
|
||||||
const PopupMenuItem(
|
const PopupMenuItem(
|
||||||
|
|
|
@ -167,7 +167,7 @@ class EntryActionDelegate with PermissionAwareMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showRenameDialog(BuildContext context, ImageEntry entry) async {
|
Future<void> _showRenameDialog(BuildContext context, ImageEntry entry) async {
|
||||||
final currentName = entry.filename ?? entry.sourceTitle;
|
final currentName = entry.filenameWithoutExtension ?? entry.sourceTitle;
|
||||||
final controller = TextEditingController(text: currentName);
|
final controller = TextEditingController(text: currentName);
|
||||||
final newName = await showDialog<String>(
|
final newName = await showDialog<String>(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
||||||
import 'package:aves/model/collection_lens.dart';
|
import 'package:aves/model/collection_lens.dart';
|
||||||
import 'package:aves/model/filters/album.dart';
|
import 'package:aves/model/filters/album.dart';
|
||||||
import 'package:aves/model/image_entry.dart';
|
import 'package:aves/model/image_entry.dart';
|
||||||
|
import 'package:aves/model/image_metadata.dart';
|
||||||
import 'package:aves/model/metadata_db.dart';
|
import 'package:aves/model/metadata_db.dart';
|
||||||
import 'package:aves/services/android_app_service.dart';
|
import 'package:aves/services/android_app_service.dart';
|
||||||
import 'package:aves/services/image_file_service.dart';
|
import 'package:aves/services/image_file_service.dart';
|
||||||
|
@ -87,7 +88,7 @@ class SelectionActionDelegate with PermissionAwareMixin {
|
||||||
_showOpReport(
|
_showOpReport(
|
||||||
context: context,
|
context: context,
|
||||||
selection: selection,
|
selection: selection,
|
||||||
opStream: ImageFileService.move(selection, copy: copy, destinationPath: filter.album),
|
opStream: ImageFileService.move(selection, copy: copy, destinationAlbum: filter.album),
|
||||||
onDone: (Set<MoveOpEvent> processed) {
|
onDone: (Set<MoveOpEvent> processed) {
|
||||||
debugPrint('$runtimeType _moveSelection onDone');
|
debugPrint('$runtimeType _moveSelection onDone');
|
||||||
final movedOps = processed.where((e) => e.success);
|
final movedOps = processed.where((e) => e.success);
|
||||||
|
@ -98,30 +99,44 @@ class SelectionActionDelegate with PermissionAwareMixin {
|
||||||
_showFeedback(context, 'Failed to move ${Intl.plural(count, one: '${count} item', other: '${count} items')}');
|
_showFeedback(context, 'Failed to move ${Intl.plural(count, one: '${count} item', other: '${count} items')}');
|
||||||
}
|
}
|
||||||
if (movedCount > 0) {
|
if (movedCount > 0) {
|
||||||
|
final source = collection.source;
|
||||||
if (copy) {
|
if (copy) {
|
||||||
final newEntries = <ImageEntry>[];
|
final newEntries = movedOps.map((movedOp) {
|
||||||
final newFavs = <ImageEntry>[];
|
|
||||||
movedOps.forEach((movedOp) {
|
|
||||||
final sourceUri = movedOp.uri;
|
final sourceUri = movedOp.uri;
|
||||||
final newFields = movedOp.newFields;
|
final newFields = movedOp.newFields;
|
||||||
final sourceEntry = selection.firstWhere((entry) => entry.uri == sourceUri, orElse: () => null);
|
final sourceEntry = selection.firstWhere((entry) => entry.uri == sourceUri, orElse: () => null);
|
||||||
final copy = sourceEntry?.copyWith(
|
return sourceEntry?.copyWith(
|
||||||
uri: newFields['uri'] as String,
|
uri: newFields['uri'] as String,
|
||||||
path: newFields['path'] as String,
|
path: newFields['path'] as String,
|
||||||
contentId: newFields['contentId'] as int,
|
contentId: newFields['contentId'] as int,
|
||||||
);
|
);
|
||||||
newEntries.add(copy);
|
}).toList();
|
||||||
if (sourceEntry.isFavourite) {
|
source.addAll(newEntries);
|
||||||
newFavs.add(copy);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
collection.source.addAll(newEntries);
|
|
||||||
metadataDb.saveMetadata(newEntries.map((entry) => entry.catalogMetadata));
|
metadataDb.saveMetadata(newEntries.map((entry) => entry.catalogMetadata));
|
||||||
metadataDb.saveAddresses(newEntries.map((entry) => entry.addressDetails));
|
metadataDb.saveAddresses(newEntries.map((entry) => entry.addressDetails));
|
||||||
newFavs.forEach((entry) => entry.addToFavourites());
|
|
||||||
} else {
|
} else {
|
||||||
// TODO TLAD update old entries path/dir/ID
|
final movedEntries = <ImageEntry>[];
|
||||||
// TODO TLAD update DB for catalog/address/fav
|
final fromAlbums = <String>{};
|
||||||
|
movedOps.forEach((movedOp) {
|
||||||
|
final sourceUri = movedOp.uri;
|
||||||
|
final newFields = movedOp.newFields;
|
||||||
|
final entry = selection.firstWhere((entry) => entry.uri == sourceUri, orElse: () => null);
|
||||||
|
if (entry != null) {
|
||||||
|
fromAlbums.add(entry.directory);
|
||||||
|
final oldContentId = entry.contentId;
|
||||||
|
final newContentId = newFields['contentId'] as int;
|
||||||
|
entry.uri = newFields['uri'] as String;
|
||||||
|
entry.path = newFields['path'] as String;
|
||||||
|
entry.contentId = newContentId;
|
||||||
|
|
||||||
|
metadataDb.updateMetadataId(oldContentId, entry.catalogMetadata);
|
||||||
|
metadataDb.updateAddressId(oldContentId, entry.addressDetails);
|
||||||
|
metadataDb.updateFavouriteId(oldContentId, FavouriteRow(contentId: entry.contentId, path: entry.path));
|
||||||
|
}
|
||||||
|
movedEntries.add(entry);
|
||||||
|
});
|
||||||
|
source.cleanEmptyAlbums(fromAlbums);
|
||||||
|
source.notifyMovedEntries(movedEntries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
collection.clearSelection();
|
collection.clearSelection();
|
||||||
|
|
Loading…
Reference in a new issue