diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3016c71ab..8141fef8a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
+### Added
+
+- Places: page & navigation entry
+
+### Fixed
+
+- replacing when moving item to vault
+- exporting item to vault
+
## [v1.8.1] - 2023-02-21
### Added
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index 7104192c1..d13036067 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -74,6 +74,7 @@
"chipActionDelete": "Delete",
"chipActionGoToAlbumPage": "Show in Albums",
"chipActionGoToCountryPage": "Show in Countries",
+ "chipActionGoToPlacePage": "Show in Places",
"chipActionGoToTagPage": "Show in Tags",
"chipActionFilterOut": "Filter out",
"chipActionFilterIn": "Filter in",
@@ -621,6 +622,7 @@
"drawerCollectionSphericalVideos": "360° Videos",
"drawerAlbumPage": "Albums",
"drawerCountryPage": "Countries",
+ "drawerPlacePage": "Places",
"drawerTagPage": "Tags",
"sortByDate": "By date",
@@ -665,6 +667,9 @@
"countryPageTitle": "Countries",
"countryEmpty": "No countries",
+ "placePageTitle": "Places",
+ "placeEmpty": "No places",
+
"tagPageTitle": "Tags",
"tagEmpty": "No tags",
diff --git a/lib/model/actions/chip_actions.dart b/lib/model/actions/chip_actions.dart
index bb7dc079a..f71b8c930 100644
--- a/lib/model/actions/chip_actions.dart
+++ b/lib/model/actions/chip_actions.dart
@@ -5,6 +5,7 @@ import 'package:flutter/widgets.dart';
enum ChipAction {
goToAlbumPage,
goToCountryPage,
+ goToPlacePage,
goToTagPage,
reverse,
hide,
@@ -18,6 +19,8 @@ extension ExtraChipAction on ChipAction {
return context.l10n.chipActionGoToAlbumPage;
case ChipAction.goToCountryPage:
return context.l10n.chipActionGoToCountryPage;
+ case ChipAction.goToPlacePage:
+ return context.l10n.chipActionGoToPlacePage;
case ChipAction.goToTagPage:
return context.l10n.chipActionGoToTagPage;
case ChipAction.reverse:
@@ -37,7 +40,9 @@ extension ExtraChipAction on ChipAction {
case ChipAction.goToAlbumPage:
return AIcons.album;
case ChipAction.goToCountryPage:
- return AIcons.location;
+ return AIcons.country;
+ case ChipAction.goToPlacePage:
+ return AIcons.place;
case ChipAction.goToTagPage:
return AIcons.tag;
case ChipAction.reverse:
diff --git a/lib/model/filters/location.dart b/lib/model/filters/location.dart
index 97289cfd8..aab90081e 100644
--- a/lib/model/filters/location.dart
+++ b/lib/model/filters/location.dart
@@ -51,6 +51,8 @@ class LocationFilter extends CoveredCollectionFilter {
String? get countryCode => _countryCode;
+ String get place => _location;
+
@override
EntryFilter get positiveTest => _test;
@@ -65,17 +67,25 @@ class LocationFilter extends CoveredCollectionFilter {
@override
Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) {
- if (_countryCode != null && device.canRenderFlagEmojis) {
- final flag = countryCodeToFlag(_countryCode);
- if (flag != null) {
- return Text(
- flag,
- style: TextStyle(fontSize: size),
- textScaleFactor: 1.0,
- );
- }
+ if (_location.isEmpty) {
+ return Icon(AIcons.locationUnlocated, size: size);
+ }
+ switch (level) {
+ case LocationLevel.place:
+ return Icon(AIcons.place, size: size);
+ case LocationLevel.country:
+ if (_countryCode != null && device.canRenderFlagEmojis) {
+ final flag = countryCodeToFlag(_countryCode);
+ if (flag != null) {
+ return Text(
+ flag,
+ style: TextStyle(fontSize: size),
+ textScaleFactor: 1.0,
+ );
+ }
+ }
+ return Icon(AIcons.country, size: size);
}
- return Icon(_location.isEmpty ? AIcons.locationUnlocated : AIcons.location, size: size);
}
@override
diff --git a/lib/model/filters/placeholder.dart b/lib/model/filters/placeholder.dart
index 933ac331e..e0be8e084 100644
--- a/lib/model/filters/placeholder.dart
+++ b/lib/model/filters/placeholder.dart
@@ -23,8 +23,10 @@ class PlaceholderFilter extends CollectionFilter {
PlaceholderFilter._private(this.placeholder) : super(reversed: false) {
switch (placeholder) {
case _country:
+ _icon = AIcons.country;
+ break;
case _place:
- _icon = AIcons.location;
+ _icon = AIcons.place;
break;
}
}
diff --git a/lib/model/settings/defaults.dart b/lib/model/settings/defaults.dart
index 13d04e8fd..72d9a972d 100644
--- a/lib/model/settings/defaults.dart
+++ b/lib/model/settings/defaults.dart
@@ -6,6 +6,7 @@ import 'package:aves/model/settings/enums/enums.dart';
import 'package:aves/model/source/enums/enums.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/places_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:flutter/material.dart';
@@ -40,6 +41,7 @@ class SettingsDefaults {
static const drawerPageBookmarks = [
AlbumListPage.routeName,
CountryListPage.routeName,
+ PlaceListPage.routeName,
TagListPage.routeName,
];
@@ -65,6 +67,7 @@ class SettingsDefaults {
static const albumGroupFactor = AlbumChipGroupFactor.importance;
static const albumSortFactor = ChipSortFactor.name;
static const countrySortFactor = ChipSortFactor.name;
+ static const placeSortFactor = ChipSortFactor.name;
static const tagSortFactor = ChipSortFactor.name;
// viewer
diff --git a/lib/model/settings/enums/thumbnail_overlay_location_icon.dart b/lib/model/settings/enums/thumbnail_overlay_location_icon.dart
index a0f76c54e..e61a0fd37 100644
--- a/lib/model/settings/enums/thumbnail_overlay_location_icon.dart
+++ b/lib/model/settings/enums/thumbnail_overlay_location_icon.dart
@@ -18,10 +18,9 @@ extension ExtraThumbnailOverlayLocationIcon on ThumbnailOverlayLocationIcon {
IconData getIcon(BuildContext context) {
switch (this) {
- case ThumbnailOverlayLocationIcon.located:
- return AIcons.location;
case ThumbnailOverlayLocationIcon.unlocated:
return AIcons.locationUnlocated;
+ case ThumbnailOverlayLocationIcon.located:
case ThumbnailOverlayLocationIcon.none:
return AIcons.location;
}
diff --git a/lib/model/settings/settings.dart b/lib/model/settings/settings.dart
index ec3584ff1..7e8b20fa7 100644
--- a/lib/model/settings/settings.dart
+++ b/lib/model/settings/settings.dart
@@ -20,6 +20,7 @@ import 'package:aves/widgets/aves_app.dart';
import 'package:aves/widgets/common/search/page.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/places_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves_map/aves_map.dart';
import 'package:collection/collection.dart';
@@ -106,9 +107,11 @@ class Settings extends ChangeNotifier {
static const albumGroupFactorKey = 'album_group_factor';
static const albumSortFactorKey = 'album_sort_factor';
static const countrySortFactorKey = 'country_sort_factor';
+ static const placeSortFactorKey = 'place_sort_factor';
static const tagSortFactorKey = 'tag_sort_factor';
static const albumSortReverseKey = 'album_sort_reverse';
static const countrySortReverseKey = 'country_sort_reverse';
+ static const placeSortReverseKey = 'place_sort_reverse';
static const tagSortReverseKey = 'tag_sort_reverse';
static const pinnedFiltersKey = 'pinned_filters';
static const hiddenFiltersKey = 'hidden_filters';
@@ -265,6 +268,7 @@ class Settings extends ChangeNotifier {
drawerPageBookmarks = [
AlbumListPage.routeName,
CountryListPage.routeName,
+ PlaceListPage.routeName,
TagListPage.routeName,
SearchPage.routeName,
];
@@ -543,6 +547,10 @@ class Settings extends ChangeNotifier {
set countrySortFactor(ChipSortFactor newValue) => _set(countrySortFactorKey, newValue.toString());
+ ChipSortFactor get placeSortFactor => getEnumOrDefault(placeSortFactorKey, SettingsDefaults.placeSortFactor, ChipSortFactor.values);
+
+ set placeSortFactor(ChipSortFactor newValue) => _set(placeSortFactorKey, newValue.toString());
+
ChipSortFactor get tagSortFactor => getEnumOrDefault(tagSortFactorKey, SettingsDefaults.tagSortFactor, ChipSortFactor.values);
set tagSortFactor(ChipSortFactor newValue) => _set(tagSortFactorKey, newValue.toString());
@@ -555,6 +563,10 @@ class Settings extends ChangeNotifier {
set countrySortReverse(bool newValue) => _set(countrySortReverseKey, newValue);
+ bool get placeSortReverse => getBool(placeSortReverseKey) ?? false;
+
+ set placeSortReverse(bool newValue) => _set(placeSortReverseKey, newValue);
+
bool get tagSortReverse => getBool(tagSortReverseKey) ?? false;
set tagSortReverse(bool newValue) => _set(tagSortReverseKey, newValue);
@@ -1038,6 +1050,7 @@ class Settings extends ChangeNotifier {
case showThumbnailVideoDurationKey:
case albumSortReverseKey:
case countrySortReverseKey:
+ case placeSortReverseKey:
case tagSortReverseKey:
case showOverlayOnOpeningKey:
case showOverlayMinimapKey:
@@ -1084,6 +1097,7 @@ class Settings extends ChangeNotifier {
case albumGroupFactorKey:
case albumSortFactorKey:
case countrySortFactorKey:
+ case placeSortFactorKey:
case tagSortFactorKey:
case imageBackgroundKey:
case videoAutoPlayModeKey:
diff --git a/lib/model/source/collection_lens.dart b/lib/model/source/collection_lens.dart
index f4e48bdd8..5c9730444 100644
--- a/lib/model/source/collection_lens.dart
+++ b/lib/model/source/collection_lens.dart
@@ -14,7 +14,7 @@ import 'package:aves/model/filters/trash.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/events.dart';
-import 'package:aves/model/source/location.dart';
+import 'package:aves/model/source/location/location.dart';
import 'package:aves/model/source/section_keys.dart';
import 'package:aves/model/source/tag.dart';
import 'package:aves/utils/change_notifier.dart';
diff --git a/lib/model/source/collection_source.dart b/lib/model/source/collection_source.dart
index fcb163e1c..e31296c46 100644
--- a/lib/model/source/collection_source.dart
+++ b/lib/model/source/collection_source.dart
@@ -15,7 +15,9 @@ import 'package:aves/model/source/album.dart';
import 'package:aves/model/source/analysis_controller.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/source/events.dart';
-import 'package:aves/model/source/location.dart';
+import 'package:aves/model/source/location/country.dart';
+import 'package:aves/model/source/location/location.dart';
+import 'package:aves/model/source/location/place.dart';
import 'package:aves/model/source/tag.dart';
import 'package:aves/model/source/trash.dart';
import 'package:aves/model/vaults/vaults.dart';
@@ -54,7 +56,7 @@ mixin SourceBase {
void invalidateEntries();
}
-abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagMixin, TrashMixin {
+abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, PlaceMixin, LocationMixin, TagMixin, TrashMixin {
CollectionSource() {
settings.updateStream.where((event) => event.key == Settings.localeKey).listen((_) => invalidateAlbumDisplayNames());
settings.updateStream.where((event) => event.key == Settings.hiddenFiltersKey).listen((event) {
@@ -136,6 +138,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM
invalidateEntries();
invalidateAlbumFilterSummary(entries: entries, notify: notify);
invalidateCountryFilterSummary(entries: entries, notify: notify);
+ invalidatePlaceFilterSummary(entries: entries, notify: notify);
invalidateTagFilterSummary(entries: entries, notify: notify);
}
@@ -501,21 +504,42 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM
int count(CollectionFilter filter) {
if (filter is AlbumFilter) return albumEntryCount(filter);
- if (filter is LocationFilter) return countryEntryCount(filter);
+ if (filter is LocationFilter) {
+ switch (filter.level) {
+ case LocationLevel.country:
+ return countryEntryCount(filter);
+ case LocationLevel.place:
+ return placeEntryCount(filter);
+ }
+ }
if (filter is TagFilter) return tagEntryCount(filter);
return 0;
}
int size(CollectionFilter filter) {
if (filter is AlbumFilter) return albumSize(filter);
- if (filter is LocationFilter) return countrySize(filter);
+ if (filter is LocationFilter) {
+ switch (filter.level) {
+ case LocationLevel.country:
+ return countrySize(filter);
+ case LocationLevel.place:
+ return placeSize(filter);
+ }
+ }
if (filter is TagFilter) return tagSize(filter);
return 0;
}
AvesEntry? recentEntry(CollectionFilter filter) {
if (filter is AlbumFilter) return albumRecentEntry(filter);
- if (filter is LocationFilter) return countryRecentEntry(filter);
+ if (filter is LocationFilter) {
+ switch (filter.level) {
+ case LocationLevel.country:
+ return countryRecentEntry(filter);
+ case LocationLevel.place:
+ return placeRecentEntry(filter);
+ }
+ }
if (filter is TagFilter) return tagRecentEntry(filter);
return null;
}
diff --git a/lib/model/source/location/country.dart b/lib/model/source/location/country.dart
new file mode 100644
index 000000000..a6de50f6a
--- /dev/null
+++ b/lib/model/source/location/country.dart
@@ -0,0 +1,66 @@
+import 'package:aves/model/entry.dart';
+import 'package:aves/model/filters/location.dart';
+import 'package:aves/model/source/collection_source.dart';
+import 'package:aves/utils/collection_utils.dart';
+import 'package:collection/collection.dart';
+
+mixin CountryMixin on SourceBase {
+ // filter summary
+
+ // by country code
+ final Map _filterEntryCountMap = {}, _filterSizeMap = {};
+ final Map _filterRecentEntryMap = {};
+
+ void invalidateCountryFilterSummary({
+ Set? entries,
+ Set? countryCodes,
+ bool notify = true,
+ }) {
+ if (_filterEntryCountMap.isEmpty && _filterSizeMap.isEmpty && _filterRecentEntryMap.isEmpty) return;
+
+ if (entries == null && countryCodes == null) {
+ _filterEntryCountMap.clear();
+ _filterSizeMap.clear();
+ _filterRecentEntryMap.clear();
+ } else {
+ countryCodes ??= {};
+ if (entries != null) {
+ countryCodes.addAll(entries.where((entry) => entry.hasAddress).map((entry) => entry.addressDetails?.countryCode).whereNotNull());
+ }
+ countryCodes.forEach((countryCode) {
+ _filterEntryCountMap.remove(countryCode);
+ _filterSizeMap.remove(countryCode);
+ _filterRecentEntryMap.remove(countryCode);
+ });
+ }
+ if (notify) {
+ eventBus.fire(CountrySummaryInvalidatedEvent(countryCodes));
+ }
+ }
+
+ int countryEntryCount(LocationFilter filter) {
+ final countryCode = filter.countryCode;
+ if (countryCode == null) return 0;
+ return _filterEntryCountMap.putIfAbsent(countryCode, () => visibleEntries.where(filter.test).length);
+ }
+
+ int countrySize(LocationFilter filter) {
+ final countryCode = filter.countryCode;
+ if (countryCode == null) return 0;
+ return _filterSizeMap.putIfAbsent(countryCode, () => visibleEntries.where(filter.test).map((v) => v.sizeBytes).sum);
+ }
+
+ AvesEntry? countryRecentEntry(LocationFilter filter) {
+ final countryCode = filter.countryCode;
+ if (countryCode == null) return null;
+ return _filterRecentEntryMap.putIfAbsent(countryCode, () => sortedEntriesByDate.firstWhereOrNull(filter.test));
+ }
+}
+
+class CountriesChangedEvent {}
+
+class CountrySummaryInvalidatedEvent {
+ final Set? countryCodes;
+
+ const CountrySummaryInvalidatedEvent(this.countryCodes);
+}
diff --git a/lib/model/source/location.dart b/lib/model/source/location/location.dart
similarity index 76%
rename from lib/model/source/location.dart
rename to lib/model/source/location/location.dart
index f063aab80..80703a5b6 100644
--- a/lib/model/source/location.dart
+++ b/lib/model/source/location/location.dart
@@ -6,15 +6,15 @@ import 'package:aves/model/filters/location.dart';
import 'package:aves/model/metadata/address.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/analysis_controller.dart';
-import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums/enums.dart';
+import 'package:aves/model/source/location/country.dart';
+import 'package:aves/model/source/location/place.dart';
import 'package:aves/services/common/services.dart';
-import 'package:aves/utils/collection_utils.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:tuple/tuple.dart';
-mixin LocationMixin on SourceBase {
+mixin LocationMixin on CountryMixin, PlaceMixin {
static const commitCountThreshold = 200;
static const _stopCheckCountThreshold = 50;
@@ -150,7 +150,7 @@ mixin LocationMixin on SourceBase {
}
void updateLocations() {
- final locations = visibleEntries.where((entry) => entry.hasAddress).map((entry) => entry.addressDetails).whereNotNull().toList();
+ final locations = visibleEntries.map((entry) => entry.addressDetails).whereNotNull().toList();
final updatedPlaces = locations.map((address) => address.place).whereNotNull().where((v) => v.isNotEmpty).toSet().toList()..sort(compareAsciiUpperCase);
if (!listEquals(updatedPlaces, sortedPlaces)) {
sortedPlaces = List.unmodifiable(updatedPlaces);
@@ -177,67 +177,6 @@ mixin LocationMixin on SourceBase {
eventBus.fire(CountriesChangedEvent());
}
}
-
- // filter summary
-
- // by country code
- final Map _filterEntryCountMap = {}, _filterSizeMap = {};
- final Map _filterRecentEntryMap = {};
-
- void invalidateCountryFilterSummary({
- Set? entries,
- Set? countryCodes,
- bool notify = true,
- }) {
- if (_filterEntryCountMap.isEmpty && _filterSizeMap.isEmpty && _filterRecentEntryMap.isEmpty) return;
-
- if (entries == null && countryCodes == null) {
- _filterEntryCountMap.clear();
- _filterSizeMap.clear();
- _filterRecentEntryMap.clear();
- } else {
- countryCodes ??= {};
- if (entries != null) {
- countryCodes.addAll(entries.where((entry) => entry.hasAddress).map((entry) => entry.addressDetails?.countryCode).whereNotNull());
- }
- countryCodes.forEach((countryCode) {
- _filterEntryCountMap.remove(countryCode);
- _filterSizeMap.remove(countryCode);
- _filterRecentEntryMap.remove(countryCode);
- });
- }
- if (notify) {
- eventBus.fire(CountrySummaryInvalidatedEvent(countryCodes));
- }
- }
-
- int countryEntryCount(LocationFilter filter) {
- final countryCode = filter.countryCode;
- if (countryCode == null) return 0;
- return _filterEntryCountMap.putIfAbsent(countryCode, () => visibleEntries.where(filter.test).length);
- }
-
- int countrySize(LocationFilter filter) {
- final countryCode = filter.countryCode;
- if (countryCode == null) return 0;
- return _filterSizeMap.putIfAbsent(countryCode, () => visibleEntries.where(filter.test).map((v) => v.sizeBytes).sum);
- }
-
- AvesEntry? countryRecentEntry(LocationFilter filter) {
- final countryCode = filter.countryCode;
- if (countryCode == null) return null;
- return _filterRecentEntryMap.putIfAbsent(countryCode, () => sortedEntriesByDate.firstWhereOrNull(filter.test));
- }
}
class AddressMetadataChangedEvent {}
-
-class PlacesChangedEvent {}
-
-class CountriesChangedEvent {}
-
-class CountrySummaryInvalidatedEvent {
- final Set? countryCodes;
-
- const CountrySummaryInvalidatedEvent(this.countryCodes);
-}
diff --git a/lib/model/source/location/place.dart b/lib/model/source/location/place.dart
new file mode 100644
index 000000000..342202990
--- /dev/null
+++ b/lib/model/source/location/place.dart
@@ -0,0 +1,60 @@
+import 'package:aves/model/entry.dart';
+import 'package:aves/model/filters/location.dart';
+import 'package:aves/model/source/collection_source.dart';
+import 'package:aves/utils/collection_utils.dart';
+import 'package:collection/collection.dart';
+
+mixin PlaceMixin on SourceBase {
+ // filter summary
+
+ // by place
+ final Map _filterEntryCountMap = {}, _filterSizeMap = {};
+ final Map _filterRecentEntryMap = {};
+
+ void invalidatePlaceFilterSummary({
+ Set? entries,
+ Set? places,
+ bool notify = true,
+ }) {
+ if (_filterEntryCountMap.isEmpty && _filterSizeMap.isEmpty && _filterRecentEntryMap.isEmpty) return;
+
+ if (entries == null && places == null) {
+ _filterEntryCountMap.clear();
+ _filterSizeMap.clear();
+ _filterRecentEntryMap.clear();
+ } else {
+ places ??= {};
+ if (entries != null) {
+ places.addAll(entries.map((entry) => entry.addressDetails?.place).whereNotNull());
+ }
+ places.forEach((place) {
+ _filterEntryCountMap.remove(place);
+ _filterSizeMap.remove(place);
+ _filterRecentEntryMap.remove(place);
+ });
+ }
+ if (notify) {
+ eventBus.fire(PlaceSummaryInvalidatedEvent(places));
+ }
+ }
+
+ int placeEntryCount(LocationFilter filter) {
+ return _filterEntryCountMap.putIfAbsent(filter.place, () => visibleEntries.where(filter.test).length);
+ }
+
+ int placeSize(LocationFilter filter) {
+ return _filterSizeMap.putIfAbsent(filter.place, () => visibleEntries.where(filter.test).map((v) => v.sizeBytes).sum);
+ }
+
+ AvesEntry? placeRecentEntry(LocationFilter filter) {
+ return _filterRecentEntryMap.putIfAbsent(filter.place, () => sortedEntriesByDate.firstWhereOrNull(filter.test));
+ }
+}
+
+class PlacesChangedEvent {}
+
+class PlaceSummaryInvalidatedEvent {
+ final Set? places;
+
+ const PlaceSummaryInvalidatedEvent(this.places);
+}
diff --git a/lib/theme/icons.dart b/lib/theme/icons.dart
index a85aea75a..b801bb73f 100644
--- a/lib/theme/icons.dart
+++ b/lib/theme/icons.dart
@@ -36,6 +36,8 @@ class AIcons {
static const IconData language = Icons.translate_outlined;
static const IconData location = Icons.place_outlined;
static const IconData locationUnlocated = Icons.location_off_outlined;
+ static const IconData country = Icons.flag_outlined;
+ static const IconData place = Icons.place_outlined;
static const IconData mainStorage = Icons.smartphone_outlined;
static const IconData mimeType = Icons.code_outlined;
static const IconData opacity = Icons.opacity;
diff --git a/lib/widgets/common/identity/aves_filter_chip.dart b/lib/widgets/common/identity/aves_filter_chip.dart
index e23294c20..f37f7ed5a 100644
--- a/lib/widgets/common/identity/aves_filter_chip.dart
+++ b/lib/widgets/common/identity/aves_filter_chip.dart
@@ -96,6 +96,7 @@ class AvesFilterChip extends StatefulWidget {
final actions = [
if (filter is AlbumFilter) ChipAction.goToAlbumPage,
if ((filter is LocationFilter && filter.level == LocationLevel.country)) ChipAction.goToCountryPage,
+ if ((filter is LocationFilter && filter.level == LocationLevel.place)) ChipAction.goToPlacePage,
if (filter is TagFilter) ChipAction.goToTagPage,
ChipAction.reverse,
ChipAction.hide,
diff --git a/lib/widgets/debug/settings.dart b/lib/widgets/debug/settings.dart
index ee08be83f..b03b03356 100644
--- a/lib/widgets/debug/settings.dart
+++ b/lib/widgets/debug/settings.dart
@@ -3,6 +3,7 @@ import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/common/identity/aves_expansion_tile.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/places_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves/widgets/viewer/info/common.dart';
import 'package:flutter/material.dart';
@@ -51,6 +52,7 @@ class DebugSettingsSection extends StatelessWidget {
'tileExtent - Collection': '${settings.getTileExtent(CollectionPage.routeName)}',
'tileExtent - Albums': '${settings.getTileExtent(AlbumListPage.routeName)}',
'tileExtent - Countries': '${settings.getTileExtent(CountryListPage.routeName)}',
+ 'tileExtent - Places': '${settings.getTileExtent(PlaceListPage.routeName)}',
'tileExtent - Tags': '${settings.getTileExtent(TagListPage.routeName)}',
'infoMapZoom': '${settings.infoMapZoom}',
'collectionSelectionQuickActions': '${settings.collectionSelectionQuickActions}',
diff --git a/lib/widgets/filter_grids/common/action_delegates/chip.dart b/lib/widgets/filter_grids/common/action_delegates/chip.dart
index 3679a812c..223d0aa38 100644
--- a/lib/widgets/filter_grids/common/action_delegates/chip.dart
+++ b/lib/widgets/filter_grids/common/action_delegates/chip.dart
@@ -11,6 +11,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/dialogs/aves_dialog.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/places_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@@ -23,6 +24,7 @@ class ChipActionDelegate with FeedbackMixin, VaultAwareMixin {
switch (action) {
case ChipAction.goToAlbumPage:
case ChipAction.goToCountryPage:
+ case ChipAction.goToPlacePage:
case ChipAction.goToTagPage:
case ChipAction.reverse:
return true;
@@ -42,6 +44,9 @@ class ChipActionDelegate with FeedbackMixin, VaultAwareMixin {
case ChipAction.goToCountryPage:
_goTo(context, filter, CountryListPage.routeName, (context) => const CountryListPage());
break;
+ case ChipAction.goToPlacePage:
+ _goTo(context, filter, PlaceListPage.routeName, (context) => const PlaceListPage());
+ break;
case ChipAction.goToTagPage:
_goTo(context, filter, TagListPage.routeName, (context) => const TagListPage());
break;
diff --git a/lib/widgets/filter_grids/common/action_delegates/place_set.dart b/lib/widgets/filter_grids/common/action_delegates/place_set.dart
new file mode 100644
index 000000000..35e5052bb
--- /dev/null
+++ b/lib/widgets/filter_grids/common/action_delegates/place_set.dart
@@ -0,0 +1,33 @@
+import 'package:aves/model/filters/filters.dart';
+import 'package:aves/model/filters/location.dart';
+import 'package:aves/model/settings/settings.dart';
+import 'package:aves/model/source/enums/enums.dart';
+import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart';
+import 'package:aves/widgets/filter_grids/places_page.dart';
+
+class PlaceChipSetActionDelegate extends ChipSetActionDelegate {
+ final Iterable> _items;
+
+ PlaceChipSetActionDelegate(Iterable> items) : _items = items;
+
+ @override
+ Iterable> get allItems => _items;
+
+ @override
+ ChipSortFactor get sortFactor => settings.placeSortFactor;
+
+ @override
+ set sortFactor(ChipSortFactor factor) => settings.placeSortFactor = factor;
+
+ @override
+ bool get sortReverse => settings.placeSortReverse;
+
+ @override
+ set sortReverse(bool value) => settings.placeSortReverse = value;
+
+ @override
+ TileLayout get tileLayout => settings.getTileLayout(PlaceListPage.routeName);
+
+ @override
+ set tileLayout(TileLayout tileLayout) => settings.setTileLayout(PlaceListPage.routeName, tileLayout);
+}
diff --git a/lib/widgets/filter_grids/common/covered_filter_chip.dart b/lib/widgets/filter_grids/common/covered_filter_chip.dart
index 4bf24cbd9..5187af03e 100644
--- a/lib/widgets/filter_grids/common/covered_filter_chip.dart
+++ b/lib/widgets/filter_grids/common/covered_filter_chip.dart
@@ -7,7 +7,7 @@ import 'package:aves/model/filters/location.dart';
import 'package:aves/model/filters/tag.dart';
import 'package:aves/model/source/album.dart';
import 'package:aves/model/source/collection_source.dart';
-import 'package:aves/model/source/location.dart';
+import 'package:aves/model/source/location/country.dart';
import 'package:aves/model/source/tag.dart';
import 'package:aves/model/vaults/vaults.dart';
import 'package:aves/theme/durations.dart';
diff --git a/lib/widgets/filter_grids/countries_page.dart b/lib/widgets/filter_grids/countries_page.dart
index 6f79a6d78..c44d4b4f6 100644
--- a/lib/widgets/filter_grids/countries_page.dart
+++ b/lib/widgets/filter_grids/countries_page.dart
@@ -3,7 +3,7 @@ import 'package:aves/model/filters/location.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums/enums.dart';
-import 'package:aves/model/source/location.dart';
+import 'package:aves/model/source/location/country.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/empty.dart';
@@ -43,7 +43,7 @@ class CountryListPage extends StatelessWidget {
filterSections: _groupToSections(gridItems),
applyQuery: applyQuery,
emptyBuilder: () => EmptyContent(
- icon: AIcons.location,
+ icon: AIcons.country,
text: context.l10n.countryEmpty,
),
);
diff --git a/lib/widgets/filter_grids/places_page.dart b/lib/widgets/filter_grids/places_page.dart
new file mode 100644
index 000000000..5c75843cb
--- /dev/null
+++ b/lib/widgets/filter_grids/places_page.dart
@@ -0,0 +1,80 @@
+import 'package:aves/model/filters/filters.dart';
+import 'package:aves/model/filters/location.dart';
+import 'package:aves/model/settings/settings.dart';
+import 'package:aves/model/source/collection_source.dart';
+import 'package:aves/model/source/enums/enums.dart';
+import 'package:aves/model/source/location/place.dart';
+import 'package:aves/theme/icons.dart';
+import 'package:aves/widgets/common/extensions/build_context.dart';
+import 'package:aves/widgets/common/identity/empty.dart';
+import 'package:aves/widgets/filter_grids/common/action_delegates/place_set.dart';
+import 'package:aves/widgets/filter_grids/common/filter_nav_page.dart';
+import 'package:aves/widgets/filter_grids/common/section_keys.dart';
+import 'package:collection/collection.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+import 'package:tuple/tuple.dart';
+
+class PlaceListPage extends StatelessWidget {
+ static const routeName = '/places';
+
+ const PlaceListPage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final source = context.read();
+ return Selector>>(
+ selector: (context, s) => Tuple3(s.placeSortFactor, s.placeSortReverse, s.pinnedFilters),
+ shouldRebuild: (t1, t2) {
+ // `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within `TupleN`
+ const eq = DeepCollectionEquality();
+ return !(eq.equals(t1.item1, t2.item1) && eq.equals(t1.item2, t2.item2) && eq.equals(t1.item3, t2.item3));
+ },
+ builder: (context, s, child) {
+ return StreamBuilder(
+ stream: source.eventBus.on(),
+ builder: (context, snapshot) {
+ final gridItems = _getGridItems(source);
+ return FilterNavigationPage(
+ source: source,
+ title: context.l10n.placePageTitle,
+ sortFactor: settings.placeSortFactor,
+ actionDelegate: PlaceChipSetActionDelegate(gridItems),
+ filterSections: _groupToSections(gridItems),
+ applyQuery: applyQuery,
+ emptyBuilder: () => EmptyContent(
+ icon: AIcons.place,
+ text: context.l10n.placeEmpty,
+ ),
+ );
+ },
+ );
+ },
+ );
+ }
+
+ List> applyQuery(BuildContext context, List> filters, String query) {
+ return filters.where((item) => item.filter.getLabel(context).toUpperCase().contains(query)).toList();
+ }
+
+ List> _getGridItems(CollectionSource source) {
+ final filters = source.sortedPlaces.map((location) => LocationFilter(LocationLevel.place, location)).toSet();
+
+ return FilterNavigationPage.sort(settings.placeSortFactor, settings.placeSortReverse, source, filters);
+ }
+
+ static Map>> _groupToSections(Iterable> sortedMapEntries) {
+ final pinned = settings.pinnedFilters.whereType();
+ final byPin = groupBy, bool>(sortedMapEntries, (e) => pinned.contains(e.filter));
+ final pinnedMapEntries = (byPin[true] ?? []);
+ final unpinnedMapEntries = (byPin[false] ?? []);
+
+ return {
+ if (pinnedMapEntries.isNotEmpty || unpinnedMapEntries.isNotEmpty)
+ const ChipSectionKey(): [
+ ...pinnedMapEntries,
+ ...unpinnedMapEntries,
+ ],
+ };
+ }
+}
diff --git a/lib/widgets/navigation/drawer/app_drawer.dart b/lib/widgets/navigation/drawer/app_drawer.dart
index d2ab4380f..3e22c1b43 100644
--- a/lib/widgets/navigation/drawer/app_drawer.dart
+++ b/lib/widgets/navigation/drawer/app_drawer.dart
@@ -6,7 +6,8 @@ import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/album.dart';
import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart';
-import 'package:aves/model/source/location.dart';
+import 'package:aves/model/source/location/country.dart';
+import 'package:aves/model/source/location/place.dart';
import 'package:aves/model/source/tag.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart';
@@ -19,6 +20,7 @@ import 'package:aves/widgets/common/identity/aves_logo.dart';
import 'package:aves/widgets/debug/app_debug_page.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/places_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves/widgets/navigation/drawer/collection_nav_tile.dart';
import 'package:aves/widgets/navigation/drawer/page_nav_tile.dart';
@@ -242,6 +244,12 @@ class _AppDrawerState extends State {
builder: (context, _) => Text('${source.sortedCountries.length}'),
);
break;
+ case PlaceListPage.routeName:
+ trailing = StreamBuilder(
+ stream: source.eventBus.on(),
+ builder: (context, _) => Text('${source.sortedPlaces.length}'),
+ );
+ break;
case TagListPage.routeName:
trailing = StreamBuilder(
stream: source.eventBus.on(),
diff --git a/lib/widgets/navigation/drawer/page_nav_tile.dart b/lib/widgets/navigation/drawer/page_nav_tile.dart
index 1e77dbdd7..8315f82a8 100644
--- a/lib/widgets/navigation/drawer/page_nav_tile.dart
+++ b/lib/widgets/navigation/drawer/page_nav_tile.dart
@@ -7,6 +7,7 @@ import 'package:aves/widgets/common/search/route.dart';
import 'package:aves/widgets/debug/app_debug_page.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/places_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves/widgets/navigation/drawer/tile.dart';
import 'package:aves/widgets/search/search_delegate.dart';
@@ -88,6 +89,8 @@ class PageNavTile extends StatelessWidget {
return (_) => const AlbumListPage();
case CountryListPage.routeName:
return (_) => const CountryListPage();
+ case PlaceListPage.routeName:
+ return (_) => const PlaceListPage();
case TagListPage.routeName:
return (_) => const TagListPage();
case SettingsPage.routeName:
diff --git a/lib/widgets/navigation/nav_display.dart b/lib/widgets/navigation/nav_display.dart
index b21f7114f..7a0d3fb14 100644
--- a/lib/widgets/navigation/nav_display.dart
+++ b/lib/widgets/navigation/nav_display.dart
@@ -9,6 +9,7 @@ import 'package:aves/widgets/common/search/page.dart';
import 'package:aves/widgets/debug/app_debug_page.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/places_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves/widgets/settings/settings_page.dart';
import 'package:flutter/material.dart';
@@ -35,6 +36,8 @@ class NavigationDisplay {
return l10n.drawerAlbumPage;
case CountryListPage.routeName:
return l10n.drawerCountryPage;
+ case PlaceListPage.routeName:
+ return l10n.drawerPlacePage;
case TagListPage.routeName:
return l10n.drawerTagPage;
case SettingsPage.routeName:
@@ -55,7 +58,9 @@ class NavigationDisplay {
case AlbumListPage.routeName:
return AIcons.album;
case CountryListPage.routeName:
- return AIcons.location;
+ return AIcons.country;
+ case PlaceListPage.routeName:
+ return AIcons.place;
case TagListPage.routeName:
return AIcons.tag;
case SettingsPage.routeName:
diff --git a/lib/widgets/search/search_delegate.dart b/lib/widgets/search/search_delegate.dart
index bd6705ca9..7dba129a5 100644
--- a/lib/widgets/search/search_delegate.dart
+++ b/lib/widgets/search/search_delegate.dart
@@ -15,7 +15,8 @@ import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/album.dart';
import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart';
-import 'package:aves/model/source/location.dart';
+import 'package:aves/model/source/location/country.dart';
+import 'package:aves/model/source/location/place.dart';
import 'package:aves/model/source/tag.dart';
import 'package:aves/ref/mime_types.dart';
import 'package:aves/widgets/collection/collection_page.dart';
diff --git a/lib/widgets/settings/navigation/drawer.dart b/lib/widgets/settings/navigation/drawer.dart
index 6cb73edfd..20e5a948e 100644
--- a/lib/widgets/settings/navigation/drawer.dart
+++ b/lib/widgets/settings/navigation/drawer.dart
@@ -6,6 +6,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/search/page.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/places_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves/widgets/navigation/drawer/app_drawer.dart';
import 'package:aves/widgets/navigation/drawer/tile.dart';
@@ -40,6 +41,7 @@ class _NavigationDrawerEditorPageState extends State
static const Set _pageOptions = {
AlbumListPage.routeName,
CountryListPage.routeName,
+ PlaceListPage.routeName,
TagListPage.routeName,
SearchPage.routeName,
};
diff --git a/untranslated.json b/untranslated.json
index 5cea5a17c..93b33381b 100644
--- a/untranslated.json
+++ b/untranslated.json
@@ -14,6 +14,7 @@
"chipActionDelete",
"chipActionGoToAlbumPage",
"chipActionGoToCountryPage",
+ "chipActionGoToPlacePage",
"chipActionGoToTagPage",
"chipActionFilterOut",
"chipActionFilterIn",
@@ -334,6 +335,7 @@
"drawerCollectionSphericalVideos",
"drawerAlbumPage",
"drawerCountryPage",
+ "drawerPlacePage",
"drawerTagPage",
"sortByDate",
"sortByName",
@@ -369,6 +371,8 @@
"newFilterBanner",
"countryPageTitle",
"countryEmpty",
+ "placePageTitle",
+ "placeEmpty",
"tagPageTitle",
"tagEmpty",
"binPageTitle",
@@ -591,6 +595,7 @@
],
"cs": [
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -609,11 +614,15 @@
"authenticateToConfigureVault",
"authenticateToUnlockVault",
"vaultBinUsageDialogMessage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty",
"settingsConfirmationVaultDataLoss",
"settingsDisablingBinWarningDialogMessage"
],
"de": [
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -632,11 +641,29 @@
"authenticateToConfigureVault",
"authenticateToUnlockVault",
"vaultBinUsageDialogMessage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty",
"settingsConfirmationVaultDataLoss",
"settingsDisablingBinWarningDialogMessage"
],
+ "el": [
+ "chipActionGoToPlacePage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty"
+ ],
+
+ "es": [
+ "chipActionGoToPlacePage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty"
+ ],
+
"eu": [
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -655,12 +682,16 @@
"authenticateToConfigureVault",
"authenticateToUnlockVault",
"vaultBinUsageDialogMessage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty",
"settingsConfirmationVaultDataLoss",
"settingsDisablingBinWarningDialogMessage"
],
"fa": [
"clearTooltip",
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -858,6 +889,7 @@
"drawerCollectionSphericalVideos",
"drawerAlbumPage",
"drawerCountryPage",
+ "drawerPlacePage",
"drawerTagPage",
"sortByDate",
"sortByName",
@@ -892,6 +924,8 @@
"newFilterBanner",
"countryPageTitle",
"countryEmpty",
+ "placePageTitle",
+ "placeEmpty",
"tagPageTitle",
"tagEmpty",
"binPageTitle",
@@ -1120,8 +1154,16 @@
"filePickerUseThisFolder"
],
+ "fr": [
+ "chipActionGoToPlacePage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty"
+ ],
+
"gl": [
"columnCount",
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -1344,6 +1386,7 @@
"drawerCollectionSphericalVideos",
"drawerAlbumPage",
"drawerCountryPage",
+ "drawerPlacePage",
"drawerTagPage",
"sortByDate",
"sortByName",
@@ -1379,6 +1422,8 @@
"newFilterBanner",
"countryPageTitle",
"countryEmpty",
+ "placePageTitle",
+ "placeEmpty",
"tagPageTitle",
"tagEmpty",
"binPageTitle",
@@ -1646,6 +1691,7 @@
"chipActionDelete",
"chipActionGoToAlbumPage",
"chipActionGoToCountryPage",
+ "chipActionGoToPlacePage",
"chipActionGoToTagPage",
"chipActionFilterOut",
"chipActionFilterIn",
@@ -1966,6 +2012,7 @@
"drawerCollectionSphericalVideos",
"drawerAlbumPage",
"drawerCountryPage",
+ "drawerPlacePage",
"drawerTagPage",
"sortByDate",
"sortByName",
@@ -2001,6 +2048,8 @@
"newFilterBanner",
"countryPageTitle",
"countryEmpty",
+ "placePageTitle",
+ "placeEmpty",
"tagPageTitle",
"tagEmpty",
"binPageTitle",
@@ -2235,7 +2284,15 @@
"filePickerUseThisFolder"
],
+ "id": [
+ "chipActionGoToPlacePage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty"
+ ],
+
"it": [
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -2254,12 +2311,16 @@
"authenticateToConfigureVault",
"authenticateToUnlockVault",
"vaultBinUsageDialogMessage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty",
"settingsConfirmationVaultDataLoss",
"settingsDisablingBinWarningDialogMessage"
],
"ja": [
"columnCount",
+ "chipActionGoToPlacePage",
"chipActionFilterIn",
"chipActionLock",
"chipActionCreateVault",
@@ -2288,6 +2349,9 @@
"authenticateToUnlockVault",
"vaultBinUsageDialogMessage",
"tooManyItemsErrorDialogMessage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty",
"settingsModificationWarningDialogMessage",
"settingsConfirmationVaultDataLoss",
"settingsViewerShowDescription",
@@ -2298,8 +2362,16 @@
"settingsWidgetDisplayedItem"
],
+ "ko": [
+ "chipActionGoToPlacePage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty"
+ ],
+
"lt": [
"columnCount",
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -2322,6 +2394,9 @@
"authenticateToUnlockVault",
"vaultBinUsageDialogMessage",
"tooManyItemsErrorDialogMessage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty",
"settingsModificationWarningDialogMessage",
"settingsConfirmationVaultDataLoss",
"settingsViewerShowDescription",
@@ -2332,6 +2407,7 @@
],
"nb": [
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -2350,12 +2426,16 @@
"authenticateToConfigureVault",
"authenticateToUnlockVault",
"vaultBinUsageDialogMessage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty",
"settingsConfirmationVaultDataLoss",
"settingsDisablingBinWarningDialogMessage"
],
"nl": [
"columnCount",
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -2389,6 +2469,9 @@
"authenticateToUnlockVault",
"vaultBinUsageDialogMessage",
"tooManyItemsErrorDialogMessage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty",
"settingsModificationWarningDialogMessage",
"settingsConfirmationVaultDataLoss",
"settingsViewerShowRatingTags",
@@ -2405,6 +2488,7 @@
"nn": [
"columnCount",
"sourceStateCataloguing",
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -2504,6 +2588,7 @@
"drawerCollectionSphericalVideos",
"drawerAlbumPage",
"drawerCountryPage",
+ "drawerPlacePage",
"drawerTagPage",
"sortByDate",
"sortByName",
@@ -2539,6 +2624,8 @@
"newFilterBanner",
"countryPageTitle",
"countryEmpty",
+ "placePageTitle",
+ "placeEmpty",
"tagPageTitle",
"tagEmpty",
"binPageTitle",
@@ -2709,8 +2796,16 @@
"wallpaperUseScrollEffect"
],
+ "pl": [
+ "chipActionGoToPlacePage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty"
+ ],
+
"pt": [
"columnCount",
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -2730,12 +2825,23 @@
"authenticateToUnlockVault",
"vaultBinUsageDialogMessage",
"tooManyItemsErrorDialogMessage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty",
"settingsConfirmationVaultDataLoss",
"settingsVideoGestureVerticalDragBrightnessVolume",
"settingsDisablingBinWarningDialogMessage"
],
+ "ro": [
+ "chipActionGoToPlacePage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty"
+ ],
+
"ru": [
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -2757,6 +2863,9 @@
"authenticateToUnlockVault",
"vaultBinUsageDialogMessage",
"tooManyItemsErrorDialogMessage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty",
"settingsModificationWarningDialogMessage",
"settingsConfirmationVaultDataLoss",
"settingsVideoGestureVerticalDragBrightnessVolume",
@@ -2768,6 +2877,7 @@
"itemCount",
"columnCount",
"timeSeconds",
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -2905,6 +3015,7 @@
"drawerCollectionSphericalVideos",
"drawerAlbumPage",
"drawerCountryPage",
+ "drawerPlacePage",
"drawerTagPage",
"sortByDate",
"sortByName",
@@ -2940,6 +3051,8 @@
"newFilterBanner",
"countryPageTitle",
"countryEmpty",
+ "placePageTitle",
+ "placeEmpty",
"tagPageTitle",
"tagEmpty",
"binPageTitle",
@@ -3182,6 +3295,7 @@
"timeDays",
"focalLength",
"applyButtonLabel",
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -3250,6 +3364,7 @@
"drawerCollectionSphericalVideos",
"drawerAlbumPage",
"drawerCountryPage",
+ "drawerPlacePage",
"drawerTagPage",
"sortByDate",
"sortByName",
@@ -3285,6 +3400,8 @@
"newFilterBanner",
"countryPageTitle",
"countryEmpty",
+ "placePageTitle",
+ "placeEmpty",
"tagPageTitle",
"tagEmpty",
"binPageTitle",
@@ -3520,6 +3637,7 @@
],
"tr": [
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -3538,11 +3656,22 @@
"authenticateToConfigureVault",
"authenticateToUnlockVault",
"vaultBinUsageDialogMessage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty",
"settingsConfirmationVaultDataLoss",
"settingsDisablingBinWarningDialogMessage"
],
+ "uk": [
+ "chipActionGoToPlacePage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty"
+ ],
+
"zh": [
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -3564,6 +3693,9 @@
"authenticateToUnlockVault",
"vaultBinUsageDialogMessage",
"tooManyItemsErrorDialogMessage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty",
"settingsModificationWarningDialogMessage",
"settingsConfirmationVaultDataLoss",
"settingsViewerShowDescription",
@@ -3575,6 +3707,7 @@
"zh_Hant": [
"columnCount",
+ "chipActionGoToPlacePage",
"chipActionLock",
"chipActionCreateVault",
"chipActionConfigureVault",
@@ -3596,6 +3729,9 @@
"authenticateToUnlockVault",
"vaultBinUsageDialogMessage",
"tooManyItemsErrorDialogMessage",
+ "drawerPlacePage",
+ "placePageTitle",
+ "placeEmpty",
"settingsModificationWarningDialogMessage",
"settingsConfirmationVaultDataLoss",
"settingsViewerShowDescription",