collection/albums/countries/tags: selection map/stats
This commit is contained in:
parent
f8337f6e3d
commit
be8f2754db
12 changed files with 241 additions and 145 deletions
|
@ -2,6 +2,8 @@
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- Map & Stats from selection
|
||||||
|
|
||||||
## [v1.4.8] - 2021-08-08
|
## [v1.4.8] - 2021-08-08
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -321,6 +321,12 @@
|
||||||
"@menuActionSort": {},
|
"@menuActionSort": {},
|
||||||
"menuActionGroup": "Group",
|
"menuActionGroup": "Group",
|
||||||
"@menuActionGroup": {},
|
"@menuActionGroup": {},
|
||||||
|
"menuActionSelect": "Select",
|
||||||
|
"@menuActionSelect": {},
|
||||||
|
"menuActionSelectAll": "Select all",
|
||||||
|
"@menuActionSelectAll": {},
|
||||||
|
"menuActionSelectNone": "Select none",
|
||||||
|
"@menuActionSelectNone": {},
|
||||||
"menuActionMap": "Map",
|
"menuActionMap": "Map",
|
||||||
"@menuActionMap": {},
|
"@menuActionMap": {},
|
||||||
"menuActionStats": "Stats",
|
"menuActionStats": "Stats",
|
||||||
|
@ -376,12 +382,6 @@
|
||||||
|
|
||||||
"collectionActionAddShortcut": "Add shortcut",
|
"collectionActionAddShortcut": "Add shortcut",
|
||||||
"@collectionActionAddShortcut": {},
|
"@collectionActionAddShortcut": {},
|
||||||
"collectionActionSelect": "Select",
|
|
||||||
"@collectionActionSelect": {},
|
|
||||||
"collectionActionSelectAll": "Select all",
|
|
||||||
"@collectionActionSelectAll": {},
|
|
||||||
"collectionActionSelectNone": "Select none",
|
|
||||||
"@collectionActionSelectNone": {},
|
|
||||||
"collectionActionCopy": "Copy to album",
|
"collectionActionCopy": "Copy to album",
|
||||||
"@collectionActionCopy": {},
|
"@collectionActionCopy": {},
|
||||||
"collectionActionMove": "Move to album",
|
"collectionActionMove": "Move to album",
|
||||||
|
|
|
@ -147,6 +147,9 @@
|
||||||
|
|
||||||
"menuActionSort": "정렬",
|
"menuActionSort": "정렬",
|
||||||
"menuActionGroup": "묶음",
|
"menuActionGroup": "묶음",
|
||||||
|
"menuActionSelect": "선택",
|
||||||
|
"menuActionSelectAll": "모두 선택",
|
||||||
|
"menuActionSelectNone": "모두 해제",
|
||||||
"menuActionMap": "지도",
|
"menuActionMap": "지도",
|
||||||
"menuActionStats": "통계",
|
"menuActionStats": "통계",
|
||||||
|
|
||||||
|
@ -174,9 +177,6 @@
|
||||||
"collectionSelectionPageTitle": "{count, plural, =0{항목 선택} other{{count}개}}",
|
"collectionSelectionPageTitle": "{count, plural, =0{항목 선택} other{{count}개}}",
|
||||||
|
|
||||||
"collectionActionAddShortcut": "홈 화면에 추가",
|
"collectionActionAddShortcut": "홈 화면에 추가",
|
||||||
"collectionActionSelect": "선택",
|
|
||||||
"collectionActionSelectAll": "모두 선택",
|
|
||||||
"collectionActionSelectNone": "모두 해제",
|
|
||||||
"collectionActionCopy": "앨범으로 복사",
|
"collectionActionCopy": "앨범으로 복사",
|
||||||
"collectionActionMove": "앨범으로 이동",
|
"collectionActionMove": "앨범으로 이동",
|
||||||
"collectionActionRefreshMetadata": "새로 분석",
|
"collectionActionRefreshMetadata": "새로 분석",
|
||||||
|
|
|
@ -6,18 +6,19 @@ enum ChipSetAction {
|
||||||
// general
|
// general
|
||||||
sort,
|
sort,
|
||||||
group,
|
group,
|
||||||
map,
|
|
||||||
select,
|
select,
|
||||||
selectAll,
|
selectAll,
|
||||||
selectNone,
|
selectNone,
|
||||||
stats,
|
|
||||||
createAlbum,
|
createAlbum,
|
||||||
// single/multiple filters
|
// all or filter selection
|
||||||
|
map,
|
||||||
|
stats,
|
||||||
|
// single/multiple filter selection
|
||||||
delete,
|
delete,
|
||||||
hide,
|
hide,
|
||||||
pin,
|
pin,
|
||||||
unpin,
|
unpin,
|
||||||
// single filter
|
// single filter selection
|
||||||
rename,
|
rename,
|
||||||
setCover,
|
setCover,
|
||||||
}
|
}
|
||||||
|
@ -31,11 +32,11 @@ extension ExtraChipSetAction on ChipSetAction {
|
||||||
case ChipSetAction.group:
|
case ChipSetAction.group:
|
||||||
return context.l10n.menuActionGroup;
|
return context.l10n.menuActionGroup;
|
||||||
case ChipSetAction.select:
|
case ChipSetAction.select:
|
||||||
return context.l10n.collectionActionSelect;
|
return context.l10n.menuActionSelect;
|
||||||
case ChipSetAction.selectAll:
|
case ChipSetAction.selectAll:
|
||||||
return context.l10n.collectionActionSelectAll;
|
return context.l10n.menuActionSelectAll;
|
||||||
case ChipSetAction.selectNone:
|
case ChipSetAction.selectNone:
|
||||||
return context.l10n.collectionActionSelectNone;
|
return context.l10n.menuActionSelectNone;
|
||||||
case ChipSetAction.map:
|
case ChipSetAction.map:
|
||||||
return context.l10n.menuActionMap;
|
return context.l10n.menuActionMap;
|
||||||
case ChipSetAction.stats:
|
case ChipSetAction.stats:
|
||||||
|
@ -69,8 +70,9 @@ extension ExtraChipSetAction on ChipSetAction {
|
||||||
case ChipSetAction.select:
|
case ChipSetAction.select:
|
||||||
return AIcons.select;
|
return AIcons.select;
|
||||||
case ChipSetAction.selectAll:
|
case ChipSetAction.selectAll:
|
||||||
|
return AIcons.selected;
|
||||||
case ChipSetAction.selectNone:
|
case ChipSetAction.selectNone:
|
||||||
return null;
|
return AIcons.unselected;
|
||||||
case ChipSetAction.map:
|
case ChipSetAction.map:
|
||||||
return AIcons.map;
|
return AIcons.map;
|
||||||
case ChipSetAction.stats:
|
case ChipSetAction.stats:
|
||||||
|
|
|
@ -1,14 +1,85 @@
|
||||||
|
import 'package:aves/theme/icons.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
enum CollectionAction {
|
enum CollectionAction {
|
||||||
addShortcut,
|
// general
|
||||||
sort,
|
sort,
|
||||||
group,
|
group,
|
||||||
select,
|
select,
|
||||||
selectAll,
|
selectAll,
|
||||||
selectNone,
|
selectNone,
|
||||||
|
// all
|
||||||
|
addShortcut,
|
||||||
|
// all or entry selection
|
||||||
map,
|
map,
|
||||||
stats,
|
stats,
|
||||||
// apply to entry set
|
// entry selection
|
||||||
copy,
|
copy,
|
||||||
move,
|
move,
|
||||||
refreshMetadata,
|
refreshMetadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ExtraCollectionAction on CollectionAction {
|
||||||
|
String getText(BuildContext context) {
|
||||||
|
switch (this) {
|
||||||
|
// general
|
||||||
|
case CollectionAction.sort:
|
||||||
|
return context.l10n.menuActionSort;
|
||||||
|
case CollectionAction.group:
|
||||||
|
return context.l10n.menuActionGroup;
|
||||||
|
case CollectionAction.select:
|
||||||
|
return context.l10n.menuActionSelect;
|
||||||
|
case CollectionAction.selectAll:
|
||||||
|
return context.l10n.menuActionSelectAll;
|
||||||
|
case CollectionAction.selectNone:
|
||||||
|
return context.l10n.menuActionSelectNone;
|
||||||
|
// all
|
||||||
|
case CollectionAction.addShortcut:
|
||||||
|
return context.l10n.collectionActionAddShortcut;
|
||||||
|
// all or entry selection
|
||||||
|
case CollectionAction.map:
|
||||||
|
return context.l10n.menuActionMap;
|
||||||
|
case CollectionAction.stats:
|
||||||
|
return context.l10n.menuActionStats;
|
||||||
|
// entry selection
|
||||||
|
case CollectionAction.copy:
|
||||||
|
return context.l10n.collectionActionCopy;
|
||||||
|
case CollectionAction.move:
|
||||||
|
return context.l10n.collectionActionMove;
|
||||||
|
case CollectionAction.refreshMetadata:
|
||||||
|
return context.l10n.collectionActionRefreshMetadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconData? getIcon() {
|
||||||
|
switch (this) {
|
||||||
|
// general
|
||||||
|
case CollectionAction.sort:
|
||||||
|
return AIcons.sort;
|
||||||
|
case CollectionAction.group:
|
||||||
|
return AIcons.group;
|
||||||
|
case CollectionAction.select:
|
||||||
|
return AIcons.select;
|
||||||
|
case CollectionAction.selectAll:
|
||||||
|
return AIcons.selected;
|
||||||
|
case CollectionAction.selectNone:
|
||||||
|
return AIcons.unselected;
|
||||||
|
// all
|
||||||
|
case CollectionAction.addShortcut:
|
||||||
|
return AIcons.addShortcut;
|
||||||
|
// all or entry selection
|
||||||
|
case CollectionAction.map:
|
||||||
|
return AIcons.map;
|
||||||
|
case CollectionAction.stats:
|
||||||
|
return AIcons.stats;
|
||||||
|
// entry selection
|
||||||
|
case CollectionAction.copy:
|
||||||
|
return AIcons.copy;
|
||||||
|
case CollectionAction.move:
|
||||||
|
return AIcons.move;
|
||||||
|
case CollectionAction.refreshMetadata:
|
||||||
|
return AIcons.refresh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ class AIcons {
|
||||||
static const IconData captureFrame = Icons.screenshot_outlined;
|
static const IconData captureFrame = Icons.screenshot_outlined;
|
||||||
static const IconData clear = Icons.clear_outlined;
|
static const IconData clear = Icons.clear_outlined;
|
||||||
static const IconData clipboard = Icons.content_copy_outlined;
|
static const IconData clipboard = Icons.content_copy_outlined;
|
||||||
|
static const IconData copy = Icons.file_copy_outlined;
|
||||||
static const IconData createAlbum = Icons.add_circle_outline;
|
static const IconData createAlbum = Icons.add_circle_outline;
|
||||||
static const IconData debug = Icons.whatshot_outlined;
|
static const IconData debug = Icons.whatshot_outlined;
|
||||||
static const IconData delete = Icons.delete_outlined;
|
static const IconData delete = Icons.delete_outlined;
|
||||||
|
@ -51,6 +52,7 @@ class AIcons {
|
||||||
static const IconData info = Icons.info_outlined;
|
static const IconData info = Icons.info_outlined;
|
||||||
static const IconData layers = Icons.layers_outlined;
|
static const IconData layers = Icons.layers_outlined;
|
||||||
static const IconData map = Icons.map_outlined;
|
static const IconData map = Icons.map_outlined;
|
||||||
|
static const IconData move = MdiIcons.fileMoveOutline;
|
||||||
static const IconData newTier = Icons.fiber_new_outlined;
|
static const IconData newTier = Icons.fiber_new_outlined;
|
||||||
static const IconData openOutside = Icons.open_in_new_outlined;
|
static const IconData openOutside = Icons.open_in_new_outlined;
|
||||||
static const IconData pin = Icons.push_pin_outlined;
|
static const IconData pin = Icons.push_pin_outlined;
|
||||||
|
@ -58,6 +60,7 @@ class AIcons {
|
||||||
static const IconData play = Icons.play_arrow;
|
static const IconData play = Icons.play_arrow;
|
||||||
static const IconData pause = Icons.pause;
|
static const IconData pause = Icons.pause;
|
||||||
static const IconData print = Icons.print_outlined;
|
static const IconData print = Icons.print_outlined;
|
||||||
|
static const IconData refresh = Icons.refresh_outlined;
|
||||||
static const IconData rename = Icons.title_outlined;
|
static const IconData rename = Icons.title_outlined;
|
||||||
static const IconData rotateLeft = Icons.rotate_left_outlined;
|
static const IconData rotateLeft = Icons.rotate_left_outlined;
|
||||||
static const IconData rotateRight = Icons.rotate_right_outlined;
|
static const IconData rotateRight = Icons.rotate_right_outlined;
|
||||||
|
|
|
@ -12,7 +12,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/services/app_shortcut_service.dart';
|
import 'package:aves/services/app_shortcut_service.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/theme/icons.dart';
|
|
||||||
import 'package:aves/utils/pedantic.dart';
|
import 'package:aves/utils/pedantic.dart';
|
||||||
import 'package:aves/widgets/collection/entry_set_action_delegate.dart';
|
import 'package:aves/widgets/collection/entry_set_action_delegate.dart';
|
||||||
import 'package:aves/widgets/collection/filter_bar.dart';
|
import 'package:aves/widgets/collection/filter_bar.dart';
|
||||||
|
@ -22,10 +21,8 @@ import 'package:aves/widgets/common/basic/menu_row.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/dialogs/add_shortcut_dialog.dart';
|
import 'package:aves/widgets/dialogs/add_shortcut_dialog.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
||||||
import 'package:aves/widgets/map/map_page.dart';
|
|
||||||
import 'package:aves/widgets/search/search_button.dart';
|
import 'package:aves/widgets/search/search_button.dart';
|
||||||
import 'package:aves/widgets/search/search_delegate.dart';
|
import 'package:aves/widgets/search/search_delegate.dart';
|
||||||
import 'package:aves/widgets/stats/stats_page.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
|
@ -192,70 +189,55 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
return PopupMenuButton<CollectionAction>(
|
return PopupMenuButton<CollectionAction>(
|
||||||
key: const Key('appbar-menu-button'),
|
key: const Key('appbar-menu-button'),
|
||||||
itemBuilder: (context) {
|
itemBuilder: (context) {
|
||||||
|
final groupable = collection.sortFactor == EntrySortFactor.date;
|
||||||
final selection = context.read<Selection<AvesEntry>>();
|
final selection = context.read<Selection<AvesEntry>>();
|
||||||
final isNotEmpty = !collection.isEmpty;
|
final isSelecting = selection.isSelecting;
|
||||||
final hasSelection = selection.selection.isNotEmpty;
|
final selectedItems = selection.selection;
|
||||||
|
final hasSelection = selectedItems.isNotEmpty;
|
||||||
|
final hasItems = !collection.isEmpty;
|
||||||
|
final otherViewEnabled = (!isSelecting && hasItems) || (isSelecting && hasSelection);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
PopupMenuItem(
|
_toMenuItem(
|
||||||
|
CollectionAction.sort,
|
||||||
key: const Key('menu-sort'),
|
key: const Key('menu-sort'),
|
||||||
value: CollectionAction.sort,
|
|
||||||
child: MenuRow(text: context.l10n.menuActionSort, icon: AIcons.sort),
|
|
||||||
),
|
),
|
||||||
if (collection.sortFactor == EntrySortFactor.date)
|
if (groupable)
|
||||||
PopupMenuItem(
|
_toMenuItem(
|
||||||
|
CollectionAction.group,
|
||||||
key: const Key('menu-group'),
|
key: const Key('menu-group'),
|
||||||
value: CollectionAction.group,
|
|
||||||
child: MenuRow(text: context.l10n.menuActionGroup, icon: AIcons.group),
|
|
||||||
),
|
),
|
||||||
if (!selection.isSelecting && appMode == AppMode.main) ...[
|
if (appMode == AppMode.main) ...[
|
||||||
PopupMenuItem(
|
if (!isSelecting)
|
||||||
value: CollectionAction.select,
|
_toMenuItem(
|
||||||
enabled: isNotEmpty,
|
CollectionAction.select,
|
||||||
child: MenuRow(text: context.l10n.collectionActionSelect, icon: AIcons.select),
|
enabled: hasItems,
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
value: CollectionAction.map,
|
|
||||||
enabled: isNotEmpty,
|
|
||||||
child: MenuRow(text: context.l10n.menuActionMap, icon: AIcons.map),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
value: CollectionAction.stats,
|
|
||||||
enabled: isNotEmpty,
|
|
||||||
child: MenuRow(text: context.l10n.menuActionStats, icon: AIcons.stats),
|
|
||||||
),
|
|
||||||
if (canAddShortcuts)
|
|
||||||
PopupMenuItem(
|
|
||||||
value: CollectionAction.addShortcut,
|
|
||||||
child: MenuRow(text: context.l10n.collectionActionAddShortcut, icon: AIcons.addShortcut),
|
|
||||||
),
|
),
|
||||||
|
const PopupMenuDivider(),
|
||||||
|
if (isSelecting)
|
||||||
|
...[
|
||||||
|
CollectionAction.copy,
|
||||||
|
CollectionAction.move,
|
||||||
|
CollectionAction.refreshMetadata,
|
||||||
|
].map((v) => _toMenuItem(v, enabled: hasSelection)),
|
||||||
|
...[
|
||||||
|
CollectionAction.map,
|
||||||
|
CollectionAction.stats,
|
||||||
|
].map((v) => _toMenuItem(v, enabled: otherViewEnabled)),
|
||||||
|
if (!isSelecting && canAddShortcuts) ...[
|
||||||
|
const PopupMenuDivider(),
|
||||||
|
_toMenuItem(CollectionAction.addShortcut),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
if (selection.isSelecting) ...[
|
if (isSelecting) ...[
|
||||||
const PopupMenuDivider(),
|
const PopupMenuDivider(),
|
||||||
PopupMenuItem(
|
_toMenuItem(
|
||||||
value: CollectionAction.copy,
|
CollectionAction.selectAll,
|
||||||
enabled: hasSelection,
|
enabled: selectedItems.length < collection.entryCount,
|
||||||
child: MenuRow(text: context.l10n.collectionActionCopy),
|
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
_toMenuItem(
|
||||||
value: CollectionAction.move,
|
CollectionAction.selectNone,
|
||||||
enabled: hasSelection,
|
enabled: hasSelection,
|
||||||
child: MenuRow(text: context.l10n.collectionActionMove),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
value: CollectionAction.refreshMetadata,
|
|
||||||
enabled: hasSelection,
|
|
||||||
child: MenuRow(text: context.l10n.collectionActionRefreshMetadata),
|
|
||||||
),
|
|
||||||
const PopupMenuDivider(),
|
|
||||||
PopupMenuItem(
|
|
||||||
value: CollectionAction.selectAll,
|
|
||||||
enabled: selection.selection.length < collection.entryCount,
|
|
||||||
child: MenuRow(text: context.l10n.collectionActionSelectAll),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
value: CollectionAction.selectNone,
|
|
||||||
enabled: hasSelection,
|
|
||||||
child: MenuRow(text: context.l10n.collectionActionSelectNone),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
@ -270,6 +252,18 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PopupMenuItem<CollectionAction> _toMenuItem(CollectionAction action, {Key? key, bool enabled = true}) {
|
||||||
|
return PopupMenuItem(
|
||||||
|
key: key,
|
||||||
|
value: action,
|
||||||
|
enabled: enabled,
|
||||||
|
child: MenuRow(
|
||||||
|
text: action.getText(context),
|
||||||
|
icon: action.getIcon(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _onActivityChange() {
|
void _onActivityChange() {
|
||||||
if (context.read<Selection<AvesEntry>>().isSelecting) {
|
if (context.read<Selection<AvesEntry>>().isSelecting) {
|
||||||
_browseToSelectAnimation.forward();
|
_browseToSelectAnimation.forward();
|
||||||
|
@ -287,6 +281,8 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
case CollectionAction.copy:
|
case CollectionAction.copy:
|
||||||
case CollectionAction.move:
|
case CollectionAction.move:
|
||||||
case CollectionAction.refreshMetadata:
|
case CollectionAction.refreshMetadata:
|
||||||
|
case CollectionAction.map:
|
||||||
|
case CollectionAction.stats:
|
||||||
_actionDelegate.onCollectionActionSelected(context, action);
|
_actionDelegate.onCollectionActionSelected(context, action);
|
||||||
break;
|
break;
|
||||||
case CollectionAction.select:
|
case CollectionAction.select:
|
||||||
|
@ -298,12 +294,6 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
case CollectionAction.selectNone:
|
case CollectionAction.selectNone:
|
||||||
context.read<Selection<AvesEntry>>().clearSelection();
|
context.read<Selection<AvesEntry>>().clearSelection();
|
||||||
break;
|
break;
|
||||||
case CollectionAction.map:
|
|
||||||
_goToMap();
|
|
||||||
break;
|
|
||||||
case CollectionAction.stats:
|
|
||||||
_goToStats();
|
|
||||||
break;
|
|
||||||
case CollectionAction.addShortcut:
|
case CollectionAction.addShortcut:
|
||||||
unawaited(_showShortcutDialog(context));
|
unawaited(_showShortcutDialog(context));
|
||||||
break;
|
break;
|
||||||
|
@ -385,30 +375,4 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _goToMap() {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
settings: const RouteSettings(name: MapPage.routeName),
|
|
||||||
builder: (context) => MapPage(
|
|
||||||
source: source,
|
|
||||||
parentCollection: collection,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _goToStats() {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
settings: const RouteSettings(name: StatsPage.routeName),
|
|
||||||
builder: (context) => StatsPage(
|
|
||||||
source: source,
|
|
||||||
parentCollection: collection,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ import 'package:aves/widgets/common/action_mixins/size_aware.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
||||||
import 'package:aves/widgets/filter_grids/album_pick.dart';
|
import 'package:aves/widgets/filter_grids/album_pick.dart';
|
||||||
|
import 'package:aves/widgets/map/map_page.dart';
|
||||||
|
import 'package:aves/widgets/stats/stats_page.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
@ -52,6 +54,12 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
||||||
case CollectionAction.refreshMetadata:
|
case CollectionAction.refreshMetadata:
|
||||||
_refreshMetadata(context);
|
_refreshMetadata(context);
|
||||||
break;
|
break;
|
||||||
|
case CollectionAction.map:
|
||||||
|
_goToMap(context);
|
||||||
|
break;
|
||||||
|
case CollectionAction.stats:
|
||||||
|
_goToStats(context);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -242,4 +250,37 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _goToMap(BuildContext context) {
|
||||||
|
final selection = context.read<Selection<AvesEntry>>();
|
||||||
|
final entries = selection.isSelecting ? _getExpandedSelectedItems(selection) : context.read<CollectionLens>().sortedEntries;
|
||||||
|
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
settings: const RouteSettings(name: MapPage.routeName),
|
||||||
|
builder: (context) => MapPage(
|
||||||
|
entries: entries.where((entry) => entry.hasGps).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _goToStats(BuildContext context) {
|
||||||
|
final selection = context.read<Selection<AvesEntry>>();
|
||||||
|
final collection = context.read<CollectionLens>();
|
||||||
|
final entries = selection.isSelecting ? _getExpandedSelectedItems(selection) : collection.sortedEntries.toSet();
|
||||||
|
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
settings: const RouteSettings(name: StatsPage.routeName),
|
||||||
|
builder: (context) => StatsPage(
|
||||||
|
entries: entries,
|
||||||
|
source: collection.source,
|
||||||
|
parentCollection: collection,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,10 +76,10 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
|
||||||
_showSortDialog(context);
|
_showSortDialog(context);
|
||||||
break;
|
break;
|
||||||
case ChipSetAction.map:
|
case ChipSetAction.map:
|
||||||
_goToMap(context);
|
_goToMap(context, filters);
|
||||||
break;
|
break;
|
||||||
case ChipSetAction.stats:
|
case ChipSetAction.stats:
|
||||||
_goToStats(context);
|
_goToStats(context, filters);
|
||||||
break;
|
break;
|
||||||
case ChipSetAction.select:
|
case ChipSetAction.select:
|
||||||
context.read<Selection<FilterGridItem<T>>>().select();
|
context.read<Selection<FilterGridItem<T>>>().select();
|
||||||
|
@ -129,28 +129,33 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _goToMap(BuildContext context) {
|
void _goToMap(BuildContext context, Set<T> filters) {
|
||||||
final source = context.read<CollectionSource>();
|
final source = context.read<CollectionSource>();
|
||||||
|
final entries = filters.isEmpty ? source.visibleEntries : source.visibleEntries.where((entry) => filters.any((f) => f.test(entry)));
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
settings: const RouteSettings(name: MapPage.routeName),
|
settings: const RouteSettings(name: MapPage.routeName),
|
||||||
builder: (context) => MapPage(
|
builder: (context) => MapPage(
|
||||||
source: source,
|
entries: entries.where((entry) => entry.hasGps).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _goToStats(BuildContext context) {
|
void _goToStats(BuildContext context, Set<T> filters) {
|
||||||
final source = context.read<CollectionSource>();
|
final source = context.read<CollectionSource>();
|
||||||
|
final entries = filters.isEmpty ? source.visibleEntries : source.visibleEntries.where((entry) => filters.any((f) => f.test(entry)));
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
settings: const RouteSettings(name: StatsPage.routeName),
|
settings: const RouteSettings(name: StatsPage.routeName),
|
||||||
builder: (context) => StatsPage(
|
builder: (context) {
|
||||||
source: source,
|
return StatsPage(
|
||||||
),
|
entries: entries.toSet(),
|
||||||
|
source: source,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,19 +174,44 @@ class _FilterGridAppBarState<T extends CollectionFilter> extends State<FilterGri
|
||||||
PopupMenuButton<ChipSetAction>(
|
PopupMenuButton<ChipSetAction>(
|
||||||
key: const Key('appbar-menu-button'),
|
key: const Key('appbar-menu-button'),
|
||||||
itemBuilder: (context) {
|
itemBuilder: (context) {
|
||||||
|
final selectedItems = selection.selection;
|
||||||
|
final hasSelection = selectedItems.isNotEmpty;
|
||||||
|
final hasItems = !widget.isEmpty;
|
||||||
|
final otherViewEnabled = (!isSelecting && hasItems) || (isSelecting && hasSelection);
|
||||||
|
|
||||||
final menuItems = <PopupMenuEntry<ChipSetAction>>[
|
final menuItems = <PopupMenuEntry<ChipSetAction>>[
|
||||||
toMenuItem(ChipSetAction.sort),
|
toMenuItem(ChipSetAction.sort),
|
||||||
if (widget.groupable) toMenuItem(ChipSetAction.group),
|
if (widget.groupable) toMenuItem(ChipSetAction.group),
|
||||||
|
if (appMode == AppMode.main && !isSelecting)
|
||||||
|
toMenuItem(
|
||||||
|
ChipSetAction.select,
|
||||||
|
enabled: hasItems,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (isSelecting) {
|
if (appMode == AppMode.main) {
|
||||||
final selectedItems = selection.selection;
|
menuItems.add(const PopupMenuDivider());
|
||||||
|
if (isSelecting) {
|
||||||
if (selectionRowActions.isNotEmpty) {
|
|
||||||
menuItems.add(const PopupMenuDivider());
|
|
||||||
menuItems.addAll(selectionRowActions.map(toMenuItem));
|
menuItems.addAll(selectionRowActions.map(toMenuItem));
|
||||||
}
|
}
|
||||||
|
menuItems.addAll([
|
||||||
|
toMenuItem(
|
||||||
|
ChipSetAction.map,
|
||||||
|
enabled: otherViewEnabled,
|
||||||
|
),
|
||||||
|
toMenuItem(
|
||||||
|
ChipSetAction.stats,
|
||||||
|
enabled: otherViewEnabled,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
if (!isSelecting) {
|
||||||
|
menuItems.addAll([
|
||||||
|
const PopupMenuDivider(),
|
||||||
|
toMenuItem(ChipSetAction.createAlbum),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isSelecting) {
|
||||||
menuItems.addAll([
|
menuItems.addAll([
|
||||||
const PopupMenuDivider(),
|
const PopupMenuDivider(),
|
||||||
toMenuItem(
|
toMenuItem(
|
||||||
|
@ -195,19 +220,9 @@ class _FilterGridAppBarState<T extends CollectionFilter> extends State<FilterGri
|
||||||
),
|
),
|
||||||
toMenuItem(
|
toMenuItem(
|
||||||
ChipSetAction.selectNone,
|
ChipSetAction.selectNone,
|
||||||
enabled: selectedItems.isNotEmpty,
|
enabled: hasSelection,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
} else if (appMode == AppMode.main) {
|
|
||||||
menuItems.addAll([
|
|
||||||
toMenuItem(
|
|
||||||
ChipSetAction.select,
|
|
||||||
enabled: !widget.isEmpty,
|
|
||||||
),
|
|
||||||
toMenuItem(ChipSetAction.map),
|
|
||||||
toMenuItem(ChipSetAction.stats),
|
|
||||||
toMenuItem(ChipSetAction.createAlbum),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return menuItems;
|
return menuItems;
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/settings/map_style.dart';
|
import 'package:aves/model/settings/map_style.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_source.dart';
|
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/map/geo_map.dart';
|
import 'package:aves/widgets/common/map/geo_map.dart';
|
||||||
|
@ -14,17 +12,12 @@ import 'package:flutter/scheduler.dart';
|
||||||
class MapPage extends StatefulWidget {
|
class MapPage extends StatefulWidget {
|
||||||
static const routeName = '/collection/map';
|
static const routeName = '/collection/map';
|
||||||
|
|
||||||
final CollectionSource source;
|
final List<AvesEntry> entries;
|
||||||
final CollectionLens? parentCollection;
|
|
||||||
late final List<AvesEntry> entries;
|
|
||||||
|
|
||||||
MapPage({
|
const MapPage({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.source,
|
required this.entries,
|
||||||
this.parentCollection,
|
}) : super(key: key);
|
||||||
}) : super(key: key) {
|
|
||||||
entries = (parentCollection?.sortedEntries.expand((entry) => entry.burstEntries ?? {entry}).toSet() ?? source.visibleEntries).where((entry) => entry.hasGps).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MapPageState createState() => _MapPageState();
|
_MapPageState createState() => _MapPageState();
|
||||||
|
|
|
@ -28,17 +28,17 @@ class StatsPage extends StatelessWidget {
|
||||||
|
|
||||||
final CollectionSource source;
|
final CollectionSource source;
|
||||||
final CollectionLens? parentCollection;
|
final CollectionLens? parentCollection;
|
||||||
late final Set<AvesEntry> entries;
|
final Set<AvesEntry> entries;
|
||||||
final Map<String, int> entryCountPerCountry = {}, entryCountPerPlace = {}, entryCountPerTag = {};
|
final Map<String, int> entryCountPerCountry = {}, entryCountPerPlace = {}, entryCountPerTag = {};
|
||||||
|
|
||||||
static const mimeDonutMinWidth = 124.0;
|
static const mimeDonutMinWidth = 124.0;
|
||||||
|
|
||||||
StatsPage({
|
StatsPage({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
required this.entries,
|
||||||
required this.source,
|
required this.source,
|
||||||
this.parentCollection,
|
this.parentCollection,
|
||||||
}) : super(key: key) {
|
}) : super(key: key) {
|
||||||
entries = parentCollection?.sortedEntries.expand((entry) => entry.burstEntries ?? {entry}).toSet() ?? source.visibleEntries;
|
|
||||||
entries.forEach((entry) {
|
entries.forEach((entry) {
|
||||||
if (entry.hasAddress) {
|
if (entry.hasAddress) {
|
||||||
final address = entry.addressDetails!;
|
final address = entry.addressDetails!;
|
||||||
|
|
Loading…
Reference in a new issue