improved metadata refreshing to include initial store data
This commit is contained in:
parent
dea00555e9
commit
d28ea44ff2
15 changed files with 47 additions and 68 deletions
|
@ -23,6 +23,7 @@ class ImageEntry {
|
||||||
String _path, _directory, _filename, _extension;
|
String _path, _directory, _filename, _extension;
|
||||||
int contentId;
|
int contentId;
|
||||||
final String sourceMimeType;
|
final String sourceMimeType;
|
||||||
|
|
||||||
// TODO TLAD use SVG viewport as width/height
|
// TODO TLAD use SVG viewport as width/height
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
|
|
@ -143,7 +143,7 @@ class MetadataDb {
|
||||||
await init();
|
await init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeIds(List<int> contentIds) async {
|
void removeIds(Set<int> contentIds, {@required bool updateFavourites}) async {
|
||||||
if (contentIds == null || contentIds.isEmpty) return;
|
if (contentIds == null || contentIds.isEmpty) return;
|
||||||
|
|
||||||
final stopwatch = Stopwatch()..start();
|
final stopwatch = Stopwatch()..start();
|
||||||
|
@ -157,7 +157,9 @@ class MetadataDb {
|
||||||
batch.delete(dateTakenTable, where: where, whereArgs: whereArgs);
|
batch.delete(dateTakenTable, where: where, whereArgs: whereArgs);
|
||||||
batch.delete(metadataTable, where: where, whereArgs: whereArgs);
|
batch.delete(metadataTable, where: where, whereArgs: whereArgs);
|
||||||
batch.delete(addressTable, where: where, whereArgs: whereArgs);
|
batch.delete(addressTable, where: where, whereArgs: whereArgs);
|
||||||
|
if (updateFavourites) {
|
||||||
batch.delete(favouriteTable, where: where, whereArgs: whereArgs);
|
batch.delete(favouriteTable, where: where, whereArgs: whereArgs);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
await batch.commit(noResult: true);
|
await batch.commit(noResult: true);
|
||||||
debugPrint('$runtimeType removeIds complete in ${stopwatch.elapsed.inMilliseconds}ms for ${contentIds.length} entries');
|
debugPrint('$runtimeType removeIds complete in ${stopwatch.elapsed.inMilliseconds}ms for ${contentIds.length} entries');
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'dart:collection';
|
||||||
import 'package:aves/model/filters/album.dart';
|
import 'package:aves/model/filters/album.dart';
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
import 'package:aves/model/image_entry.dart';
|
import 'package:aves/model/image_entry.dart';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
|
||||||
import 'package:aves/model/source/collection_source.dart';
|
import 'package:aves/model/source/collection_source.dart';
|
||||||
import 'package:aves/model/source/tag.dart';
|
import 'package:aves/model/source/tag.dart';
|
||||||
import 'package:aves/utils/change_notifier.dart';
|
import 'package:aves/utils/change_notifier.dart';
|
||||||
|
@ -50,14 +49,6 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
factory CollectionLens.empty() {
|
|
||||||
return CollectionLens(
|
|
||||||
source: CollectionSource(),
|
|
||||||
groupFactor: settings.collectionGroupFactor,
|
|
||||||
sortFactor: settings.collectionSortFactor,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
CollectionLens derive(CollectionFilter filter) {
|
CollectionLens derive(CollectionFilter filter) {
|
||||||
return CollectionLens(
|
return CollectionLens(
|
||||||
source: source,
|
source: source,
|
||||||
|
|
|
@ -37,7 +37,7 @@ mixin SourceBase {
|
||||||
void setProgress({@required int done, @required int total}) => _progressStreamController.add(ProgressEvent(done: done, total: total));
|
void setProgress({@required int done, @required int total}) => _progressStreamController.add(ProgressEvent(done: done, total: total));
|
||||||
}
|
}
|
||||||
|
|
||||||
class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagMixin {
|
abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagMixin {
|
||||||
@override
|
@override
|
||||||
List<ImageEntry> get sortedEntriesForFilterList => CollectionLens(
|
List<ImageEntry> get sortedEntriesForFilterList => CollectionLens(
|
||||||
source: this,
|
source: this,
|
||||||
|
@ -109,7 +109,7 @@ class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateAfterMove({
|
void updateAfterMove({
|
||||||
@required List<ImageEntry> selection,
|
@required Set<ImageEntry> selection,
|
||||||
@required bool copy,
|
@required bool copy,
|
||||||
@required String destinationAlbum,
|
@required String destinationAlbum,
|
||||||
@required Iterable<MoveOpEvent> movedOps,
|
@required Iterable<MoveOpEvent> movedOps,
|
||||||
|
@ -163,6 +163,10 @@ class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagMixin {
|
||||||
int count(CollectionFilter filter) {
|
int count(CollectionFilter filter) {
|
||||||
return _filterEntryCountMap.putIfAbsent(filter, () => _rawEntries.where((entry) => filter.filter(entry)).length);
|
return _filterEntryCountMap.putIfAbsent(filter, () => _rawEntries.where((entry) => filter.filter(entry)).length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> refresh();
|
||||||
|
|
||||||
|
Future<void> refreshMetadata(Set<ImageEntry> entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SourceState { loading, cataloguing, locating, ready }
|
enum SourceState { loading, cataloguing, locating, ready }
|
||||||
|
|
|
@ -14,7 +14,6 @@ import 'package:aves/widgets/common/action_delegates/selection_action_delegate.d
|
||||||
import 'package:aves/widgets/common/app_bar_subtitle.dart';
|
import 'package:aves/widgets/common/app_bar_subtitle.dart';
|
||||||
import 'package:aves/widgets/common/app_bar_title.dart';
|
import 'package:aves/widgets/common/app_bar_title.dart';
|
||||||
import 'package:aves/widgets/common/aves_selection_dialog.dart';
|
import 'package:aves/widgets/common/aves_selection_dialog.dart';
|
||||||
import 'package:aves/widgets/common/data_providers/media_store_collection_provider.dart';
|
|
||||||
import 'package:aves/widgets/common/entry_actions.dart';
|
import 'package:aves/widgets/common/entry_actions.dart';
|
||||||
import 'package:aves/widgets/common/icons.dart';
|
import 'package:aves/widgets/common/icons.dart';
|
||||||
import 'package:aves/widgets/common/menu_row.dart';
|
import 'package:aves/widgets/common/menu_row.dart';
|
||||||
|
@ -290,10 +289,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
_actionDelegate.onCollectionActionSelected(context, action);
|
_actionDelegate.onCollectionActionSelected(context, action);
|
||||||
break;
|
break;
|
||||||
case CollectionAction.refresh:
|
case CollectionAction.refresh:
|
||||||
if (source is MediaStoreSource) {
|
unawaited(source.refresh());
|
||||||
source.clearEntries();
|
|
||||||
unawaited((source as MediaStoreSource).refresh());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case CollectionAction.select:
|
case CollectionAction.select:
|
||||||
collection.select();
|
collection.select();
|
||||||
|
|
|
@ -128,14 +128,14 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _flip(BuildContext context, ImageEntry entry) async {
|
Future<void> _flip(BuildContext context, ImageEntry entry) async {
|
||||||
if (!await checkStoragePermission(context, [entry])) return;
|
if (!await checkStoragePermission(context, {entry})) return;
|
||||||
|
|
||||||
final success = await entry.flip();
|
final success = await entry.flip();
|
||||||
if (!success) showFeedback(context, 'Failed');
|
if (!success) showFeedback(context, 'Failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _rotate(BuildContext context, ImageEntry entry, {@required bool clockwise}) async {
|
Future<void> _rotate(BuildContext context, ImageEntry entry, {@required bool clockwise}) async {
|
||||||
if (!await checkStoragePermission(context, [entry])) return;
|
if (!await checkStoragePermission(context, {entry})) return;
|
||||||
|
|
||||||
final success = await entry.rotate(clockwise: clockwise);
|
final success = await entry.rotate(clockwise: clockwise);
|
||||||
if (!success) showFeedback(context, 'Failed');
|
if (!success) showFeedback(context, 'Failed');
|
||||||
|
@ -162,7 +162,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin {
|
||||||
);
|
);
|
||||||
if (confirmed == null || !confirmed) return;
|
if (confirmed == null || !confirmed) return;
|
||||||
|
|
||||||
if (!await checkStoragePermission(context, [entry])) return;
|
if (!await checkStoragePermission(context, {entry})) return;
|
||||||
|
|
||||||
if (!await entry.delete()) {
|
if (!await entry.delete()) {
|
||||||
showFeedback(context, 'Failed');
|
showFeedback(context, 'Failed');
|
||||||
|
@ -185,7 +185,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin {
|
||||||
);
|
);
|
||||||
if (newName == null || newName.isEmpty) return;
|
if (newName == null || newName.isEmpty) return;
|
||||||
|
|
||||||
if (!await checkStoragePermission(context, [entry])) return;
|
if (!await checkStoragePermission(context, {entry})) return;
|
||||||
|
|
||||||
showFeedback(context, await entry.rename(newName) ? 'Done!' : 'Failed');
|
showFeedback(context, await entry.rename(newName) ? 'Done!' : 'Failed');
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ mixin FeedbackMixin {
|
||||||
|
|
||||||
void showOpReport<T extends ImageOpEvent>({
|
void showOpReport<T extends ImageOpEvent>({
|
||||||
@required BuildContext context,
|
@required BuildContext context,
|
||||||
@required List<ImageEntry> selection,
|
@required Set<ImageEntry> selection,
|
||||||
@required Stream<T> opStream,
|
@required Stream<T> opStream,
|
||||||
@required void Function(Set<T> processed) onDone,
|
@required void Function(Set<T> processed) onDone,
|
||||||
}) {
|
}) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
|
||||||
import '../aves_dialog.dart';
|
import '../aves_dialog.dart';
|
||||||
|
|
||||||
mixin PermissionAwareMixin {
|
mixin PermissionAwareMixin {
|
||||||
Future<bool> checkStoragePermission(BuildContext context, Iterable<ImageEntry> entries) {
|
Future<bool> checkStoragePermission(BuildContext context, Set<ImageEntry> entries) {
|
||||||
return checkStoragePermissionForAlbums(context, entries.where((e) => e.path != null).map((e) => e.directory).toSet());
|
return checkStoragePermissionForAlbums(context, entries.where((e) => e.path != null).map((e) => e.directory).toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
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/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/model/source/collection_lens.dart';
|
import 'package:aves/model/source/collection_lens.dart';
|
||||||
import 'package:aves/model/source/collection_source.dart';
|
import 'package:aves/model/source/collection_source.dart';
|
||||||
|
@ -29,6 +30,10 @@ import 'package:provider/provider.dart';
|
||||||
class SelectionActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
|
class SelectionActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
|
||||||
final CollectionLens collection;
|
final CollectionLens collection;
|
||||||
|
|
||||||
|
CollectionSource get source => collection.source;
|
||||||
|
|
||||||
|
Set<ImageEntry> get selection => collection.selection;
|
||||||
|
|
||||||
SelectionActionDelegate({
|
SelectionActionDelegate({
|
||||||
@required this.collection,
|
@required this.collection,
|
||||||
});
|
});
|
||||||
|
@ -39,7 +44,7 @@ class SelectionActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwar
|
||||||
_showDeleteDialog(context);
|
_showDeleteDialog(context);
|
||||||
break;
|
break;
|
||||||
case EntryAction.share:
|
case EntryAction.share:
|
||||||
AndroidAppService.share(collection.selection);
|
AndroidAppService.share(selection);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -55,7 +60,9 @@ class SelectionActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwar
|
||||||
_moveSelection(context, copy: false);
|
_moveSelection(context, copy: false);
|
||||||
break;
|
break;
|
||||||
case CollectionAction.refreshMetadata:
|
case CollectionAction.refreshMetadata:
|
||||||
_refreshSelectionMetadata();
|
source.refreshMetadata(selection);
|
||||||
|
collection.clearSelection();
|
||||||
|
collection.browse();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -63,7 +70,6 @@ class SelectionActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwar
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _moveSelection(BuildContext context, {@required bool copy}) async {
|
Future<void> _moveSelection(BuildContext context, {@required bool copy}) async {
|
||||||
final source = collection.source;
|
|
||||||
final chipSetActionDelegate = AlbumChipSetActionDelegate(source: source);
|
final chipSetActionDelegate = AlbumChipSetActionDelegate(source: source);
|
||||||
final destinationAlbum = await Navigator.push(
|
final destinationAlbum = await Navigator.push(
|
||||||
context,
|
context,
|
||||||
|
@ -114,7 +120,6 @@ class SelectionActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwar
|
||||||
if (destinationAlbum == null || destinationAlbum.isEmpty) return;
|
if (destinationAlbum == null || destinationAlbum.isEmpty) return;
|
||||||
if (!await checkStoragePermissionForAlbums(context, {destinationAlbum})) return;
|
if (!await checkStoragePermissionForAlbums(context, {destinationAlbum})) return;
|
||||||
|
|
||||||
final selection = collection.selection.toList();
|
|
||||||
if (!await checkStoragePermission(context, selection)) return;
|
if (!await checkStoragePermission(context, selection)) return;
|
||||||
|
|
||||||
if (!await checkFreeSpaceForMove(context, selection, destinationAlbum, copy)) return;
|
if (!await checkFreeSpaceForMove(context, selection, destinationAlbum, copy)) return;
|
||||||
|
@ -146,18 +151,7 @@ class SelectionActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwar
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _refreshSelectionMetadata() async {
|
|
||||||
collection.selection.forEach((entry) => entry.clearMetadata());
|
|
||||||
final source = collection.source;
|
|
||||||
source.stateNotifier.value = SourceState.cataloguing;
|
|
||||||
await source.catalogEntries();
|
|
||||||
source.stateNotifier.value = SourceState.locating;
|
|
||||||
await source.locateEntries();
|
|
||||||
source.stateNotifier.value = SourceState.ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _showDeleteDialog(BuildContext context) async {
|
Future<void> _showDeleteDialog(BuildContext context) async {
|
||||||
final selection = collection.selection.toList();
|
|
||||||
final count = selection.length;
|
final count = selection.length;
|
||||||
|
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await showDialog<bool>(
|
||||||
|
@ -195,7 +189,7 @@ class SelectionActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwar
|
||||||
showFeedback(context, 'Failed to delete ${Intl.plural(count, one: '$count item', other: '$count items')}');
|
showFeedback(context, 'Failed to delete ${Intl.plural(count, one: '$count item', other: '$count items')}');
|
||||||
}
|
}
|
||||||
if (deletedCount > 0) {
|
if (deletedCount > 0) {
|
||||||
collection.source.removeEntries(selection.where((e) => deletedUris.contains(e.uri)).toList());
|
source.removeEntries(selection.where((e) => deletedUris.contains(e.uri)).toList());
|
||||||
}
|
}
|
||||||
collection.clearSelection();
|
collection.clearSelection();
|
||||||
collection.browse();
|
collection.browse();
|
||||||
|
|
|
@ -11,7 +11,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
mixin SizeAwareMixin {
|
mixin SizeAwareMixin {
|
||||||
Future<bool> checkFreeSpaceForMove(BuildContext context, List<ImageEntry> selection, String destinationAlbum, bool copy) async {
|
Future<bool> checkFreeSpaceForMove(BuildContext context, Set<ImageEntry> selection, String destinationAlbum, bool copy) async {
|
||||||
final destinationVolume = androidFileUtils.getStorageVolume(destinationAlbum);
|
final destinationVolume = androidFileUtils.getStorageVolume(destinationAlbum);
|
||||||
final free = await AndroidFileService.getFreeSpace(destinationVolume);
|
final free = await AndroidFileService.getFreeSpace(destinationVolume);
|
||||||
int needed;
|
int needed;
|
||||||
|
|
|
@ -31,14 +31,16 @@ class MediaStoreSource extends CollectionSource {
|
||||||
debugPrint('$runtimeType init done, elapsed=${stopwatch.elapsed}');
|
debugPrint('$runtimeType init done, elapsed=${stopwatch.elapsed}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
Future<void> refresh() async {
|
Future<void> refresh() async {
|
||||||
debugPrint('$runtimeType refresh start');
|
debugPrint('$runtimeType refresh start');
|
||||||
final stopwatch = Stopwatch()..start();
|
final stopwatch = Stopwatch()..start();
|
||||||
stateNotifier.value = SourceState.loading;
|
stateNotifier.value = SourceState.loading;
|
||||||
|
clearEntries();
|
||||||
|
|
||||||
final oldEntries = await metadataDb.loadEntries(); // 400ms for 5500 entries
|
final oldEntries = await metadataDb.loadEntries(); // 400ms for 5500 entries
|
||||||
final knownEntryMap = Map.fromEntries(oldEntries.map((entry) => MapEntry(entry.contentId, entry.dateModifiedSecs)));
|
final knownEntryMap = Map.fromEntries(oldEntries.map((entry) => MapEntry(entry.contentId, entry.dateModifiedSecs)));
|
||||||
final obsoleteEntries = await ImageFileService.getObsoleteEntries(knownEntryMap.keys.toList());
|
final obsoleteEntries = (await ImageFileService.getObsoleteEntries(knownEntryMap.keys.toList())).toSet();
|
||||||
oldEntries.removeWhere((entry) => obsoleteEntries.contains(entry.contentId));
|
oldEntries.removeWhere((entry) => obsoleteEntries.contains(entry.contentId));
|
||||||
|
|
||||||
// show known entries
|
// show known entries
|
||||||
|
@ -48,7 +50,7 @@ class MediaStoreSource extends CollectionSource {
|
||||||
debugPrint('$runtimeType refresh loaded ${oldEntries.length} known entries, elapsed=${stopwatch.elapsed}');
|
debugPrint('$runtimeType refresh loaded ${oldEntries.length} known entries, elapsed=${stopwatch.elapsed}');
|
||||||
|
|
||||||
// clean up obsolete entries
|
// clean up obsolete entries
|
||||||
metadataDb.removeIds(obsoleteEntries);
|
metadataDb.removeIds(obsoleteEntries, updateFavourites: true);
|
||||||
|
|
||||||
// fetch new entries
|
// fetch new entries
|
||||||
var refreshCount = 10;
|
var refreshCount = 10;
|
||||||
|
@ -92,4 +94,11 @@ class MediaStoreSource extends CollectionSource {
|
||||||
onError: (error) => debugPrint('$runtimeType stream error=$error'),
|
onError: (error) => debugPrint('$runtimeType stream error=$error'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> refreshMetadata(Set<ImageEntry> entries) {
|
||||||
|
final contentIds = entries.map((entry) => entry.contentId).toSet();
|
||||||
|
metadataDb.removeIds(contentIds, updateFavourites: false);
|
||||||
|
return refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ class AlbumChipActionDelegate extends ChipActionDelegate with FeedbackMixin, Per
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showDeleteDialog(BuildContext context, AlbumFilter filter) async {
|
Future<void> _showDeleteDialog(BuildContext context, AlbumFilter filter) async {
|
||||||
final selection = source.rawEntries.where(filter.filter).toList();
|
final selection = source.rawEntries.where(filter.filter).toSet();
|
||||||
final count = selection.length;
|
final count = selection.length;
|
||||||
|
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await showDialog<bool>(
|
||||||
|
@ -111,7 +111,7 @@ class AlbumChipActionDelegate extends ChipActionDelegate with FeedbackMixin, Per
|
||||||
|
|
||||||
if (!await checkStoragePermissionForAlbums(context, {album})) return;
|
if (!await checkStoragePermissionForAlbums(context, {album})) return;
|
||||||
|
|
||||||
final selection = source.rawEntries.where(filter.filter).toList();
|
final selection = source.rawEntries.where(filter.filter).toSet();
|
||||||
final destinationAlbum = path.join(path.dirname(album), newName);
|
final destinationAlbum = path.join(path.dirname(album), newName);
|
||||||
|
|
||||||
if (!await checkFreeSpaceForMove(context, selection, destinationAlbum, false)) return;
|
if (!await checkFreeSpaceForMove(context, selection, destinationAlbum, false)) return;
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'package:aves/model/source/collection_source.dart';
|
||||||
import 'package:aves/model/source/enums.dart';
|
import 'package:aves/model/source/enums.dart';
|
||||||
import 'package:aves/utils/durations.dart';
|
import 'package:aves/utils/durations.dart';
|
||||||
import 'package:aves/widgets/common/aves_selection_dialog.dart';
|
import 'package:aves/widgets/common/aves_selection_dialog.dart';
|
||||||
import 'package:aves/widgets/common/data_providers/media_store_collection_provider.dart';
|
|
||||||
import 'package:aves/widgets/filter_grids/common/chip_actions.dart';
|
import 'package:aves/widgets/filter_grids/common/chip_actions.dart';
|
||||||
import 'package:aves/widgets/stats/stats.dart';
|
import 'package:aves/widgets/stats/stats.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -27,10 +26,7 @@ abstract class ChipSetActionDelegate {
|
||||||
await _showSortDialog(context);
|
await _showSortDialog(context);
|
||||||
break;
|
break;
|
||||||
case ChipSetAction.refresh:
|
case ChipSetAction.refresh:
|
||||||
if (source is MediaStoreSource) {
|
unawaited(source.refresh());
|
||||||
source.clearEntries();
|
|
||||||
unawaited((source as MediaStoreSource).refresh());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ChipSetAction.stats:
|
case ChipSetAction.stats:
|
||||||
_goToStats(context);
|
_goToStats(context);
|
||||||
|
|
|
@ -41,21 +41,6 @@ class _DbTabState extends State<DbTab> {
|
||||||
return ListView(
|
return ListView(
|
||||||
padding: EdgeInsets.all(16),
|
padding: EdgeInsets.all(16),
|
||||||
children: [
|
children: [
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text('DB'),
|
|
||||||
),
|
|
||||||
SizedBox(width: 8),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await metadataDb.removeIds([entry.contentId]);
|
|
||||||
_loadDatabase();
|
|
||||||
},
|
|
||||||
child: Text('Remove from DB'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
FutureBuilder<DateMetadata>(
|
FutureBuilder<DateMetadata>(
|
||||||
future: _dbDateLoader,
|
future: _dbDateLoader,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
|
|
@ -72,7 +72,9 @@ class _ImageViewState extends State<ImageView> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget child;
|
Widget child;
|
||||||
if (entry.isVideo) {
|
if (entry.isVideo) {
|
||||||
|
if (entry.width > 0 && entry.height > 0) {
|
||||||
child = _buildVideoView();
|
child = _buildVideoView();
|
||||||
|
}
|
||||||
} else if (entry.isSvg) {
|
} else if (entry.isSvg) {
|
||||||
child = _buildSvgView();
|
child = _buildSvgView();
|
||||||
} else if (entry.canDecode) {
|
} else if (entry.canDecode) {
|
||||||
|
@ -81,9 +83,8 @@ class _ImageViewState extends State<ImageView> {
|
||||||
} else {
|
} else {
|
||||||
child = _buildImageView();
|
child = _buildImageView();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
child = _buildError();
|
|
||||||
}
|
}
|
||||||
|
child ??= _buildError();
|
||||||
|
|
||||||
// if the hero tag is defined in the `loadingBuilder` and also set by the `heroAttributes`,
|
// if the hero tag is defined in the `loadingBuilder` and also set by the `heroAttributes`,
|
||||||
// the route transition becomes visible if the final image is loaded before the hero animation is done.
|
// the route transition becomes visible if the final image is loaded before the hero animation is done.
|
||||||
|
|
Loading…
Reference in a new issue