#62 collection: custom quick actions for entry selection

This commit is contained in:
Thibault Deckers 2021-09-02 17:11:14 +09:00
parent 656821225b
commit b09bd8c18c
12 changed files with 154 additions and 66 deletions

View file

@ -627,6 +627,13 @@
"settingsThumbnailShowVideoDuration": "Show video duration",
"@settingsThumbnailShowVideoDuration": {},
"settingsCollectionSelectionQuickActionsTile": "Quick actions for item selection",
"@settingsCollectionSelectionQuickActionsTile": {},
"settingsCollectionSelectionQuickActionEditorTitle": "Quick Actions",
"@settingsCollectionSelectionQuickActionEditorTitle": {},
"settingsCollectionSelectionQuickActionEditorBanner": "Touch and hold to move buttons and select which actions are displayed when selecting items.",
"@settingsCollectionSelectionQuickActionEditorBanner": {},
"settingsSectionViewer": "Viewer",
"@settingsSectionViewer": {},
"settingsImageBackground": "Image background",
@ -671,9 +678,9 @@
"@settingsVideoLoopModeTile": {},
"settingsVideoLoopModeTitle": "Loop Mode",
"@settingsVideoLoopModeTitle": {},
"settingsVideoQuickActionsTile": "Quick video actions",
"settingsVideoQuickActionsTile": "Quick actions for videos",
"@settingsVideoQuickActionsTile": {},
"settingsVideoQuickActionEditorTitle": "Quick Video Actions",
"settingsVideoQuickActionEditorTitle": "Quick Actions",
"@settingsVideoQuickActionEditorTitle": {},
"settingsSubtitleThemeTile": "Subtitles",

View file

@ -299,6 +299,10 @@
"settingsThumbnailShowRawIcon": "Raw 아이콘 표시",
"settingsThumbnailShowVideoDuration": "동영상 길이 표시",
"settingsCollectionSelectionQuickActionsTile": "항목 선택의 빠른 작업",
"settingsCollectionSelectionQuickActionEditorTitle": "빠른 작업",
"settingsCollectionSelectionQuickActionEditorBanner": "버튼을 길게 누른 후 이동하여 항목 선택할 때 표시될 버튼을 선택하세요.",
"settingsSectionViewer": "뷰어",
"settingsImageBackground": "사진 배경",
"settingsViewerShowMinimap": "미니맵 표시",
@ -322,8 +326,8 @@
"settingsVideoEnableAutoPlay": "자동 재생",
"settingsVideoLoopModeTile": "반복 모드",
"settingsVideoLoopModeTitle": "반복 모드",
"settingsVideoQuickActionsTile": "빠른 동영상 작업",
"settingsVideoQuickActionEditorTitle": "빠른 동영상 작업",
"settingsVideoQuickActionsTile": "동영상의 빠른 작업",
"settingsVideoQuickActionEditorTitle": "빠른 작업",
"settingsSubtitleThemeTile": "자막",
"settingsSubtitleThemeTitle": "자막",

View file

