#560 video: action to lock viewer

This commit is contained in:
Thibault Deckers 2023-03-25 23:31:55 +01:00
parent 3eb1b30552
commit 2e0b15787f
12 changed files with 278 additions and 43 deletions

View file

@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
### Added ### Added
- Collection: optional support for Samsung and Sony burst patterns - Collection: optional support for Samsung and Sony burst patterns
- Video: action to lock viewer
- Info: improved state/place display (requires rescan, limited to AU/GB/IN/US) - Info: improved state/place display (requires rescan, limited to AU/GB/IN/US)
- Info: edit tags with state placeholder - Info: edit tags with state placeholder
- improved support for system font scale - improved support for system font scale

View file

@ -125,6 +125,8 @@
"videoActionSetSpeed": "Playback speed", "videoActionSetSpeed": "Playback speed",
"viewerActionSettings": "Settings", "viewerActionSettings": "Settings",
"viewerActionLock": "Lock viewer",
"viewerActionUnlock": "Unlock viewer",
"slideshowActionResume": "Resume", "slideshowActionResume": "Resume",
"slideshowActionShowInCollection": "Show in Collection", "slideshowActionShowInCollection": "Show in Collection",

View file

@ -23,6 +23,7 @@ enum EntryAction {
// vector // vector
viewSource, viewSource,
// video // video
lockViewer,
videoCaptureFrame, videoCaptureFrame,
videoSelectStreams, videoSelectStreams,
videoSetSpeed, videoSetSpeed,
@ -88,7 +89,7 @@ class EntryActions {
EntryAction.setAs, EntryAction.setAs,
]; ];
static const pageActions = [ static const pageActions = {
EntryAction.videoCaptureFrame, EntryAction.videoCaptureFrame,
EntryAction.videoSelectStreams, EntryAction.videoSelectStreams,
EntryAction.videoSetSpeed, EntryAction.videoSetSpeed,
@ -100,7 +101,7 @@ class EntryActions {
EntryAction.rotateCCW, EntryAction.rotateCCW,
EntryAction.rotateCW, EntryAction.rotateCW,
EntryAction.flip, EntryAction.flip,
]; };
static const trashed = [ static const trashed = [
EntryAction.delete, EntryAction.delete,
@ -114,6 +115,7 @@ class EntryActions {
EntryAction.videoSetSpeed, EntryAction.videoSetSpeed,
EntryAction.videoSelectStreams, EntryAction.videoSelectStreams,
EntryAction.videoSettings, EntryAction.videoSettings,
EntryAction.lockViewer,
]; ];
static const videoPlayback = [ static const videoPlayback = [
@ -178,6 +180,8 @@ extension ExtraEntryAction on EntryAction {
case EntryAction.viewSource: case EntryAction.viewSource:
return context.l10n.entryActionViewSource; return context.l10n.entryActionViewSource;
// video // video
case EntryAction.lockViewer:
return context.l10n.viewerActionLock;
case EntryAction.videoCaptureFrame: case EntryAction.videoCaptureFrame:
return context.l10n.videoActionCaptureFrame; return context.l10n.videoActionCaptureFrame;
case EntryAction.videoToggleMute: case EntryAction.videoToggleMute:
@ -290,6 +294,8 @@ extension ExtraEntryAction on EntryAction {
case EntryAction.viewSource: case EntryAction.viewSource:
return AIcons.vector; return AIcons.vector;
// video // video
case EntryAction.lockViewer:
return AIcons.viewerLock;
case EntryAction.videoCaptureFrame: case EntryAction.videoCaptureFrame:
return AIcons.captureFrame; return AIcons.captureFrame;
case EntryAction.videoToggleMute: case EntryAction.videoToggleMute:

View file

@ -0,0 +1,26 @@
import 'package:aves/model/settings/enums/enums.dart';
import 'package:aves/services/accessibility_service.dart';
import 'package:aves/theme/durations.dart';
extension ExtraAccessibilityTimeout on AccessibilityTimeout {
Future<Duration> getSnackBarDuration(bool hasAction) async {
switch (this) {
case AccessibilityTimeout.system:
if (hasAction) {
return Duration(milliseconds: await (AccessibilityService.getRecommendedTimeToTakeAction(Durations.opToastActionDisplay)));
} else {
return Duration(milliseconds: await (AccessibilityService.getRecommendedTimeToRead(Durations.opToastTextDisplay)));
}
case AccessibilityTimeout.s1:
return const Duration(seconds: 1);
case AccessibilityTimeout.s3:
return const Duration(seconds: 3);
case AccessibilityTimeout.s5:
return const Duration(seconds: 5);
case AccessibilityTimeout.s10:
return const Duration(seconds: 10);
case AccessibilityTimeout.s30:
return const Duration(seconds: 30);
}
}
}

View file

@ -139,6 +139,8 @@ class AIcons {
static const IconData vaultConfigure = MdiIcons.shieldLockOutline; static const IconData vaultConfigure = MdiIcons.shieldLockOutline;
static const IconData videoSettings = Icons.video_settings_outlined; static const IconData videoSettings = Icons.video_settings_outlined;
static const IconData view = Icons.grid_view_outlined; static const IconData view = Icons.grid_view_outlined;
static const IconData viewerLock = Icons.lock_outline;
static const IconData viewerUnlock = Icons.lock_open_outlined;
static const IconData zoomIn = Icons.add_outlined; static const IconData zoomIn = Icons.add_outlined;
static const IconData zoomOut = Icons.remove_outlined; static const IconData zoomOut = Icons.remove_outlined;
static const IconData collapse = Icons.expand_less_outlined; static const IconData collapse = Icons.expand_less_outlined;

View file

@ -2,9 +2,8 @@ import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart';
import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/enums/accessibility_timeout.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/services/accessibility_service.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/action_mixins/overlay_snack_bar.dart'; import 'package:aves/widgets/common/action_mixins/overlay_snack_bar.dart';
@ -38,7 +37,7 @@ mixin FeedbackMixin {
// provide the messenger if feedback happens as the widget is disposed // provide the messenger if feedback happens as the widget is disposed
void showFeedbackWithMessenger(BuildContext context, ScaffoldMessengerState messenger, String message, [SnackBarAction? action]) { void showFeedbackWithMessenger(BuildContext context, ScaffoldMessengerState messenger, String message, [SnackBarAction? action]) {
_getSnackBarDuration(action != null).then((duration) { settings.timeToTakeAction.getSnackBarDuration(action != null).then((duration) {
final start = DateTime.now(); final start = DateTime.now();
final theme = Theme.of(context); final theme = Theme.of(context);
final snackBarTheme = theme.snackBarTheme; final snackBarTheme = theme.snackBarTheme;
@ -107,27 +106,6 @@ mixin FeedbackMixin {
return horizontalPadding; return horizontalPadding;
} }
Future<Duration> _getSnackBarDuration(bool hasAction) async {
switch (settings.timeToTakeAction) {
case AccessibilityTimeout.system:
if (hasAction) {
return Duration(milliseconds: await (AccessibilityService.getRecommendedTimeToTakeAction(Durations.opToastActionDisplay)));
} else {
return Duration(milliseconds: await (AccessibilityService.getRecommendedTimeToRead(Durations.opToastTextDisplay)));
}
case AccessibilityTimeout.s1:
return const Duration(seconds: 1);
case AccessibilityTimeout.s3:
return const Duration(seconds: 3);
case AccessibilityTimeout.s5:
return const Duration(seconds: 5);
case AccessibilityTimeout.s10:
return const Duration(seconds: 10);
case AccessibilityTimeout.s30:
return const Duration(seconds: 30);
}
}
// report overlay for multiple operations // report overlay for multiple operations
Future<void> showOpReport<T>({ Future<void> showOpReport<T>({

View file

@ -2,6 +2,7 @@ import 'package:aves/model/actions/entry.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/settings/common/quick_actions/editor_page.dart'; import 'package:aves/widgets/settings/common/quick_actions/editor_page.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ViewerActionEditorPage extends StatelessWidget { class ViewerActionEditorPage extends StatelessWidget {
@ -9,7 +10,7 @@ class ViewerActionEditorPage extends StatelessWidget {
const ViewerActionEditorPage({super.key}); const ViewerActionEditorPage({super.key});
static const allAvailableActions = [ static final allAvailableActions = [
[ [
EntryAction.share, EntryAction.share,
EntryAction.edit, EntryAction.edit,
@ -26,10 +27,7 @@ class ViewerActionEditorPage extends StatelessWidget {
], ],
[ [
...EntryActions.exportInternal, ...EntryActions.exportInternal,
EntryAction.videoCaptureFrame, ...EntryActions.video.whereNot((v) => v == EntryAction.videoSettings),
EntryAction.videoToggleMute,
EntryAction.videoSetSpeed,
EntryAction.videoSelectStreams,
], ],
EntryActions.commonMetadataActions, EntryActions.commonMetadataActions,
]; ];

View file

@ -92,6 +92,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
return targetEntry.isSvg; return targetEntry.isSvg;
case EntryAction.videoCaptureFrame: case EntryAction.videoCaptureFrame:
return canWrite && targetEntry.isVideo; return canWrite && targetEntry.isVideo;
case EntryAction.lockViewer:
case EntryAction.videoToggleMute: case EntryAction.videoToggleMute:
return !settings.useTvLayout && targetEntry.isVideo; return !settings.useTvLayout && targetEntry.isVideo;
case EntryAction.videoSelectStreams: case EntryAction.videoSelectStreams:
@ -235,6 +236,9 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
case EntryAction.viewSource: case EntryAction.viewSource:
_goToSourceViewer(context, targetEntry); _goToSourceViewer(context, targetEntry);
break; break;
case EntryAction.lockViewer:
const LockViewNotification(locked: true).dispatch(context);
break;
// video // video
case EntryAction.videoCaptureFrame: case EntryAction.videoCaptureFrame:
case EntryAction.videoToggleMute: case EntryAction.videoToggleMute:

View file

@ -6,6 +6,13 @@ import 'package:aves_video/aves_video.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@immutable
class LockViewNotification extends Notification {
final bool locked;
const LockViewNotification({required this.locked});
}
@immutable @immutable
class PopVisualNotification extends Notification {} class PopVisualNotification extends Notification {}

View file

@ -11,6 +11,7 @@ import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/trash.dart'; import 'package:aves/model/filters/trash.dart';
import 'package:aves/model/highlight.dart'; import 'package:aves/model/highlight.dart';
import 'package:aves/model/settings/enums/accessibility_timeout.dart';
import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/enums/enums.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_lens.dart';
@ -27,6 +28,7 @@ import 'package:aves/widgets/viewer/entry_vertical_pager.dart';
import 'package:aves/widgets/viewer/hero.dart'; import 'package:aves/widgets/viewer/hero.dart';
import 'package:aves/widgets/viewer/multipage/conductor.dart'; import 'package:aves/widgets/viewer/multipage/conductor.dart';
import 'package:aves/widgets/viewer/overlay/bottom.dart'; import 'package:aves/widgets/viewer/overlay/bottom.dart';
import 'package:aves/widgets/viewer/overlay/locked.dart';
import 'package:aves/widgets/viewer/overlay/panorama.dart'; import 'package:aves/widgets/viewer/overlay/panorama.dart';
import 'package:aves/widgets/viewer/overlay/slideshow_buttons.dart'; import 'package:aves/widgets/viewer/overlay/slideshow_buttons.dart';
import 'package:aves/widgets/viewer/overlay/top.dart'; import 'package:aves/widgets/viewer/overlay/top.dart';
@ -70,6 +72,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
final AChangeNotifier _verticalScrollNotifier = AChangeNotifier(); final AChangeNotifier _verticalScrollNotifier = AChangeNotifier();
bool _overlayInitialized = false; bool _overlayInitialized = false;
final ValueNotifier<bool> _overlayVisible = ValueNotifier(true); final ValueNotifier<bool> _overlayVisible = ValueNotifier(true);
final ValueNotifier<bool> _viewLocked = ValueNotifier(false);
final ValueNotifier<bool> _overlayExpandedNotifier = ValueNotifier(false); final ValueNotifier<bool> _overlayExpandedNotifier = ValueNotifier(false);
late AnimationController _overlayAnimationController; late AnimationController _overlayAnimationController;
late Animation<double> _overlayButtonScale, _overlayVideoControlScale, _overlayOpacity; late Animation<double> _overlayButtonScale, _overlayVideoControlScale, _overlayOpacity;
@ -78,6 +81,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
late VideoActionDelegate _videoActionDelegate; late VideoActionDelegate _videoActionDelegate;
final ValueNotifier<HeroInfo?> _heroInfoNotifier = ValueNotifier(null); final ValueNotifier<HeroInfo?> _heroInfoNotifier = ValueNotifier(null);
bool _isEntryTracked = true; bool _isEntryTracked = true;
Timer? _overlayHidingTimer;
@override @override
bool get isViewingImage => _currentVerticalPage.value == imagePage; bool get isViewingImage => _currentVerticalPage.value == imagePage;
@ -147,6 +151,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
)); ));
_overlayVisible.value = settings.showOverlayOnOpening && !viewerController.autopilot; _overlayVisible.value = settings.showOverlayOnOpening && !viewerController.autopilot;
_overlayVisible.addListener(_onOverlayVisibleChanged); _overlayVisible.addListener(_onOverlayVisibleChanged);
_viewLocked.addListener(_onViewLockedChanged);
_videoActionDelegate = VideoActionDelegate( _videoActionDelegate = VideoActionDelegate(
collection: collection, collection: collection,
); );
@ -170,9 +175,11 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
_videoActionDelegate.dispose(); _videoActionDelegate.dispose();
_overlayAnimationController.dispose(); _overlayAnimationController.dispose();
_overlayVisible.dispose(); _overlayVisible.dispose();
_viewLocked.dispose();
_overlayExpandedNotifier.dispose(); _overlayExpandedNotifier.dispose();
_verticalPager.dispose(); _verticalPager.dispose();
_heroInfoNotifier.dispose(); _heroInfoNotifier.dispose();
_stopOverlayHidingTimer();
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
_unregisterWidget(widget); _unregisterWidget(widget);
super.dispose(); super.dispose();
@ -250,16 +257,33 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
stream: device.supportPictureInPicture ? _floating.pipStatus$ : Stream.value(PiPStatus.disabled), stream: device.supportPictureInPicture ? _floating.pipStatus$ : Stream.value(PiPStatus.disabled),
builder: (context, snapshot) { builder: (context, snapshot) {
var pipEnabled = snapshot.data == PiPStatus.enabled; var pipEnabled = snapshot.data == PiPStatus.enabled;
return Stack( return ValueListenableBuilder<bool>(
children: [ valueListenable: _viewLocked,
viewer, builder: (context, locked, child) {
if (!pipEnabled) ...[ return Stack(
..._buildOverlays(availableSize).map(_decorateOverlay), children: [
const TopGestureAreaProtector(), child!,
const SideGestureAreaProtector(), if (!pipEnabled) ...[
const BottomGestureAreaProtector(), if (locked) ...[
], const Positioned.fill(
], child: AbsorbPointer(),
),
Positioned.fill(
child: GestureDetector(
onTap: () => _overlayVisible.value = !_overlayVisible.value,
),
),
_buildViewerLockedBottomOverlay(),
] else
..._buildOverlays(availableSize).map(_decorateOverlay),
const TopGestureAreaProtector(),
const SideGestureAreaProtector(),
const BottomGestureAreaProtector(),
],
],
);
},
child: viewer,
); );
}, },
); );
@ -301,6 +325,17 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
} }
} }
Widget _buildViewerLockedBottomOverlay() {
return TooltipTheme(
data: TooltipTheme.of(context).copyWith(
preferBelow: false,
),
child: ViewerLockedOverlay(
animationController: _overlayAnimationController,
),
);
}
Widget _buildSlideshowBottomOverlay(Size availableSize) { Widget _buildSlideshowBottomOverlay(Size availableSize) {
return SizedBox.fromSize( return SizedBox.fromSize(
size: availableSize, size: availableSize,
@ -491,6 +526,8 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
} }
} else if (notification is ToggleOverlayNotification) { } else if (notification is ToggleOverlayNotification) {
_overlayVisible.value = notification.visible ?? !_overlayVisible.value; _overlayVisible.value = notification.visible ?? !_overlayVisible.value;
} else if (notification is LockViewNotification) {
_viewLocked.value = notification.locked;
} else if (notification is VideoActionNotification) { } else if (notification is VideoActionNotification) {
_onVideoAction( _onVideoAction(
context: context, context: context,
@ -809,8 +846,12 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
Future<void> _onOverlayVisibleChanged({bool animate = true}) async { Future<void> _onOverlayVisibleChanged({bool animate = true}) async {
if (!mounted) return; if (!mounted) return;
if (_overlayVisible.value) { if (_overlayVisible.value) {
await AvesApp.showSystemUI(); if (_viewLocked.value) {
AvesApp.setSystemUIStyle(Theme.of(context)); await _startOverlayHidingTimer();
} else {
await AvesApp.showSystemUI();
AvesApp.setSystemUIStyle(Theme.of(context));
}
if (animate) { if (animate) {
await _overlayAnimationController.forward(); await _overlayAnimationController.forward();
} else { } else {
@ -835,4 +876,24 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
}); });
} }
} }
Future<void> _onViewLockedChanged() async {
if (_viewLocked.value) {
await AvesApp.hideSystemUI();
await _startOverlayHidingTimer();
} else {
await AvesApp.showSystemUI();
AvesApp.setSystemUIStyle(Theme.of(context));
_stopOverlayHidingTimer();
_overlayVisible.value = true;
}
}
Future<void> _startOverlayHidingTimer() async {
_stopOverlayHidingTimer();
final duration = await settings.timeToTakeAction.getSnackBarDuration(true);
_overlayHidingTimer = Timer(duration, () => _overlayVisible.value = false);
}
void _stopOverlayHidingTimer() => _overlayHidingTimer?.cancel();
} }

View file

@ -0,0 +1,90 @@
import 'dart:math';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/extensions/media_query.dart';
import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:aves/widgets/viewer/controls/notifications.dart';
import 'package:aves/widgets/viewer/overlay/viewer_buttons.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ViewerLockedOverlay extends StatefulWidget {
final AnimationController animationController;
final EdgeInsets? viewInsets, viewPadding;
const ViewerLockedOverlay({
super.key,
required this.animationController,
this.viewInsets,
this.viewPadding,
});
@override
State<StatefulWidget> createState() => _ViewerLockedOverlayState();
}
class _ViewerLockedOverlayState extends State<ViewerLockedOverlay> {
late Animation<double> _buttonScale;
@override
void initState() {
super.initState();
_registerWidget(widget);
}
@override
void didUpdateWidget(covariant ViewerLockedOverlay oldWidget) {
super.didUpdateWidget(oldWidget);
_unregisterWidget(oldWidget);
_registerWidget(widget);
}
@override
void dispose() {
_unregisterWidget(widget);
super.dispose();
}
void _registerWidget(ViewerLockedOverlay widget) {
_buttonScale = CurvedAnimation(
parent: widget.animationController,
// a little bounce at the top
curve: Curves.easeOutBack,
);
}
void _unregisterWidget(ViewerLockedOverlay widget) {
// nothing
}
@override
Widget build(BuildContext context) {
return Selector<MediaQueryData, double>(
selector: (context, mq) => max(mq.effectiveBottomPadding, mq.systemGestureInsets.bottom),
builder: (context, mqPaddingBottom, child) {
final viewInsetsPadding = (widget.viewInsets ?? EdgeInsets.zero) + (widget.viewPadding ?? EdgeInsets.zero);
return Container(
alignment: AlignmentDirectional.bottomEnd,
padding: EdgeInsets.only(bottom: mqPaddingBottom) + const EdgeInsets.all(ViewerButtonRowContent.padding),
child: SafeArea(
top: false,
bottom: false,
minimum: EdgeInsets.only(
left: viewInsetsPadding.left,
right: viewInsetsPadding.right,
),
child: OverlayButton(
scale: _buttonScale,
child: IconButton(
icon: const Icon(AIcons.viewerUnlock),
onPressed: () => const LockViewNotification(locked: false).dispatch(context),
tooltip: context.l10n.viewerActionUnlock,
),
),
),
);
},
);
}
}

View file

@ -62,6 +62,8 @@
"videoActionSelectStreams", "videoActionSelectStreams",
"videoActionSetSpeed", "videoActionSetSpeed",
"viewerActionSettings", "viewerActionSettings",
"viewerActionLock",
"viewerActionUnlock",
"slideshowActionResume", "slideshowActionResume",
"slideshowActionShowInCollection", "slideshowActionShowInCollection",
"entryInfoActionEditDate", "entryInfoActionEditDate",
@ -623,6 +625,8 @@
"videoActionSelectStreams", "videoActionSelectStreams",
"videoActionSetSpeed", "videoActionSetSpeed",
"viewerActionSettings", "viewerActionSettings",
"viewerActionLock",
"viewerActionUnlock",
"slideshowActionResume", "slideshowActionResume",
"slideshowActionShowInCollection", "slideshowActionShowInCollection",
"entryInfoActionEditDate", "entryInfoActionEditDate",
@ -1181,6 +1185,8 @@
], ],
"cs": [ "cs": [
"viewerActionLock",
"viewerActionUnlock",
"settingsVideoEnablePip", "settingsVideoEnablePip",
"settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsTile",
"settingsCollectionBurstPatternsNone", "settingsCollectionBurstPatternsNone",
@ -1194,6 +1200,8 @@
"chipActionLock", "chipActionLock",
"chipActionCreateVault", "chipActionCreateVault",
"chipActionConfigureVault", "chipActionConfigureVault",
"viewerActionLock",
"viewerActionUnlock",
"albumTierVaults", "albumTierVaults",
"lengthUnitPixel", "lengthUnitPixel",
"lengthUnitPercent", "lengthUnitPercent",
@ -1230,6 +1238,8 @@
"el": [ "el": [
"chipActionGoToPlacePage", "chipActionGoToPlacePage",
"viewerActionLock",
"viewerActionUnlock",
"vaultLockTypePattern", "vaultLockTypePattern",
"settingsVideoEnablePip", "settingsVideoEnablePip",
"patternDialogEnter", "patternDialogEnter",
@ -1246,12 +1256,16 @@
], ],
"es": [ "es": [
"viewerActionLock",
"viewerActionUnlock",
"settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsTile",
"settingsCollectionBurstPatternsNone", "settingsCollectionBurstPatternsNone",
"tagPlaceholderState" "tagPlaceholderState"
], ],
"eu": [ "eu": [
"viewerActionLock",
"viewerActionUnlock",
"settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsTile",
"settingsCollectionBurstPatternsNone", "settingsCollectionBurstPatternsNone",
"tagPlaceholderState" "tagPlaceholderState"
@ -1266,6 +1280,8 @@
"videoActionPause", "videoActionPause",
"videoActionPlay", "videoActionPlay",
"videoActionSelectStreams", "videoActionSelectStreams",
"viewerActionLock",
"viewerActionUnlock",
"slideshowActionResume", "slideshowActionResume",
"filterAspectRatioLandscapeLabel", "filterAspectRatioLandscapeLabel",
"filterAspectRatioPortraitLabel", "filterAspectRatioPortraitLabel",
@ -1735,6 +1751,8 @@
], ],
"fr": [ "fr": [
"viewerActionLock",
"viewerActionUnlock",
"tagPlaceholderState" "tagPlaceholderState"
], ],
@ -1746,6 +1764,8 @@
"chipActionConfigureVault", "chipActionConfigureVault",
"entryActionShareImageOnly", "entryActionShareImageOnly",
"entryActionShareVideoOnly", "entryActionShareVideoOnly",
"viewerActionLock",
"viewerActionUnlock",
"entryInfoActionExportMetadata", "entryInfoActionExportMetadata",
"entryInfoActionRemoveLocation", "entryInfoActionRemoveLocation",
"filterAspectRatioLandscapeLabel", "filterAspectRatioLandscapeLabel",
@ -2328,6 +2348,8 @@
"videoActionSelectStreams", "videoActionSelectStreams",
"videoActionSetSpeed", "videoActionSetSpeed",
"viewerActionSettings", "viewerActionSettings",
"viewerActionLock",
"viewerActionUnlock",
"slideshowActionResume", "slideshowActionResume",
"slideshowActionShowInCollection", "slideshowActionShowInCollection",
"entryInfoActionEditDate", "entryInfoActionEditDate",
@ -2946,6 +2968,8 @@
"videoActionSelectStreams", "videoActionSelectStreams",
"videoActionSetSpeed", "videoActionSetSpeed",
"viewerActionSettings", "viewerActionSettings",
"viewerActionLock",
"viewerActionUnlock",
"slideshowActionResume", "slideshowActionResume",
"slideshowActionShowInCollection", "slideshowActionShowInCollection",
"entryInfoActionEditDate", "entryInfoActionEditDate",
@ -3504,12 +3528,16 @@
], ],
"id": [ "id": [
"viewerActionLock",
"viewerActionUnlock",
"settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsTile",
"settingsCollectionBurstPatternsNone", "settingsCollectionBurstPatternsNone",
"tagPlaceholderState" "tagPlaceholderState"
], ],
"it": [ "it": [
"viewerActionLock",
"viewerActionUnlock",
"settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsTile",
"settingsCollectionBurstPatternsNone", "settingsCollectionBurstPatternsNone",
"tagPlaceholderState" "tagPlaceholderState"
@ -3522,6 +3550,8 @@
"chipActionLock", "chipActionLock",
"chipActionCreateVault", "chipActionCreateVault",
"chipActionConfigureVault", "chipActionConfigureVault",
"viewerActionLock",
"viewerActionUnlock",
"filterAspectRatioLandscapeLabel", "filterAspectRatioLandscapeLabel",
"filterAspectRatioPortraitLabel", "filterAspectRatioPortraitLabel",
"filterNoAddressLabel", "filterNoAddressLabel",
@ -3572,6 +3602,8 @@
], ],
"ko": [ "ko": [
"viewerActionLock",
"viewerActionUnlock",
"tagPlaceholderState" "tagPlaceholderState"
], ],
@ -3581,6 +3613,8 @@
"chipActionLock", "chipActionLock",
"chipActionCreateVault", "chipActionCreateVault",
"chipActionConfigureVault", "chipActionConfigureVault",
"viewerActionLock",
"viewerActionUnlock",
"filterLocatedLabel", "filterLocatedLabel",
"filterTaggedLabel", "filterTaggedLabel",
"albumTierVaults", "albumTierVaults",
@ -3625,6 +3659,8 @@
], ],
"nb": [ "nb": [
"viewerActionLock",
"viewerActionUnlock",
"vaultLockTypePattern", "vaultLockTypePattern",
"settingsVideoEnablePip", "settingsVideoEnablePip",
"patternDialogEnter", "patternDialogEnter",
@ -3644,6 +3680,8 @@
"chipActionConfigureVault", "chipActionConfigureVault",
"entryActionShareImageOnly", "entryActionShareImageOnly",
"entryActionShareVideoOnly", "entryActionShareVideoOnly",
"viewerActionLock",
"viewerActionUnlock",
"entryInfoActionExportMetadata", "entryInfoActionExportMetadata",
"entryInfoActionRemoveLocation", "entryInfoActionRemoveLocation",
"filterAspectRatioLandscapeLabel", "filterAspectRatioLandscapeLabel",
@ -3707,6 +3745,8 @@
"chipActionLock", "chipActionLock",
"chipActionCreateVault", "chipActionCreateVault",
"chipActionConfigureVault", "chipActionConfigureVault",
"viewerActionLock",
"viewerActionUnlock",
"entryInfoActionRemoveLocation", "entryInfoActionRemoveLocation",
"filterLocatedLabel", "filterLocatedLabel",
"filterNoLocationLabel", "filterNoLocationLabel",
@ -4024,12 +4064,16 @@
], ],
"pl": [ "pl": [
"viewerActionLock",
"viewerActionUnlock",
"settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsTile",
"settingsCollectionBurstPatternsNone", "settingsCollectionBurstPatternsNone",
"tagPlaceholderState" "tagPlaceholderState"
], ],
"pt": [ "pt": [
"viewerActionLock",
"viewerActionUnlock",
"settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsTile",
"settingsCollectionBurstPatternsNone", "settingsCollectionBurstPatternsNone",
"settingsVideoBackgroundModeDialogTitle", "settingsVideoBackgroundModeDialogTitle",
@ -4037,6 +4081,8 @@
], ],
"ro": [ "ro": [
"viewerActionLock",
"viewerActionUnlock",
"settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsTile",
"settingsCollectionBurstPatternsNone", "settingsCollectionBurstPatternsNone",
"tagPlaceholderState" "tagPlaceholderState"
@ -4044,6 +4090,8 @@
"ru": [ "ru": [
"chipActionLock", "chipActionLock",
"viewerActionLock",
"viewerActionUnlock",
"vaultLockTypePattern", "vaultLockTypePattern",
"settingsVideoEnablePip", "settingsVideoEnablePip",
"patternDialogEnter", "patternDialogEnter",
@ -4071,6 +4119,8 @@
"chipActionLock", "chipActionLock",
"chipActionCreateVault", "chipActionCreateVault",
"chipActionConfigureVault", "chipActionConfigureVault",
"viewerActionLock",
"viewerActionUnlock",
"filterLocatedLabel", "filterLocatedLabel",
"filterNoLocationLabel", "filterNoLocationLabel",
"albumTierVaults", "albumTierVaults",
@ -4501,6 +4551,8 @@
"chipActionLock", "chipActionLock",
"chipActionCreateVault", "chipActionCreateVault",
"chipActionConfigureVault", "chipActionConfigureVault",
"viewerActionLock",
"viewerActionUnlock",
"albumTierVaults", "albumTierVaults",
"lengthUnitPixel", "lengthUnitPixel",
"lengthUnitPercent", "lengthUnitPercent",
@ -4855,6 +4907,8 @@
"chipActionLock", "chipActionLock",
"chipActionCreateVault", "chipActionCreateVault",
"chipActionConfigureVault", "chipActionConfigureVault",
"viewerActionLock",
"viewerActionUnlock",
"albumTierVaults", "albumTierVaults",
"lengthUnitPixel", "lengthUnitPixel",
"lengthUnitPercent", "lengthUnitPercent",
@ -4890,6 +4944,8 @@
], ],
"uk": [ "uk": [
"viewerActionLock",
"viewerActionUnlock",
"settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsTile",
"settingsCollectionBurstPatternsNone", "settingsCollectionBurstPatternsNone",
"tagPlaceholderState" "tagPlaceholderState"
@ -4900,6 +4956,8 @@
"chipActionLock", "chipActionLock",
"chipActionCreateVault", "chipActionCreateVault",
"chipActionConfigureVault", "chipActionConfigureVault",
"viewerActionLock",
"viewerActionUnlock",
"filterLocatedLabel", "filterLocatedLabel",
"filterTaggedLabel", "filterTaggedLabel",
"albumTierVaults", "albumTierVaults",
@ -4948,6 +5006,8 @@
"chipActionLock", "chipActionLock",
"chipActionCreateVault", "chipActionCreateVault",
"chipActionConfigureVault", "chipActionConfigureVault",
"viewerActionLock",
"viewerActionUnlock",
"filterLocatedLabel", "filterLocatedLabel",
"filterTaggedLabel", "filterTaggedLabel",
"albumTierVaults", "albumTierVaults",