#62 collection: custom quick actions for entry selection
This commit is contained in:
parent
656821225b
commit
b09bd8c18c
12 changed files with 154 additions and 66 deletions
|
@ -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",
|
||||
|
|
|
@ -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": "자막",
|
||||
|
|
|
@ -32,11 +32,6 @@ enum EntryAction {
|
|||
}
|
||||
|
||||
class EntryActions {
|
||||
static const selection = [
|
||||
EntryAction.share,
|
||||
EntryAction.delete,
|
||||
];
|
||||
|
||||
static const inApp = [
|
||||
EntryAction.info,
|
||||
EntryAction.toggleFavourite,
|
||||
|
|
|
@ -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:
|
||||
|
|
41
lib/model/settings/defaults.dart
Normal file
41
lib/model/settings/defaults.dart
Normal 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,
|
||||
];
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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,
|
Loading…
Reference in a new issue