@ -32,11 +32,6 @@ enum EntryAction {
}
class EntryActions {
static const selection = [
EntryAction.share,
EntryAction.delete,
];
static const inApp = [
EntryAction.info,
EntryAction.toggleFavourite,

View file

@ -15,11 +15,25 @@ enum EntrySetAction {
map,
stats,
// entry selection
share,
delete,
copy,
move,
refreshMetadata,
}
class EntrySetActions {
static const selection = [
EntrySetAction.share,
EntrySetAction.delete,
EntrySetAction.copy,
EntrySetAction.move,
EntrySetAction.refreshMetadata,
EntrySetAction.map,
EntrySetAction.stats,
];
}
extension ExtraEntrySetAction on EntrySetAction {
String getText(BuildContext context) {
switch (this) {
@ -43,6 +57,10 @@ extension ExtraEntrySetAction on EntrySetAction {
case EntrySetAction.stats:
return context.l10n.menuActionStats;
// entry selection
case EntrySetAction.share:
return context.l10n.entryActionShare;
case EntrySetAction.delete:
return context.l10n.entryActionDelete;
case EntrySetAction.copy:
return context.l10n.collectionActionCopy;
case EntrySetAction.move:
@ -78,6 +96,10 @@ extension ExtraEntrySetAction on EntrySetAction {
case EntrySetAction.stats:
return AIcons.stats;
// entry selection
case EntrySetAction.share:
return AIcons.share;
case EntrySetAction.delete:
return AIcons.delete;
case EntrySetAction.copy:
return AIcons.copy;
case EntrySetAction.move:

View file

@ -0,0 +1,41 @@
import 'package:aves/model/actions/entry_actions.dart';
import 'package:aves/model/actions/entry_set_actions.dart';
import 'package:aves/model/actions/video_actions.dart';
import 'package:aves/model/filters/favourite.dart';
import 'package:aves/model/filters/mime.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/countries_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
class SettingsDefaults {
// drawer
static final drawerTypeBookmarks = [
null,
MimeFilter.video,
FavouriteFilter.instance,
];
static final drawerPageBookmarks = [
AlbumListPage.routeName,
CountryListPage.routeName,
TagListPage.routeName,
];
// collection
static const collectionSelectionQuickActions = [
EntrySetAction.share,
EntrySetAction.delete,
];
// viewer
static const viewerQuickActions = [
EntryAction.toggleFavourite,
EntryAction.share,
EntryAction.rotateScreen,
];
// video
static const videoQuickActions = [
VideoAction.replay10,
VideoAction.togglePlay,
];
}

View file

@ -2,10 +2,10 @@ import 'dart:convert';
import 'dart:math';
import 'package:aves/model/actions/entry_actions.dart';
import 'package:aves/model/actions/entry_set_actions.dart';
import 'package:aves/model/actions/video_actions.dart';
import 'package:aves/model/filters/favourite.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/mime.dart';
import 'package:aves/model/settings/defaults.dart';
import 'package:aves/model/settings/enums.dart';
import 'package:aves/model/settings/map_style.dart';
import 'package:aves/model/settings/screen_on.dart';
@ -13,9 +13,6 @@ import 'package:aves/model/source/enums.dart';
import 'package:aves/services/device_service.dart';
import 'package:aves/services/services.dart';
import 'package:aves/utils/pedantic.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/countries_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:collection/collection.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
@ -59,6 +56,7 @@ class Settings extends ChangeNotifier {
// collection
static const collectionGroupFactorKey = 'collection_group_factor';
static const collectionSortFactorKey = 'collection_sort_factor';
static const collectionSelectionQuickActionsKey = 'collection_selection_quick_actions';
static const showThumbnailLocationKey = 'show_thumbnail_location';
static const showThumbnailRawKey = 'show_thumbnail_raw';
static const showThumbnailVideoDurationKey = 'show_thumbnail_video_duration';
@ -108,27 +106,6 @@ class Settings extends ChangeNotifier {
// version
static const lastVersionCheckDateKey = 'last_version_check_date';
// defaults
static final drawerTypeBookmarksDefault = [
null,
MimeFilter.video,
FavouriteFilter.instance,
];
static final drawerPageBookmarksDefault = [
AlbumListPage.routeName,
CountryListPage.routeName,
TagListPage.routeName,
];
static const viewerQuickActionsDefault = [
EntryAction.toggleFavourite,
EntryAction.share,
EntryAction.rotateScreen,
];
static const videoQuickActionsDefault = [
VideoAction.replay10,
VideoAction.togglePlay,
];
Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
_isRotationLocked = await windowService.isRotationLocked();
@ -235,7 +212,7 @@ class Settings extends ChangeNotifier {
if (v.isEmpty) return null;
return CollectionFilter.fromJson(v);
}).toList() ??
drawerTypeBookmarksDefault;
SettingsDefaults.drawerTypeBookmarks;
set drawerTypeBookmarks(List<CollectionFilter?> newValue) => setAndNotify(drawerTypeBookmarksKey, newValue.map((filter) => filter?.toJson() ?? '').toList());
@ -243,7 +220,7 @@ class Settings extends ChangeNotifier {
set drawerAlbumBookmarks(List<String>? newValue) => setAndNotify(drawerAlbumBookmarksKey, newValue);
List<String> get drawerPageBookmarks => _prefs!.getStringList(drawerPageBookmarksKey) ?? drawerPageBookmarksDefault;
List<String> get drawerPageBookmarks => _prefs!.getStringList(drawerPageBookmarksKey) ?? SettingsDefaults.drawerPageBookmarks;
set drawerPageBookmarks(List<String> newValue) => setAndNotify(drawerPageBookmarksKey, newValue);
@ -257,6 +234,10 @@ class Settings extends ChangeNotifier {
set collectionSortFactor(EntrySortFactor newValue) => setAndNotify(collectionSortFactorKey, newValue.toString());
List<EntrySetAction> get collectionSelectionQuickActions => getEnumListOrDefault(collectionSelectionQuickActionsKey, SettingsDefaults.collectionSelectionQuickActions, EntrySetAction.values);
set collectionSelectionQuickActions(List<EntrySetAction> newValue) => setAndNotify(collectionSelectionQuickActionsKey, newValue.map((v) => v.toString()).toList());
bool get showThumbnailLocation => getBoolOrDefault(showThumbnailLocationKey, true);
set showThumbnailLocation(bool newValue) => setAndNotify(showThumbnailLocationKey, newValue);
@ -297,7 +278,7 @@ class Settings extends ChangeNotifier {
// viewer
List<EntryAction> get viewerQuickActions => getEnumListOrDefault(viewerQuickActionsKey, viewerQuickActionsDefault, EntryAction.values);
List<EntryAction> get viewerQuickActions => getEnumListOrDefault(viewerQuickActionsKey, SettingsDefaults.viewerQuickActions, EntryAction.values);
set viewerQuickActions(List<EntryAction> newValue) => setAndNotify(viewerQuickActionsKey, newValue.map((v) => v.toString()).toList());
@ -323,7 +304,7 @@ class Settings extends ChangeNotifier {
// video
List<VideoAction> get videoQuickActions => getEnumListOrDefault(videoQuickActionsKey, videoQuickActionsDefault, VideoAction.values);
List<VideoAction> get videoQuickActions => getEnumListOrDefault(videoQuickActionsKey, SettingsDefaults.videoQuickActions, VideoAction.values);
set videoQuickActions(List<VideoAction> newValue) => setAndNotify(videoQuickActionsKey, newValue.map((v) => v.toString()).toList());
@ -556,6 +537,7 @@ class Settings extends ChangeNotifier {
case drawerPageBookmarksKey:
case pinnedFiltersKey:
case hiddenFiltersKey:
case collectionSelectionQuickActionsKey:
case viewerQuickActionsKey:
case videoQuickActionsKey:
if (value is List) {

View file

@ -1,7 +1,6 @@
import 'dart:async';
import 'package:aves/app_mode.dart';
import 'package:aves/model/actions/entry_actions.dart';
import 'package:aves/model/actions/entry_set_actions.dart';
import 'package:aves/model/entry.dart';
import 'package:aves/model/filters/filters.dart';
@ -168,6 +167,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
List<Widget> _buildActions(bool isSelecting) {
final appMode = context.watch<ValueNotifier<AppMode>>().value;
final selectionQuickActions = settings.collectionSelectionQuickActions;
return [
if (!isSelecting && appMode.canSearch)
CollectionSearchButton(
@ -175,11 +175,11 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
parentCollection: collection,
),
if (isSelecting)
...EntryActions.selection.map((action) => Selector<Selection<AvesEntry>, bool>(
...selectionQuickActions.map((action) => Selector<Selection<AvesEntry>, bool>(
selector: (context, selection) => selection.selectedItems.isEmpty,
builder: (context, isEmpty, child) => IconButton(
icon: action.getIcon() ?? const SizedBox(),
onPressed: isEmpty ? null : () => _actionDelegate.onEntryActionSelected(context, action),
icon: action.getIcon(),
onPressed: isEmpty ? null : () => _onCollectionActionSelected(action),
tooltip: action.getText(context),
),
)),
@ -219,16 +219,12 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
enabled: hasItems,
),
const PopupMenuDivider(),
if (isSelecting)
if (isSelecting) ...EntrySetActions.selection.where((v) => !selectionQuickActions.contains(v)).map((v) => _toMenuItem(v, enabled: hasSelection)),
if (!isSelecting)
...[
EntrySetAction.copy,
EntrySetAction.move,
EntrySetAction.refreshMetadata,
].map((v) => _toMenuItem(v, enabled: hasSelection)),
...[
EntrySetAction.map,
EntrySetAction.stats,
].map((v) => _toMenuItem(v, enabled: otherViewEnabled)),
EntrySetAction.map,
EntrySetAction.stats,
].map((v) => _toMenuItem(v, enabled: otherViewEnabled)),
if (!isSelecting && canAddShortcuts) ...[
const PopupMenuDivider(),
_toMenuItem(EntrySetAction.addShortcut),
@ -290,12 +286,14 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
Future<void> _onCollectionActionSelected(EntrySetAction action) async {
switch (action) {
case EntrySetAction.share:
case EntrySetAction.delete:
case EntrySetAction.copy:
case EntrySetAction.move:
case EntrySetAction.refreshMetadata:
case EntrySetAction.map:
case EntrySetAction.stats:
_actionDelegate.onCollectionActionSelected(context, action);
_actionDelegate.onActionSelected(context, action);
break;
case EntrySetAction.select:
context.read<Selection<AvesEntry>>().select();

View file

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:aves/model/actions/entry_actions.dart';
import 'package:aves/model/actions/entry_set_actions.dart';
import 'package:aves/model/actions/move_type.dart';
import 'package:aves/model/entry.dart';
@ -30,21 +29,14 @@ import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
void onEntryActionSelected(BuildContext context, EntryAction action) {
void onActionSelected(BuildContext context, EntrySetAction action) {
switch (action) {
case EntryAction.delete:
_showDeleteDialog(context);
break;
case EntryAction.share:
case EntrySetAction.share:
_share(context);
break;
default:
case EntrySetAction.delete:
_showDeleteDialog(context);
break;
}
}
void onCollectionActionSelected(BuildContext context, EntrySetAction action) {
switch (action) {
case EntrySetAction.copy:
_moveSelection(context, moveType: MoveType.copy);
break;

View file

@ -52,6 +52,7 @@ class DebugSettingsSection extends StatelessWidget {
'tileExtent - Countries': '${settings.getTileExtent(CountryListPage.routeName)}',
'tileExtent - Tags': '${settings.getTileExtent(TagListPage.routeName)}',
'infoMapZoom': '${settings.infoMapZoom}',
'collectionSelectionQuickActions': '${settings.collectionSelectionQuickActions}',
'viewerQuickActions': '${settings.viewerQuickActions}',
'videoQuickActions': '${settings.videoQuickActions}',
'drawerTypeBookmarks': toMultiline(settings.drawerTypeBookmarks),

View file

@ -14,7 +14,7 @@ import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/settings/language/language.dart';
import 'package:aves/widgets/settings/navigation/navigation.dart';
import 'package:aves/widgets/settings/privacy/privacy.dart';
import 'package:aves/widgets/settings/thumbnails.dart';
import 'package:aves/widgets/settings/thumbnails/thumbnails.dart';
import 'package:aves/widgets/settings/video/video.dart';
import 'package:aves/widgets/settings/viewer/viewer.dart';
import 'package:flutter/material.dart';

View file

@ -0,0 +1,44 @@
import 'package:aves/model/actions/entry_set_actions.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/settings/common/quick_actions/editor_page.dart';
import 'package:flutter/material.dart';
class SelectionActionsTile extends StatelessWidget {
const SelectionActionsTile({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(context.l10n.settingsCollectionSelectionQuickActionsTile),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
settings: const RouteSettings(name: SelectionActionEditorPage.routeName),
builder: (context) => const SelectionActionEditorPage(),
),
);
},
);
}
}
class SelectionActionEditorPage extends StatelessWidget {
static const routeName = '/settings/collection_selection_actions';
const SelectionActionEditorPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return QuickActionEditorPage<EntrySetAction>(
title: context.l10n.settingsCollectionSelectionQuickActionEditorTitle,
bannerText: context.l10n.settingsCollectionSelectionQuickActionEditorBanner,
allAvailableActions: EntrySetActions.selection,
actionIcon: (action) => action.getIcon(),
actionText: (context, action) => action.getText(context),
load: () => settings.collectionSelectionQuickActions.toList(),
save: (actions) => settings.collectionSelectionQuickActions = actions,
);
}
}

View file

@ -5,6 +5,7 @@ import 'package:aves/utils/color_utils.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
import 'package:aves/widgets/settings/common/tile_leading.dart';
import 'package:aves/widgets/settings/thumbnails/selection_actions_editor.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@ -34,6 +35,7 @@ class ThumbnailsSection extends StatelessWidget {
expandedNotifier: expandedNotifier,
showHighlight: false,
children: [
const SelectionActionsTile(),
SwitchListTile(
value: currentShowThumbnailLocation,
onChanged: (v) => settings.showThumbnailLocation = v,