selection: delete
This commit is contained in:
parent
1751b7b3d7
commit
a08c5a3369
8 changed files with 319 additions and 210 deletions
|
@ -31,7 +31,7 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
|
|||
groupFactor = groupFactor ?? GroupFactor.month,
|
||||
sortFactor = sortFactor ?? SortFactor.date {
|
||||
_subscriptions.add(source.eventBus.on<EntryAddedEvent>().listen((e) => onEntryAdded()));
|
||||
_subscriptions.add(source.eventBus.on<EntryRemovedEvent>().listen((e) => onEntryRemoved(e.entry)));
|
||||
_subscriptions.add(source.eventBus.on<EntryRemovedEvent>().listen((e) => onEntryRemoved(e.entries)));
|
||||
_subscriptions.add(source.eventBus.on<CatalogMetadataChangedEvent>().listen((e) => onMetadataChanged()));
|
||||
onEntryAdded();
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
|
|||
|
||||
int get entryCount => _filteredEntries.length;
|
||||
|
||||
// sorted as displayed to the user, i.e. sorted then grouped, not an absolute order on all entries
|
||||
List<ImageEntry> _sortedEntries;
|
||||
|
||||
List<ImageEntry> get sortedEntries {
|
||||
|
@ -180,11 +181,12 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
|
|||
_applyGroup();
|
||||
}
|
||||
|
||||
void onEntryRemoved(ImageEntry entry) {
|
||||
void onEntryRemoved(Iterable<ImageEntry> entries) {
|
||||
// do not apply sort/group as section order change would surprise the user while browsing
|
||||
_filteredEntries.remove(entry);
|
||||
_sortedEntries?.remove(entry);
|
||||
sections.forEach((key, entries) => entries.remove(entry));
|
||||
_filteredEntries.removeWhere(entries.contains);
|
||||
_sortedEntries?.removeWhere(entries.contains);
|
||||
sections.forEach((key, sectionEntries) => sectionEntries.removeWhere(entries.contains));
|
||||
selection.removeAll(entries);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
@ -223,14 +225,14 @@ mixin CollectionSelectionMixin on CollectionActivityMixin {
|
|||
|
||||
Set<ImageEntry> get selection => _selection;
|
||||
|
||||
bool isSelected(List<ImageEntry> entries) => entries.every(selection.contains);
|
||||
bool isSelected(Iterable<ImageEntry> entries) => entries.every(selection.contains);
|
||||
|
||||
void addToSelection(List<ImageEntry> entries) {
|
||||
void addToSelection(Iterable<ImageEntry> entries) {
|
||||
_selection.addAll(entries);
|
||||
selectionChangeNotifier.notifyListeners();
|
||||
}
|
||||
|
||||
void removeFromSelection(List<ImageEntry> entries) {
|
||||
void removeFromSelection(Iterable<ImageEntry> entries) {
|
||||
_selection.removeAll(entries);
|
||||
selectionChangeNotifier.notifyListeners();
|
||||
}
|
||||
|
|
|
@ -138,13 +138,9 @@ class CollectionSource {
|
|||
eventBus.fire(const EntryAddedEvent());
|
||||
}
|
||||
|
||||
Future<bool> delete(ImageEntry entry) async {
|
||||
final success = await entry.delete();
|
||||
if (success) {
|
||||
_rawEntries.remove(entry);
|
||||
eventBus.fire(EntryRemovedEvent(entry));
|
||||
}
|
||||
return success;
|
||||
void removeEntries(Iterable<ImageEntry> entries) async {
|
||||
_rawEntries.remove(entries);
|
||||
eventBus.fire(EntryRemovedEvent(entries));
|
||||
}
|
||||
|
||||
String getUniqueAlbumName(String album) {
|
||||
|
@ -170,7 +166,7 @@ class EntryAddedEvent {
|
|||
}
|
||||
|
||||
class EntryRemovedEvent {
|
||||
final ImageEntry entry;
|
||||
final Iterable<ImageEntry> entries;
|
||||
|
||||
const EntryRemovedEvent(this.entry);
|
||||
const EntryRemovedEvent(this.entries);
|
||||
}
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:aves/model/collection_lens.dart';
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/model/settings.dart';
|
||||
import 'package:aves/services/android_app_service.dart';
|
||||
import 'package:aves/services/image_file_service.dart';
|
||||
import 'package:aves/utils/constants.dart';
|
||||
import 'package:aves/widgets/album/filter_bar.dart';
|
||||
import 'package:aves/widgets/album/search/search_delegate.dart';
|
||||
import 'package:aves/widgets/common/entry_actions.dart';
|
||||
import 'package:aves/widgets/common/menu_row.dart';
|
||||
import 'package:aves/widgets/fullscreen/fullscreen_actions.dart';
|
||||
import 'package:aves/widgets/stats/stats.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flushbar/flushbar.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:outline_material_icons/outline_material_icons.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
import 'package:percent_indicator/circular_percent_indicator.dart';
|
||||
|
||||
class CollectionAppBar extends StatefulWidget {
|
||||
final ValueNotifier<double> appBarHeightNotifier;
|
||||
|
@ -150,17 +155,16 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
onPressed: _goToSearch,
|
||||
),
|
||||
if (collection.isSelecting)
|
||||
AnimatedBuilder(
|
||||
...EntryActions.selection.map((action) => AnimatedBuilder(
|
||||
animation: collection.selectionChangeNotifier,
|
||||
builder: (context, child) {
|
||||
const action = FullscreenAction.share;
|
||||
return IconButton(
|
||||
icon: Icon(action.getIcon()),
|
||||
onPressed: collection.selection.isEmpty ? null : _shareSelection,
|
||||
onPressed: collection.selection.isEmpty ? null : () => _onSelectionActionSelected(action),
|
||||
tooltip: action.getText(),
|
||||
);
|
||||
},
|
||||
),
|
||||
)),
|
||||
Builder(
|
||||
builder: (context) => PopupMenuButton<CollectionAction>(
|
||||
itemBuilder: (context) => [
|
||||
|
@ -177,7 +181,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
),
|
||||
],
|
||||
],
|
||||
onSelected: _onActionSelected,
|
||||
onSelected: _onCollectionActionSelected,
|
||||
),
|
||||
),
|
||||
];
|
||||
|
@ -221,7 +225,20 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
: [];
|
||||
}
|
||||
|
||||
void _onActionSelected(CollectionAction action) async {
|
||||
void _onActivityChange() {
|
||||
if (collection.isSelecting) {
|
||||
_browseToSelectAnimation.forward();
|
||||
} else {
|
||||
_browseToSelectAnimation.reverse();
|
||||
_searchFieldController.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void _updateHeight() {
|
||||
widget.appBarHeightNotifier.value = kToolbarHeight + (hasFilters ? FilterBar.preferredHeight : 0);
|
||||
}
|
||||
|
||||
void _onCollectionActionSelected(CollectionAction action) async {
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
await Future.delayed(Constants.popupMenuTransitionDuration);
|
||||
switch (action) {
|
||||
|
@ -277,22 +294,108 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
);
|
||||
}
|
||||
|
||||
void _onSelectionActionSelected(EntryAction action) {
|
||||
switch (action) {
|
||||
case EntryAction.share:
|
||||
_shareSelection();
|
||||
break;
|
||||
case EntryAction.delete:
|
||||
_deleteSelection();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _shareSelection() {
|
||||
final urisByMimeType = groupBy<ImageEntry, String>(collection.selection, (e) => e.mimeType).map((k, v) => MapEntry(k, v.map((e) => e.uri).toList()));
|
||||
AndroidAppService.share(urisByMimeType);
|
||||
}
|
||||
|
||||
void _onActivityChange() {
|
||||
if (collection.isSelecting) {
|
||||
_browseToSelectAnimation.forward();
|
||||
} else {
|
||||
_browseToSelectAnimation.reverse();
|
||||
_searchFieldController.clear();
|
||||
void _deleteSelection() {
|
||||
final selection = collection.selection.toList();
|
||||
_showOpReport(
|
||||
selection: selection,
|
||||
opStream: ImageFileService.delete(selection),
|
||||
onDone: (processed) {
|
||||
final deletedUris = processed.where((e) => e.success).map((e) => e.uri);
|
||||
final deletedCount = deletedUris.length;
|
||||
final selectionCount = selection.length;
|
||||
if (deletedCount < selectionCount) {
|
||||
_showFeedback(context, 'Failed to delete ${selectionCount - deletedCount} items');
|
||||
}
|
||||
if (deletedCount > 0) {
|
||||
collection.source.removeEntries(selection.where((e) => deletedUris.contains(e.uri)));
|
||||
}
|
||||
collection.browse();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _updateHeight() {
|
||||
widget.appBarHeightNotifier.value = kToolbarHeight + (hasFilters ? FilterBar.preferredHeight : 0);
|
||||
// selection action report overlay
|
||||
|
||||
OverlayEntry _opReportOverlayEntry;
|
||||
|
||||
static const _overlayAnimationDuration = Duration(milliseconds: 300);
|
||||
|
||||
void _showOpReport({
|
||||
@required List<ImageEntry> selection,
|
||||
@required Stream<ImageOpEvent> opStream,
|
||||
@required void Function(Set<ImageOpEvent> processed) onDone,
|
||||
}) {
|
||||
final processed = <ImageOpEvent>{};
|
||||
_opReportOverlayEntry = OverlayEntry(
|
||||
builder: (context) {
|
||||
return StreamBuilder<ImageOpEvent>(
|
||||
stream: opStream,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
processed.add(snapshot.data);
|
||||
}
|
||||
|
||||
Widget child = const SizedBox.shrink();
|
||||
if (snapshot.hasError || snapshot.connectionState == ConnectionState.done) {
|
||||
_hideOpReportOverlay().then((_) => onDone(processed));
|
||||
} else if (snapshot.connectionState == ConnectionState.active) {
|
||||
final percent = processed.length.toDouble() / selection.length;
|
||||
child = CircularPercentIndicator(
|
||||
percent: percent,
|
||||
lineWidth: 16,
|
||||
radius: 160,
|
||||
backgroundColor: Colors.white24,
|
||||
progressColor: Theme.of(context).accentColor,
|
||||
animation: true,
|
||||
center: Text(NumberFormat.percentPattern().format(percent)),
|
||||
animateFromLastPercent: true,
|
||||
);
|
||||
}
|
||||
return AnimatedSwitcher(
|
||||
duration: _overlayAnimationDuration,
|
||||
child: child,
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
Overlay.of(context).insert(_opReportOverlayEntry);
|
||||
}
|
||||
|
||||
Future<void> _hideOpReportOverlay() async {
|
||||
await Future.delayed(_overlayAnimationDuration);
|
||||
_opReportOverlayEntry.remove();
|
||||
_opReportOverlayEntry = null;
|
||||
}
|
||||
|
||||
void _showFeedback(BuildContext context, String message) {
|
||||
Flushbar(
|
||||
message: message,
|
||||
margin: const EdgeInsets.all(8),
|
||||
borderRadius: 8,
|
||||
borderColor: Colors.white30,
|
||||
borderWidth: 0.5,
|
||||
duration: const Duration(seconds: 2),
|
||||
flushbarPosition: FlushbarPosition.TOP,
|
||||
animationDuration: const Duration(milliseconds: 600),
|
||||
).show(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@ import 'package:aves/model/collection_lens.dart';
|
|||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/services/android_app_service.dart';
|
||||
import 'package:aves/services/image_file_service.dart';
|
||||
import 'package:aves/widgets/common/entry_actions.dart';
|
||||
import 'package:aves/widgets/common/image_providers/uri_image_provider.dart';
|
||||
import 'package:aves/widgets/fullscreen/debug.dart';
|
||||
import 'package:aves/widgets/fullscreen/fullscreen_actions.dart';
|
||||
import 'package:flushbar/flushbar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
@ -15,58 +15,58 @@ import 'package:pdf/widgets.dart' as pdf;
|
|||
import 'package:pedantic/pedantic.dart';
|
||||
import 'package:printing/printing.dart';
|
||||
|
||||
class FullscreenActionDelegate {
|
||||
class EntryActionDelegate {
|
||||
final CollectionLens collection;
|
||||
final VoidCallback showInfo;
|
||||
|
||||
FullscreenActionDelegate({
|
||||
EntryActionDelegate({
|
||||
@required this.collection,
|
||||
@required this.showInfo,
|
||||
});
|
||||
|
||||
bool get hasCollection => collection != null;
|
||||
|
||||
void onActionSelected(BuildContext context, ImageEntry entry, FullscreenAction action) {
|
||||
void onActionSelected(BuildContext context, ImageEntry entry, EntryAction action) {
|
||||
switch (action) {
|
||||
case FullscreenAction.toggleFavourite:
|
||||
case EntryAction.toggleFavourite:
|
||||
entry.toggleFavourite();
|
||||
break;
|
||||
case FullscreenAction.delete:
|
||||
case EntryAction.delete:
|
||||
_showDeleteDialog(context, entry);
|
||||
break;
|
||||
case FullscreenAction.edit:
|
||||
case EntryAction.edit:
|
||||
AndroidAppService.edit(entry.uri, entry.mimeType);
|
||||
break;
|
||||
case FullscreenAction.info:
|
||||
case EntryAction.info:
|
||||
showInfo();
|
||||
break;
|
||||
case FullscreenAction.rename:
|
||||
case EntryAction.rename:
|
||||
_showRenameDialog(context, entry);
|
||||
break;
|
||||
case FullscreenAction.open:
|
||||
case EntryAction.open:
|
||||
AndroidAppService.open(entry.uri, entry.mimeTypeAnySubtype);
|
||||
break;
|
||||
case FullscreenAction.openMap:
|
||||
case EntryAction.openMap:
|
||||
AndroidAppService.openMap(entry.geoUri);
|
||||
break;
|
||||
case FullscreenAction.print:
|
||||
case EntryAction.print:
|
||||
_print(entry);
|
||||
break;
|
||||
case FullscreenAction.rotateCCW:
|
||||
case EntryAction.rotateCCW:
|
||||
_rotate(context, entry, clockwise: false);
|
||||
break;
|
||||
case FullscreenAction.rotateCW:
|
||||
case EntryAction.rotateCW:
|
||||
_rotate(context, entry, clockwise: true);
|
||||
break;
|
||||
case FullscreenAction.setAs:
|
||||
case EntryAction.setAs:
|
||||
AndroidAppService.setAs(entry.uri, entry.mimeType);
|
||||
break;
|
||||
case FullscreenAction.share:
|
||||
case EntryAction.share:
|
||||
AndroidAppService.share({
|
||||
entry.mimeType: [entry.uri]
|
||||
});
|
||||
break;
|
||||
case FullscreenAction.debug:
|
||||
case EntryAction.debug:
|
||||
_goToDebug(context, entry);
|
||||
break;
|
||||
}
|
||||
|
@ -146,13 +146,16 @@ class FullscreenActionDelegate {
|
|||
},
|
||||
);
|
||||
if (confirmed == null || !confirmed) return;
|
||||
if (hasCollection) {
|
||||
if (!await collection.source.delete(entry)) {
|
||||
if (!await entry.delete()) {
|
||||
_showFeedback(context, 'Failed');
|
||||
} else if (collection.sortedEntries.isEmpty) {
|
||||
} else if (hasCollection) {
|
||||
// update collection
|
||||
collection.source.removeEntries([entry]);
|
||||
if (collection.sortedEntries.isEmpty) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} else if (await entry.delete()) {
|
||||
} else {
|
||||
// leave viewer
|
||||
exit(0);
|
||||
}
|
||||
}
|
99
lib/widgets/common/entry_actions.dart
Normal file
99
lib/widgets/common/entry_actions.dart
Normal file
|
@ -0,0 +1,99 @@
|
|||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:outline_material_icons/outline_material_icons.dart';
|
||||
|
||||
enum EntryAction { delete, edit, info, open, openMap, print, rename, rotateCCW, rotateCW, setAs, share, toggleFavourite, debug }
|
||||
|
||||
class EntryActions {
|
||||
static const selection = [
|
||||
EntryAction.share,
|
||||
EntryAction.delete,
|
||||
];
|
||||
|
||||
static const inApp = [
|
||||
EntryAction.info,
|
||||
EntryAction.toggleFavourite,
|
||||
EntryAction.share,
|
||||
EntryAction.delete,
|
||||
EntryAction.rename,
|
||||
EntryAction.rotateCCW,
|
||||
EntryAction.rotateCW,
|
||||
EntryAction.print,
|
||||
];
|
||||
|
||||
static const externalApp = [
|
||||
EntryAction.edit,
|
||||
EntryAction.open,
|
||||
EntryAction.setAs,
|
||||
EntryAction.openMap,
|
||||
];
|
||||
}
|
||||
|
||||
extension ExtraEntryAction on EntryAction {
|
||||
String getText() {
|
||||
switch (this) {
|
||||
// in app actions
|
||||
case EntryAction.toggleFavourite:
|
||||
// different data depending on toggle state
|
||||
return null;
|
||||
case EntryAction.delete:
|
||||
return 'Delete';
|
||||
case EntryAction.info:
|
||||
return 'Info';
|
||||
case EntryAction.rename:
|
||||
return 'Rename';
|
||||
case EntryAction.rotateCCW:
|
||||
return 'Rotate left';
|
||||
case EntryAction.rotateCW:
|
||||
return 'Rotate right';
|
||||
case EntryAction.print:
|
||||
return 'Print';
|
||||
case EntryAction.share:
|
||||
return 'Share';
|
||||
// external app actions
|
||||
case EntryAction.edit:
|
||||
return 'Edit with…';
|
||||
case EntryAction.open:
|
||||
return 'Open with…';
|
||||
case EntryAction.setAs:
|
||||
return 'Set as…';
|
||||
case EntryAction.openMap:
|
||||
return 'Show on map…';
|
||||
case EntryAction.debug:
|
||||
return 'Debug';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
IconData getIcon() {
|
||||
switch (this) {
|
||||
// in app actions
|
||||
case EntryAction.toggleFavourite:
|
||||
// different data depending on toggle state
|
||||
return null;
|
||||
case EntryAction.delete:
|
||||
return AIcons.delete;
|
||||
case EntryAction.info:
|
||||
return OMIcons.info;
|
||||
case EntryAction.rename:
|
||||
return OMIcons.title;
|
||||
case EntryAction.rotateCCW:
|
||||
return AIcons.rotateLeft;
|
||||
case EntryAction.rotateCW:
|
||||
return AIcons.rotateRight;
|
||||
case EntryAction.print:
|
||||
return AIcons.print;
|
||||
case EntryAction.share:
|
||||
return AIcons.share;
|
||||
// external app actions
|
||||
case EntryAction.edit:
|
||||
case EntryAction.open:
|
||||
case EntryAction.setAs:
|
||||
case EntryAction.openMap:
|
||||
return null;
|
||||
case EntryAction.debug:
|
||||
return OMIcons.whatshot;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:outline_material_icons/outline_material_icons.dart';
|
||||
|
||||
enum FullscreenAction { delete, edit, info, open, openMap, print, rename, rotateCCW, rotateCW, setAs, share, toggleFavourite, debug }
|
||||
|
||||
class FullscreenActions {
|
||||
static const inApp = [
|
||||
FullscreenAction.info,
|
||||
FullscreenAction.toggleFavourite,
|
||||
FullscreenAction.share,
|
||||
FullscreenAction.delete,
|
||||
FullscreenAction.rename,
|
||||
FullscreenAction.rotateCCW,
|
||||
FullscreenAction.rotateCW,
|
||||
FullscreenAction.print,
|
||||
];
|
||||
|
||||
static const externalApp = [
|
||||
FullscreenAction.edit,
|
||||
FullscreenAction.open,
|
||||
FullscreenAction.setAs,
|
||||
FullscreenAction.openMap,
|
||||
];
|
||||
}
|
||||
|
||||
extension ExtraFullscreenAction on FullscreenAction {
|
||||
String getText() {
|
||||
switch (this) {
|
||||
// in app actions
|
||||
case FullscreenAction.toggleFavourite:
|
||||
// different data depending on toggle state
|
||||
return null;
|
||||
case FullscreenAction.delete:
|
||||
return 'Delete';
|
||||
case FullscreenAction.info:
|
||||
return 'Info';
|
||||
case FullscreenAction.rename:
|
||||
return 'Rename';
|
||||
case FullscreenAction.rotateCCW:
|
||||
return 'Rotate left';
|
||||
case FullscreenAction.rotateCW:
|
||||
return 'Rotate right';
|
||||
case FullscreenAction.print:
|
||||
return 'Print';
|
||||
case FullscreenAction.share:
|
||||
return 'Share';
|
||||
// external app actions
|
||||
case FullscreenAction.edit:
|
||||
return 'Edit with…';
|
||||
case FullscreenAction.open:
|
||||
return 'Open with…';
|
||||
case FullscreenAction.setAs:
|
||||
return 'Set as…';
|
||||
case FullscreenAction.openMap:
|
||||
return 'Show on map…';
|
||||
case FullscreenAction.debug:
|
||||
return 'Debug';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
IconData getIcon() {
|
||||
switch (this) {
|
||||
// in app actions
|
||||
case FullscreenAction.toggleFavourite:
|
||||
// different data depending on toggle state
|
||||
return null;
|
||||
case FullscreenAction.delete:
|
||||
return AIcons.delete;
|
||||
case FullscreenAction.info:
|
||||
return OMIcons.info;
|
||||
case FullscreenAction.rename:
|
||||
return OMIcons.title;
|
||||
case FullscreenAction.rotateCCW:
|
||||
return AIcons.rotateLeft;
|
||||
case FullscreenAction.rotateCW:
|
||||
return AIcons.rotateRight;
|
||||
case FullscreenAction.print:
|
||||
return AIcons.print;
|
||||
case FullscreenAction.share:
|
||||
return AIcons.share;
|
||||
// external app actions
|
||||
case FullscreenAction.edit:
|
||||
case FullscreenAction.open:
|
||||
case FullscreenAction.setAs:
|
||||
case FullscreenAction.openMap:
|
||||
return null;
|
||||
case FullscreenAction.debug:
|
||||
return OMIcons.whatshot;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ import 'package:aves/utils/constants.dart';
|
|||
import 'package:aves/widgets/album/collection_page.dart';
|
||||
import 'package:aves/widgets/common/image_providers/thumbnail_provider.dart';
|
||||
import 'package:aves/widgets/common/image_providers/uri_image_provider.dart';
|
||||
import 'package:aves/widgets/fullscreen/fullscreen_action_delegate.dart';
|
||||
import 'package:aves/widgets/common/entry_action_delegate.dart';
|
||||
import 'package:aves/widgets/fullscreen/image_page.dart';
|
||||
import 'package:aves/widgets/fullscreen/info/info_page.dart';
|
||||
import 'package:aves/widgets/fullscreen/overlay/bottom.dart';
|
||||
|
@ -49,7 +49,7 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
|
|||
Animation<double> _topOverlayScale, _bottomOverlayScale;
|
||||
Animation<Offset> _bottomOverlayOffset;
|
||||
EdgeInsets _frozenViewInsets, _frozenViewPadding;
|
||||
FullscreenActionDelegate _actionDelegate;
|
||||
EntryActionDelegate _actionDelegate;
|
||||
final List<Tuple2<String, IjkMediaController>> _videoControllers = [];
|
||||
|
||||
CollectionLens get collection => widget.collection;
|
||||
|
@ -93,7 +93,7 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
|
|||
curve: Curves.easeOutQuad,
|
||||
));
|
||||
_overlayVisible.addListener(_onOverlayVisibleChange);
|
||||
_actionDelegate = FullscreenActionDelegate(
|
||||
_actionDelegate = EntryActionDelegate(
|
||||
collection: collection,
|
||||
showInfo: () => _goToVerticalPage(infoPage),
|
||||
);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/widgets/common/entry_actions.dart';
|
||||
import 'package:aves/widgets/common/fx/sweeper.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:aves/widgets/common/menu_row.dart';
|
||||
import 'package:aves/widgets/fullscreen/fullscreen_actions.dart';
|
||||
import 'package:aves/widgets/fullscreen/overlay/common.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -15,7 +15,7 @@ class FullscreenTopOverlay extends StatelessWidget {
|
|||
final int index;
|
||||
final Animation<double> scale;
|
||||
final EdgeInsets viewInsets, viewPadding;
|
||||
final Function(FullscreenAction value) onActionSelected;
|
||||
final Function(EntryAction value) onActionSelected;
|
||||
final bool canToggleFavourite;
|
||||
|
||||
ImageEntry get entry => entries[index];
|
||||
|
@ -53,12 +53,12 @@ class FullscreenTopOverlay extends StatelessWidget {
|
|||
final quickActionCount = min(targetCount, availableCount);
|
||||
|
||||
final quickActions = [
|
||||
FullscreenAction.toggleFavourite,
|
||||
FullscreenAction.share,
|
||||
FullscreenAction.delete,
|
||||
EntryAction.toggleFavourite,
|
||||
EntryAction.share,
|
||||
EntryAction.delete,
|
||||
].where(_canDo).take(quickActionCount);
|
||||
final inAppActions = FullscreenActions.inApp.where((action) => !quickActions.contains(action)).where(_canDo);
|
||||
final externalAppActions = FullscreenActions.externalApp.where(_canDo);
|
||||
final inAppActions = EntryActions.inApp.where((action) => !quickActions.contains(action)).where(_canDo);
|
||||
final externalAppActions = EntryActions.externalApp.where(_canDo);
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
|
@ -70,14 +70,14 @@ class FullscreenTopOverlay extends StatelessWidget {
|
|||
...quickActions.map(_buildOverlayButton),
|
||||
OverlayButton(
|
||||
scale: scale,
|
||||
child: PopupMenuButton<FullscreenAction>(
|
||||
child: PopupMenuButton<EntryAction>(
|
||||
itemBuilder: (context) => [
|
||||
...inAppActions.map(_buildPopupMenuItem),
|
||||
const PopupMenuDivider(),
|
||||
...externalAppActions.map(_buildPopupMenuItem),
|
||||
if (kDebugMode) ...[
|
||||
const PopupMenuDivider(),
|
||||
_buildPopupMenuItem(FullscreenAction.debug),
|
||||
_buildPopupMenuItem(EntryAction.debug),
|
||||
]
|
||||
],
|
||||
onSelected: onActionSelected,
|
||||
|
@ -93,37 +93,37 @@ class FullscreenTopOverlay extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
bool _canDo(FullscreenAction action) {
|
||||
bool _canDo(EntryAction action) {
|
||||
switch (action) {
|
||||
case FullscreenAction.toggleFavourite:
|
||||
case EntryAction.toggleFavourite:
|
||||
return canToggleFavourite;
|
||||
case FullscreenAction.delete:
|
||||
case FullscreenAction.rename:
|
||||
case EntryAction.delete:
|
||||
case EntryAction.rename:
|
||||
return entry.canEdit;
|
||||
case FullscreenAction.rotateCCW:
|
||||
case FullscreenAction.rotateCW:
|
||||
case EntryAction.rotateCCW:
|
||||
case EntryAction.rotateCW:
|
||||
return entry.canRotate;
|
||||
case FullscreenAction.print:
|
||||
case EntryAction.print:
|
||||
return entry.canPrint;
|
||||
case FullscreenAction.openMap:
|
||||
case EntryAction.openMap:
|
||||
return entry.hasGps;
|
||||
case FullscreenAction.share:
|
||||
case FullscreenAction.info:
|
||||
case FullscreenAction.open:
|
||||
case FullscreenAction.edit:
|
||||
case FullscreenAction.setAs:
|
||||
case EntryAction.share:
|
||||
case EntryAction.info:
|
||||
case EntryAction.open:
|
||||
case EntryAction.edit:
|
||||
case EntryAction.setAs:
|
||||
return true;
|
||||
case FullscreenAction.debug:
|
||||
case EntryAction.debug:
|
||||
return kDebugMode;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Widget _buildOverlayButton(FullscreenAction action) {
|
||||
Widget _buildOverlayButton(EntryAction action) {
|
||||
Widget child;
|
||||
final onPressed = () => onActionSelected?.call(action);
|
||||
switch (action) {
|
||||
case FullscreenAction.toggleFavourite:
|
||||
case EntryAction.toggleFavourite:
|
||||
child = ValueListenableBuilder<bool>(
|
||||
valueListenable: entry.isFavouriteNotifier,
|
||||
builder: (context, isFavourite, child) => Stack(
|
||||
|
@ -142,24 +142,24 @@ class FullscreenTopOverlay extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
break;
|
||||
case FullscreenAction.info:
|
||||
case FullscreenAction.share:
|
||||
case FullscreenAction.delete:
|
||||
case FullscreenAction.rename:
|
||||
case FullscreenAction.rotateCCW:
|
||||
case FullscreenAction.rotateCW:
|
||||
case FullscreenAction.print:
|
||||
case EntryAction.info:
|
||||
case EntryAction.share:
|
||||
case EntryAction.delete:
|
||||
case EntryAction.rename:
|
||||
case EntryAction.rotateCCW:
|
||||
case EntryAction.rotateCW:
|
||||
case EntryAction.print:
|
||||
child = IconButton(
|
||||
icon: Icon(action.getIcon()),
|
||||
onPressed: onPressed,
|
||||
tooltip: action.getText(),
|
||||
);
|
||||
break;
|
||||
case FullscreenAction.openMap:
|
||||
case FullscreenAction.open:
|
||||
case FullscreenAction.edit:
|
||||
case FullscreenAction.setAs:
|
||||
case FullscreenAction.debug:
|
||||
case EntryAction.openMap:
|
||||
case EntryAction.open:
|
||||
case EntryAction.edit:
|
||||
case EntryAction.setAs:
|
||||
case EntryAction.debug:
|
||||
break;
|
||||
}
|
||||
return child != null
|
||||
|
@ -173,11 +173,11 @@ class FullscreenTopOverlay extends StatelessWidget {
|
|||
: const SizedBox.shrink();
|
||||
}
|
||||
|
||||
PopupMenuEntry<FullscreenAction> _buildPopupMenuItem(FullscreenAction action) {
|
||||
PopupMenuEntry<EntryAction> _buildPopupMenuItem(EntryAction action) {
|
||||
Widget child;
|
||||
switch (action) {
|
||||
// in app actions
|
||||
case FullscreenAction.toggleFavourite:
|
||||
case EntryAction.toggleFavourite:
|
||||
child = entry.isFavouriteNotifier.value
|
||||
? const MenuRow(
|
||||
text: 'Remove from favourites',
|
||||
|
@ -188,21 +188,21 @@ class FullscreenTopOverlay extends StatelessWidget {
|
|||
icon: AIcons.favourite,
|
||||
);
|
||||
break;
|
||||
case FullscreenAction.info:
|
||||
case FullscreenAction.share:
|
||||
case FullscreenAction.delete:
|
||||
case FullscreenAction.rename:
|
||||
case FullscreenAction.rotateCCW:
|
||||
case FullscreenAction.rotateCW:
|
||||
case FullscreenAction.print:
|
||||
case FullscreenAction.debug:
|
||||
case EntryAction.info:
|
||||
case EntryAction.share:
|
||||
case EntryAction.delete:
|
||||
case EntryAction.rename:
|
||||
case EntryAction.rotateCCW:
|
||||
case EntryAction.rotateCW:
|
||||
case EntryAction.print:
|
||||
case EntryAction.debug:
|
||||
child = MenuRow(text: action.getText(), icon: action.getIcon());
|
||||
break;
|
||||
// external app actions
|
||||
case FullscreenAction.edit:
|
||||
case FullscreenAction.open:
|
||||
case FullscreenAction.setAs:
|
||||
case FullscreenAction.openMap:
|
||||
case EntryAction.edit:
|
||||
case EntryAction.open:
|
||||
case EntryAction.setAs:
|
||||
case EntryAction.openMap:
|
||||
child = Text(action.getText());
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue