diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d866c405..7bbda9a11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. - Video: action to lock viewer - Info: improved state/place display (requires rescan, limited to AU/GB/IN/US) - Info: edit tags with state placeholder +- Countries: show states for selected countries - improved support for system font scale ### Changed diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index f28f4b79e..4bc25f2df 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -696,6 +696,7 @@ "searchDateSectionTitle": "Date", "searchAlbumsSectionTitle": "Albums", "searchCountriesSectionTitle": "Countries", + "searchStatesSectionTitle": "States", "searchPlacesSectionTitle": "Places", "searchTagsSectionTitle": "Tags", "searchRatingSectionTitle": "Ratings", @@ -901,6 +902,7 @@ } }, "statsTopCountriesSectionTitle": "Top Countries", + "statsTopStatesSectionTitle": "Top States", "statsTopPlacesSectionTitle": "Top Places", "statsTopTagsSectionTitle": "Top Tags", "statsTopAlbumsSectionTitle": "Top Albums", diff --git a/lib/model/entry/extensions/location.dart b/lib/model/entry/extensions/location.dart index 1e4988503..e33c4ab72 100644 --- a/lib/model/entry/extensions/location.dart +++ b/lib/model/entry/extensions/location.dart @@ -12,6 +12,8 @@ import 'package:flutter/foundation.dart'; import 'package:latlong2/latlong.dart'; extension ExtraAvesEntryLocation on AvesEntry { + static final _invalidLocalityPattern = RegExp(r'^[-+\dA-Z]+$'); + LatLng? get latLng => hasGps ? LatLng(catalogMetadata!.latitude!, catalogMetadata!.longitude!) : null; Future locate({required bool background, required bool force, required Locale geocoderLocale}) async { @@ -55,7 +57,7 @@ extension ExtraAvesEntryLocation on AvesEntry { if (addresses.isNotEmpty) { final v = addresses.first; var locality = v.locality ?? v.subLocality ?? v.featureName; - if (locality == null || locality == v.subThoroughfare) { + if (locality == null || _invalidLocalityPattern.hasMatch(locality) || {v.subThoroughfare, v.countryName}.contains(locality)) { locality = v.subAdminArea; } addressDetails = AddressDetails( diff --git a/lib/model/filters/query.dart b/lib/model/filters/query.dart index d48c72602..523a1b156 100644 --- a/lib/model/filters/query.dart +++ b/lib/model/filters/query.dart @@ -10,7 +10,7 @@ import 'package:provider/provider.dart'; class QueryFilter extends CollectionFilter { static const type = 'query'; - static final RegExp exactRegex = RegExp('^"(.*)"\$'); + static final exactRegex = RegExp('^"(.*)"\$'); final String query; final bool colorful, live; diff --git a/lib/widgets/common/app_bar/app_bar_subtitle.dart b/lib/widgets/common/app_bar/app_bar_subtitle.dart index a021b149c..3414e50fa 100644 --- a/lib/widgets/common/app_bar/app_bar_subtitle.dart +++ b/lib/widgets/common/app_bar/app_bar_subtitle.dart @@ -83,6 +83,9 @@ class SourceStateSubtitle extends StatelessWidget { ] ], ), + softWrap: false, + overflow: TextOverflow.fade, + maxLines: 1, ); }, ), diff --git a/lib/widgets/search/search_delegate.dart b/lib/widgets/search/search_delegate.dart index 060c3a515..fb1fd59d4 100644 --- a/lib/widgets/search/search_delegate.dart +++ b/lib/widgets/search/search_delegate.dart @@ -136,6 +136,7 @@ class CollectionSearchDelegate extends AvesSearchDelegate with FeedbackMixin, Va _buildDateFilters(context, containQuery), _buildAlbumFilters(containQuery), _buildCountryFilters(containQuery), + _buildStateFilters(containQuery), _buildPlaceFilters(containQuery), _buildTagFilters(containQuery), _buildRatingFilters(context, containQuery), @@ -223,6 +224,19 @@ class CollectionSearchDelegate extends AvesSearchDelegate with FeedbackMixin, Va ); } + Widget _buildStateFilters(_ContainQuery containQuery) { + return StreamBuilder( + stream: source.eventBus.on(), + builder: (context, snapshot) { + return _buildFilterRow( + context: context, + title: context.l10n.searchStatesSectionTitle, + filters: source.sortedStates.where(containQuery).map((s) => LocationFilter(LocationLevel.state, s)).toList(), + ); + }, + ); + } + Widget _buildPlaceFilters(_ContainQuery containQuery) { return StreamBuilder( stream: source.eventBus.on(), @@ -230,10 +244,7 @@ class CollectionSearchDelegate extends AvesSearchDelegate with FeedbackMixin, Va return _buildFilterRow( context: context, title: context.l10n.searchPlacesSectionTitle, - filters: [ - ...source.sortedStates.where(containQuery).map((s) => LocationFilter(LocationLevel.state, s)), - ...source.sortedPlaces.where(containQuery).map((s) => LocationFilter(LocationLevel.place, s)), - ].toList(), + filters: source.sortedPlaces.where(containQuery).map((s) => LocationFilter(LocationLevel.place, s)).toList(), ); }, ); diff --git a/lib/widgets/stats/stats_page.dart b/lib/widgets/stats/stats_page.dart index 171a7fb7c..e3c81ad43 100644 --- a/lib/widgets/stats/stats_page.dart +++ b/lib/widgets/stats/stats_page.dart @@ -29,7 +29,6 @@ import 'package:aves/widgets/stats/filter_table.dart'; import 'package:aves/widgets/stats/mime_donut.dart'; import 'package:aves/widgets/stats/percent_text.dart'; import 'package:collection/collection.dart'; -import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; @@ -55,8 +54,8 @@ class StatsPage extends StatefulWidget { } class _StatsPageState extends State with FeedbackMixin, VaultAwareMixin { - final Map _entryCountPerCountry = {}, _entryCountPerTag = {}, _entryCountPerAlbum = {}; - final Map<_PlaceFilterKey, int> _entryCountPerPlace = {}; + final Map _entryCountPerCountry = {}, _entryCountPerState = {}, _entryCountPerPlace = {}; + final Map _entryCountPerTag = {}, _entryCountPerAlbum = {}; final Map _entryCountPerRating = Map.fromEntries(List.generate(7, (i) => MapEntry(5 - i, 0))); late final ValueNotifier _isPageAnimatingNotifier; @@ -83,13 +82,11 @@ class _StatsPageState extends State with FeedbackMixin, VaultAwareMix var state = address.stateName; if (state != null && state.isNotEmpty) { state += '${LocationFilter.locationSeparator}${address.stateCode}'; - final key = _PlaceFilterKey(LocationLevel.state, state); - _entryCountPerPlace[key] = (_entryCountPerPlace[key] ?? 0) + 1; + _entryCountPerState[state] = (_entryCountPerState[state] ?? 0) + 1; } final place = address.place; if (place != null && place.isNotEmpty) { - final key = _PlaceFilterKey(LocationLevel.place, place); - _entryCountPerPlace[key] = (_entryCountPerPlace[key] ?? 0) + 1; + _entryCountPerPlace[place] = (_entryCountPerPlace[place] ?? 0) + 1; } } @@ -219,7 +216,8 @@ class _StatsPageState extends State with FeedbackMixin, VaultAwareMix ), locationIndicator, ..._buildFilterSection(context, l10n.statsTopCountriesSectionTitle, _entryCountPerCountry, (v) => LocationFilter(LocationLevel.country, v)), - ..._buildFilterSection<_PlaceFilterKey>(context, l10n.statsTopPlacesSectionTitle, _entryCountPerPlace, (v) => LocationFilter(v.level, v.location)), + ..._buildFilterSection(context, l10n.statsTopStatesSectionTitle, _entryCountPerState, (v) => LocationFilter(LocationLevel.state, v)), + ..._buildFilterSection(context, l10n.statsTopPlacesSectionTitle, _entryCountPerPlace, (v) => LocationFilter(LocationLevel.place, v)), ..._buildFilterSection(context, l10n.statsTopTagsSectionTitle, _entryCountPerTag, TagFilter.new), ..._buildFilterSection(context, l10n.statsTopAlbumsSectionTitle, _entryCountPerAlbum, (v) => AlbumFilter(v, source.getAlbumDisplayName(context, v))), if (showRatings) ..._buildFilterSection(context, l10n.searchRatingSectionTitle, _entryCountPerRating, RatingFilter.new, sortByCount: false, maxRowCount: null), @@ -408,27 +406,3 @@ class StatsTopPage extends StatelessWidget { ); } } - -@immutable -class _PlaceFilterKey extends Comparable<_PlaceFilterKey> with EquatableMixin { - final LocationLevel level; - final String location; - - @override - List get props => [level, location]; - - _PlaceFilterKey(this.level, this.location); - - static const _levelOrder = [ - LocationLevel.country, - LocationLevel.state, - LocationLevel.place, - ]; - - @override - int compareTo(_PlaceFilterKey other) { - final c = _levelOrder.indexOf(level).compareTo(_levelOrder.indexOf(other.level)); - if (c != 0) return c; - return location.compareTo(other.location); - } -} diff --git a/lib/widgets/viewer/overlay/details/location.dart b/lib/widgets/viewer/overlay/details/location.dart index 4d6897f0b..b646168e6 100644 --- a/lib/widgets/viewer/overlay/details/location.dart +++ b/lib/widgets/viewer/overlay/details/location.dart @@ -4,6 +4,7 @@ import 'package:aves/model/settings/enums/coordinate_format.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/theme/styles.dart'; +import 'package:aves/theme/text.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/viewer/overlay/details/details.dart'; import 'package:decorated_icon/decorated_icon.dart'; @@ -19,22 +20,21 @@ class OverlayLocationRow extends AnimatedWidget { @override Widget build(BuildContext context) { - late final String location; + String? location; if (entry.hasAddress) { location = entry.shortAddress; - } else { + } + if (location == null || location.isEmpty) { final latLng = entry.latLng; if (latLng != null) { location = settings.coordinateFormat.format(context.l10n, latLng); - } else { - location = ''; } } return Row( children: [ DecoratedIcon(AIcons.location, size: ViewerDetailOverlayContent.iconSize, shadows: ViewerDetailOverlayContent.shadows(context)), const SizedBox(width: ViewerDetailOverlayContent.iconPadding), - Expanded(child: Text(location, strutStyle: AStyles.overflowStrut)), + Expanded(child: Text(location ?? AText.valueNotAvailable, strutStyle: AStyles.overflowStrut)), ], ); } diff --git a/lib/widgets/viewer/visual/controller_mixin.dart b/lib/widgets/viewer/visual/controller_mixin.dart index 6f39dd025..787607311 100644 --- a/lib/widgets/viewer/visual/controller_mixin.dart +++ b/lib/widgets/viewer/visual/controller_mixin.dart @@ -133,6 +133,8 @@ mixin EntryViewControllerMixin on State { } Future _initMultiPageController(AvesEntry entry) async { + if (!mounted) return; + final multiPageController = context.read().getOrCreateController(entry); setState(() {}); diff --git a/untranslated.json b/untranslated.json index 768eb4cf6..dc649ce48 100644 --- a/untranslated.json +++ b/untranslated.json @@ -393,6 +393,7 @@ "searchDateSectionTitle", "searchAlbumsSectionTitle", "searchCountriesSectionTitle", + "searchStatesSectionTitle", "searchPlacesSectionTitle", "searchTagsSectionTitle", "searchRatingSectionTitle", @@ -565,6 +566,7 @@ "statsPageTitle", "statsWithGps", "statsTopCountriesSectionTitle", + "statsTopStatesSectionTitle", "statsTopPlacesSectionTitle", "statsTopTagsSectionTitle", "statsTopAlbumsSectionTitle", @@ -959,6 +961,7 @@ "searchDateSectionTitle", "searchAlbumsSectionTitle", "searchCountriesSectionTitle", + "searchStatesSectionTitle", "searchPlacesSectionTitle", "searchTagsSectionTitle", "searchRatingSectionTitle", @@ -1131,6 +1134,7 @@ "statsPageTitle", "statsWithGps", "statsTopCountriesSectionTitle", + "statsTopStatesSectionTitle", "statsTopPlacesSectionTitle", "statsTopTagsSectionTitle", "statsTopAlbumsSectionTitle", @@ -1197,10 +1201,12 @@ "settingsVideoEnablePip", "statePageTitle", "stateEmpty", + "searchStatesSectionTitle", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", "settingsVideoBackgroundMode", "settingsVideoBackgroundModeDialogTitle", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -1239,12 +1245,14 @@ "stateEmpty", "placePageTitle", "placeEmpty", + "searchStatesSectionTitle", "settingsConfirmationVaultDataLoss", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", "settingsVideoBackgroundMode", "settingsVideoBackgroundModeDialogTitle", "settingsDisablingBinWarningDialogMessage", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -1263,10 +1271,12 @@ "stateEmpty", "placePageTitle", "placeEmpty", + "searchStatesSectionTitle", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", "settingsVideoBackgroundMode", "settingsVideoBackgroundModeDialogTitle", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -1276,8 +1286,10 @@ "viewerActionUnlock", "statePageTitle", "stateEmpty", + "searchStatesSectionTitle", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -1287,8 +1299,10 @@ "viewerActionUnlock", "statePageTitle", "stateEmpty", + "searchStatesSectionTitle", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -1549,6 +1563,7 @@ "searchDateSectionTitle", "searchAlbumsSectionTitle", "searchCountriesSectionTitle", + "searchStatesSectionTitle", "searchPlacesSectionTitle", "searchTagsSectionTitle", "searchRatingSectionTitle", @@ -1717,6 +1732,7 @@ "statsPageTitle", "statsWithGps", "statsTopCountriesSectionTitle", + "statsTopStatesSectionTitle", "statsTopPlacesSectionTitle", "statsTopTagsSectionTitle", "statsTopAlbumsSectionTitle", @@ -1780,6 +1796,8 @@ "viewerActionUnlock", "statePageTitle", "stateEmpty", + "searchStatesSectionTitle", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -2066,6 +2084,7 @@ "searchDateSectionTitle", "searchAlbumsSectionTitle", "searchCountriesSectionTitle", + "searchStatesSectionTitle", "searchPlacesSectionTitle", "searchTagsSectionTitle", "searchRatingSectionTitle", @@ -2238,6 +2257,7 @@ "statsPageTitle", "statsWithGps", "statsTopCountriesSectionTitle", + "statsTopStatesSectionTitle", "statsTopPlacesSectionTitle", "statsTopTagsSectionTitle", "statsTopAlbumsSectionTitle", @@ -2709,6 +2729,7 @@ "searchDateSectionTitle", "searchAlbumsSectionTitle", "searchCountriesSectionTitle", + "searchStatesSectionTitle", "searchPlacesSectionTitle", "searchTagsSectionTitle", "searchRatingSectionTitle", @@ -2881,6 +2902,7 @@ "statsPageTitle", "statsWithGps", "statsTopCountriesSectionTitle", + "statsTopStatesSectionTitle", "statsTopPlacesSectionTitle", "statsTopTagsSectionTitle", "statsTopAlbumsSectionTitle", @@ -3332,6 +3354,7 @@ "searchDateSectionTitle", "searchAlbumsSectionTitle", "searchCountriesSectionTitle", + "searchStatesSectionTitle", "searchPlacesSectionTitle", "searchTagsSectionTitle", "searchRatingSectionTitle", @@ -3504,6 +3527,7 @@ "statsPageTitle", "statsWithGps", "statsTopCountriesSectionTitle", + "statsTopStatesSectionTitle", "statsTopPlacesSectionTitle", "statsTopTagsSectionTitle", "statsTopAlbumsSectionTitle", @@ -3569,8 +3593,10 @@ "viewerActionUnlock", "statePageTitle", "stateEmpty", + "searchStatesSectionTitle", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -3580,8 +3606,10 @@ "viewerActionUnlock", "statePageTitle", "stateEmpty", + "searchStatesSectionTitle", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -3631,6 +3659,7 @@ "stateEmpty", "placePageTitle", "placeEmpty", + "searchStatesSectionTitle", "settingsModificationWarningDialogMessage", "settingsConfirmationVaultDataLoss", "settingsCollectionBurstPatternsTile", @@ -3643,6 +3672,7 @@ "settingsAccessibilityShowPinchGestureAlternatives", "settingsDisplayUseTvInterface", "settingsWidgetDisplayedItem", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -3652,6 +3682,8 @@ "viewerActionUnlock", "statePageTitle", "stateEmpty", + "searchStatesSectionTitle", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -3695,6 +3727,7 @@ "stateEmpty", "placePageTitle", "placeEmpty", + "searchStatesSectionTitle", "settingsModificationWarningDialogMessage", "settingsConfirmationVaultDataLoss", "settingsCollectionBurstPatternsTile", @@ -3706,6 +3739,7 @@ "settingsDisablingBinWarningDialogMessage", "settingsAccessibilityShowPinchGestureAlternatives", "settingsDisplayUseTvInterface", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -3719,10 +3753,12 @@ "patternDialogConfirm", "statePageTitle", "stateEmpty", + "searchStatesSectionTitle", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", "settingsVideoBackgroundMode", "settingsVideoBackgroundModeDialogTitle", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -3777,6 +3813,7 @@ "stateEmpty", "placePageTitle", "placeEmpty", + "searchStatesSectionTitle", "settingsModificationWarningDialogMessage", "settingsConfirmationVaultDataLoss", "settingsCollectionBurstPatternsTile", @@ -3792,6 +3829,7 @@ "settingsAccessibilityShowPinchGestureAlternatives", "settingsDisplayUseTvInterface", "settingsWidgetDisplayedItem", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -3956,6 +3994,7 @@ "searchDateSectionTitle", "searchAlbumsSectionTitle", "searchCountriesSectionTitle", + "searchStatesSectionTitle", "searchPlacesSectionTitle", "searchTagsSectionTitle", "searchRatingSectionTitle", @@ -4112,6 +4151,7 @@ "settingsDisplayUseTvInterface", "settingsWidgetOpenPage", "statsWithGps", + "statsTopStatesSectionTitle", "viewerInfoPageTitle", "viewerInfoLabelDescription", "mapPointNorthUpTooltip", @@ -4129,8 +4169,10 @@ "viewerActionUnlock", "statePageTitle", "stateEmpty", + "searchStatesSectionTitle", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -4140,9 +4182,11 @@ "viewerActionUnlock", "statePageTitle", "stateEmpty", + "searchStatesSectionTitle", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", "settingsVideoBackgroundModeDialogTitle", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -4152,8 +4196,10 @@ "viewerActionUnlock", "statePageTitle", "stateEmpty", + "searchStatesSectionTitle", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -4174,12 +4220,14 @@ "statePageTitle", "stateEmpty", "placeEmpty", + "searchStatesSectionTitle", "settingsConfirmationVaultDataLoss", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", "settingsVideoBackgroundMode", "settingsVideoBackgroundModeDialogTitle", "settingsVideoGestureVerticalDragBrightnessVolume", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -4383,6 +4431,7 @@ "searchDateSectionTitle", "searchAlbumsSectionTitle", "searchCountriesSectionTitle", + "searchStatesSectionTitle", "searchPlacesSectionTitle", "searchTagsSectionTitle", "searchRatingSectionTitle", @@ -4555,6 +4604,7 @@ "statsPageTitle", "statsWithGps", "statsTopCountriesSectionTitle", + "statsTopStatesSectionTitle", "statsTopPlacesSectionTitle", "statsTopTagsSectionTitle", "statsTopAlbumsSectionTitle", @@ -4749,6 +4799,7 @@ "searchDateSectionTitle", "searchAlbumsSectionTitle", "searchCountriesSectionTitle", + "searchStatesSectionTitle", "searchPlacesSectionTitle", "searchTagsSectionTitle", "searchRatingSectionTitle", @@ -4921,6 +4972,7 @@ "statsPageTitle", "statsWithGps", "statsTopCountriesSectionTitle", + "statsTopStatesSectionTitle", "statsTopPlacesSectionTitle", "statsTopTagsSectionTitle", "statsTopAlbumsSectionTitle", @@ -5015,12 +5067,14 @@ "stateEmpty", "placePageTitle", "placeEmpty", + "searchStatesSectionTitle", "settingsConfirmationVaultDataLoss", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", "settingsVideoBackgroundMode", "settingsVideoBackgroundModeDialogTitle", "settingsDisablingBinWarningDialogMessage", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -5030,8 +5084,10 @@ "viewerActionUnlock", "statePageTitle", "stateEmpty", + "searchStatesSectionTitle", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -5073,6 +5129,7 @@ "stateEmpty", "placePageTitle", "placeEmpty", + "searchStatesSectionTitle", "settingsModificationWarningDialogMessage", "settingsConfirmationVaultDataLoss", "settingsCollectionBurstPatternsTile", @@ -5084,6 +5141,7 @@ "settingsDisablingBinWarningDialogMessage", "settingsAccessibilityShowPinchGestureAlternatives", "settingsDisplayUseTvInterface", + "statsTopStatesSectionTitle", "tagPlaceholderState" ], @@ -5126,6 +5184,7 @@ "stateEmpty", "placePageTitle", "placeEmpty", + "searchStatesSectionTitle", "settingsModificationWarningDialogMessage", "settingsConfirmationVaultDataLoss", "settingsCollectionBurstPatternsTile", @@ -5137,6 +5196,7 @@ "settingsDisablingBinWarningDialogMessage", "settingsAccessibilityShowPinchGestureAlternatives", "settingsDisplayUseTvInterface", + "statsTopStatesSectionTitle", "tagPlaceholderState" ] }