Add a dialog that lead user to the video preview help page

This commit is contained in:
Ming Ming 2024-06-21 00:32:41 +08:00
parent bd0e4fd342
commit 4fc6b17de1
15 changed files with 180 additions and 27 deletions

View file

@ -152,6 +152,12 @@ class PrefController {
value: value, value: value,
); );
Future<bool> setDontShowVideoPreviewHint(bool value) => _set<bool>(
controller: _isDontShowVideoPreviewHintController,
setter: (pref, value) => pref.setDontShowVideoPreviewHint(value),
value: value,
);
Future<bool> _set<T>({ Future<bool> _set<T>({
required BehaviorSubject<T> controller, required BehaviorSubject<T> controller,
required Future<bool> Function(Pref pref, T value) setter, required Future<bool> Function(Pref pref, T value) setter,
@ -249,6 +255,9 @@ class PrefController {
@NpSubjectAccessor(type: "Color?") @NpSubjectAccessor(type: "Color?")
late final _secondarySeedColorController = BehaviorSubject<Color?>.seeded( late final _secondarySeedColorController = BehaviorSubject<Color?>.seeded(
_c.pref.getSecondarySeedColor()?.run(Color.new)); _c.pref.getSecondarySeedColor()?.run(Color.new));
@npSubjectAccessor
late final _isDontShowVideoPreviewHintController =
BehaviorSubject.seeded(_c.pref.isDontShowVideoPreviewHintOr(false));
} }
@npSubjectAccessor @npSubjectAccessor

View file

@ -148,6 +148,15 @@ extension $PrefControllerNpSubjectAccessor on PrefController {
Stream<Color?> get secondarySeedColorChange => Stream<Color?> get secondarySeedColorChange =>
secondarySeedColor.distinct().skip(1); secondarySeedColor.distinct().skip(1);
Color? get secondarySeedColorValue => _secondarySeedColorController.value; Color? get secondarySeedColorValue => _secondarySeedColorController.value;
// _isDontShowVideoPreviewHintController
ValueStream<bool> get isDontShowVideoPreviewHint =>
_isDontShowVideoPreviewHintController.stream;
Stream<bool> get isDontShowVideoPreviewHintNew =>
isDontShowVideoPreviewHint.skip(1);
Stream<bool> get isDontShowVideoPreviewHintChange =>
isDontShowVideoPreviewHint.distinct().skip(1);
bool get isDontShowVideoPreviewHintValue =>
_isDontShowVideoPreviewHintController.value;
} }
extension $SecurePrefControllerNpSubjectAccessor on SecurePrefController { extension $SecurePrefControllerNpSubjectAccessor on SecurePrefController {

View file

@ -81,4 +81,11 @@ extension on Pref {
return provider.setString(PrefKey.protectedPageAuthPassword, value); return provider.setString(PrefKey.protectedPageAuthPassword, value);
} }
} }
bool? isDontShowVideoPreviewHint() =>
provider.getBool(PrefKey.dontShowVideoPreviewHint);
bool isDontShowVideoPreviewHintOr(bool def) =>
isDontShowVideoPreviewHint() ?? def;
Future<bool> setDontShowVideoPreviewHint(bool value) =>
provider.setBool(PrefKey.dontShowVideoPreviewHint, value);
} }

View file

@ -112,6 +112,7 @@ enum PrefKey implements PrefKeyInterface {
protectedPageAuthType, protectedPageAuthType,
protectedPageAuthPin, protectedPageAuthPin,
protectedPageAuthPassword, protectedPageAuthPassword,
dontShowVideoPreviewHint,
; ;
@override @override
@ -196,6 +197,8 @@ enum PrefKey implements PrefKeyInterface {
return "protectedPageAuthPin"; return "protectedPageAuthPin";
case PrefKey.protectedPageAuthPassword: case PrefKey.protectedPageAuthPassword:
return "protectedPageAuthPassword"; return "protectedPageAuthPassword";
case PrefKey.dontShowVideoPreviewHint:
return "dontShowVideoPreviewHint";
} }
} }
} }

View file

@ -13,3 +13,4 @@ const enhanceRetouchUrl = "https://bit.ly/3Ds2cea";
const editPhotosUrl = "https://bit.ly/3v82oKA"; const editPhotosUrl = "https://bit.ly/3v82oKA";
const collectionTypesUrl = "https://bit.ly/3OwSiNq"; const collectionTypesUrl = "https://bit.ly/3OwSiNq";
const contributorsUrl = "https://bit.ly/3QhlQQs"; const contributorsUrl = "https://bit.ly/3QhlQQs";
const videoPreviewUrl = "https://bit.ly/4c7cazP";

View file

@ -1485,6 +1485,8 @@
"description": "There's no HTTPS server in the account list" "description": "There's no HTTPS server in the account list"
}, },
"trustedCertManagerFailedToRemoveCertError": "Failed to remove certificate", "trustedCertManagerFailedToRemoveCertError": "Failed to remove certificate",
"missingVideoThumbnailHelpDialogTitle": "Having trouble with video thumbnails?",
"dontShowAgain": "Don't show again",
"errorUnauthenticated": "Unauthenticated access. Please sign-in again if the problem continues", "errorUnauthenticated": "Unauthenticated access. Please sign-in again if the problem continues",
"@errorUnauthenticated": { "@errorUnauthenticated": {

View file

@ -247,6 +247,8 @@
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError", "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain",
"errorUnauthenticated", "errorUnauthenticated",
"errorDisconnected", "errorDisconnected",
"errorLocked", "errorLocked",
@ -280,7 +282,9 @@
"trustedCertManagerAlreadyTrustedError", "trustedCertManagerAlreadyTrustedError",
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError" "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain"
], ],
"de": [ "de": [
@ -332,7 +336,9 @@
"trustedCertManagerAlreadyTrustedError", "trustedCertManagerAlreadyTrustedError",
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError" "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain"
], ],
"el": [ "el": [
@ -467,7 +473,9 @@
"trustedCertManagerAlreadyTrustedError", "trustedCertManagerAlreadyTrustedError",
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError" "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain"
], ],
"es": [ "es": [
@ -493,7 +501,9 @@
"trustedCertManagerAlreadyTrustedError", "trustedCertManagerAlreadyTrustedError",
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError" "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain"
], ],
"fi": [ "fi": [
@ -519,7 +529,9 @@
"trustedCertManagerAlreadyTrustedError", "trustedCertManagerAlreadyTrustedError",
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError" "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain"
], ],
"fr": [ "fr": [
@ -545,7 +557,9 @@
"trustedCertManagerAlreadyTrustedError", "trustedCertManagerAlreadyTrustedError",
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError" "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain"
], ],
"it": [ "it": [
@ -576,7 +590,9 @@
"trustedCertManagerAlreadyTrustedError", "trustedCertManagerAlreadyTrustedError",
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError" "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain"
], ],
"nl": [ "nl": [
@ -944,6 +960,8 @@
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError", "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain",
"errorUnauthenticated", "errorUnauthenticated",
"errorDisconnected", "errorDisconnected",
"errorLocked", "errorLocked",
@ -981,7 +999,9 @@
"trustedCertManagerAlreadyTrustedError", "trustedCertManagerAlreadyTrustedError",
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError" "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain"
], ],
"pt": [ "pt": [
@ -1027,7 +1047,9 @@
"trustedCertManagerAlreadyTrustedError", "trustedCertManagerAlreadyTrustedError",
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError" "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain"
], ],
"ru": [ "ru": [
@ -1053,7 +1075,9 @@
"trustedCertManagerAlreadyTrustedError", "trustedCertManagerAlreadyTrustedError",
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError" "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain"
], ],
"tr": [ "tr": [
@ -1062,7 +1086,9 @@
"trustedCertManagerAlreadyTrustedError", "trustedCertManagerAlreadyTrustedError",
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError" "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain"
], ],
"zh": [ "zh": [
@ -1119,7 +1145,9 @@
"trustedCertManagerAlreadyTrustedError", "trustedCertManagerAlreadyTrustedError",
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError" "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain"
], ],
"zh_Hant": [ "zh_Hant": [
@ -1270,6 +1298,8 @@
"trustedCertManagerAlreadyTrustedError", "trustedCertManagerAlreadyTrustedError",
"trustedCertManagerSelectServer", "trustedCertManagerSelectServer",
"trustedCertManagerNoHttpsServerError", "trustedCertManagerNoHttpsServerError",
"trustedCertManagerFailedToRemoveCertError" "trustedCertManagerFailedToRemoveCertError",
"missingVideoThumbnailHelpDialogTitle",
"dontShowAgain"
] ]
} }

View file

@ -22,5 +22,7 @@ class SessionStorage {
DateTime lastSuspendTime = clock.now(); DateTime lastSuspendTime = clock.now();
bool hasShownVideoPreviewHint = false;
static final _inst = SessionStorage._(); static final _inst = SessionStorage._();
} }

View file

@ -50,6 +50,8 @@ class _Bloc extends Bloc<_Event, _State>
on<_UpdateDateTimeGroup>(_onUpdateDateTimeGroup); on<_UpdateDateTimeGroup>(_onUpdateDateTimeGroup);
on<_UpdateMemories>(_onUpdateMemories); on<_UpdateMemories>(_onUpdateMemories);
on<_TripMissingVideoPreview>(_onTripMissingVideoPreview);
on<_SetError>(_onSetError); on<_SetError>(_onSetError);
_subscriptions _subscriptions
@ -486,6 +488,14 @@ class _Bloc extends Bloc<_Event, _State>
)); ));
} }
void _onTripMissingVideoPreview(
_TripMissingVideoPreview ev, Emitter<_State> emit) {
// _log.info(ev);
if (!state.hasMissingVideoPreview) {
emit(state.copyWith(hasMissingVideoPreview: true));
}
}
void _onSetError(_SetError ev, Emitter<_State> emit) { void _onSetError(_SetError ev, Emitter<_State> emit) {
_log.info(ev); _log.info(ev);
emit(state.copyWith(error: ExceptionEvent(ev.error, ev.stackTrace))); emit(state.copyWith(error: ExceptionEvent(ev.error, ev.stackTrace)));

View file

@ -25,6 +25,7 @@ class _State {
this.minimapItems, this.minimapItems,
required this.minimapYRatio, required this.minimapYRatio,
this.scrollDate, this.scrollDate,
required this.hasMissingVideoPreview,
this.error, this.error,
}); });
@ -45,6 +46,7 @@ class _State {
isScrolling: false, isScrolling: false,
filesSummary: const DbFilesSummary(items: {}), filesSummary: const DbFilesSummary(items: {}),
minimapYRatio: 1, minimapYRatio: 1,
hasMissingVideoPreview: false,
); );
@override @override
@ -76,6 +78,8 @@ class _State {
final double minimapYRatio; final double minimapYRatio;
final Date? scrollDate; final Date? scrollDate;
final bool hasMissingVideoPreview;
final ExceptionEvent? error; final ExceptionEvent? error;
} }
@ -315,6 +319,14 @@ class _UpdateMemories implements _Event {
String toString() => _$toString(); String toString() => _$toString();
} }
@toString
class _TripMissingVideoPreview implements _Event {
const _TripMissingVideoPreview();
@override
String toString() => _$toString();
}
@toString @toString
class _SetError implements _Event { class _SetError implements _Event {
const _SetError(this.error, [this.stackTrace]); const _SetError(this.error, [this.stackTrace]);

View file

@ -72,6 +72,9 @@ class _VideoItem extends _FileItem {
account: account, account: account,
previewUrl: _previewUrl, previewUrl: _previewUrl,
isFavorite: file.fdIsFavorite, isFavorite: file.fdIsFavorite,
onError: () {
context.addEvent(const _TripMissingVideoPreview());
},
); );
} }

View file

@ -311,3 +311,30 @@ class _ScrollLabel extends StatelessWidget {
); );
} }
} }
class _VideoPreviewHintDialog extends StatelessWidget {
const _VideoPreviewHintDialog();
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(L10n.global().missingVideoThumbnailHelpDialogTitle),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
launch(help_util.videoPreviewUrl);
},
child: Text(L10n.global().learnMoreButtonLabel),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
context.read<PrefController>().setDontShowVideoPreviewHint(true);
},
child: Text(L10n.global().dontShowAgain),
),
],
);
}
}

View file

@ -33,13 +33,16 @@ import 'package:nc_photos/entity/file_util.dart' as file_util;
import 'package:nc_photos/event/event.dart'; import 'package:nc_photos/event/event.dart';
import 'package:nc_photos/exception_event.dart'; import 'package:nc_photos/exception_event.dart';
import 'package:nc_photos/flutter_util.dart' as flutter_util; import 'package:nc_photos/flutter_util.dart' as flutter_util;
import 'package:nc_photos/help_utils.dart' as help_util;
import 'package:nc_photos/k.dart' as k; import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/progress_util.dart'; import 'package:nc_photos/progress_util.dart';
import 'package:nc_photos/remote_storage_util.dart' as remote_storage_util; import 'package:nc_photos/remote_storage_util.dart' as remote_storage_util;
import 'package:nc_photos/session_storage.dart';
import 'package:nc_photos/snack_bar_manager.dart'; import 'package:nc_photos/snack_bar_manager.dart';
import 'package:nc_photos/stream_extension.dart'; import 'package:nc_photos/stream_extension.dart';
import 'package:nc_photos/theme.dart'; import 'package:nc_photos/theme.dart';
import 'package:nc_photos/theme/dimension.dart'; import 'package:nc_photos/theme/dimension.dart';
import 'package:nc_photos/url_launcher_util.dart';
import 'package:nc_photos/widget/collection_browser.dart'; import 'package:nc_photos/widget/collection_browser.dart';
import 'package:nc_photos/widget/collection_picker.dart'; import 'package:nc_photos/widget/collection_picker.dart';
import 'package:nc_photos/widget/file_sharer_dialog.dart'; import 'package:nc_photos/widget/file_sharer_dialog.dart';
@ -133,6 +136,23 @@ class _WrappedHomePhotosState extends State<_WrappedHomePhotos> {
}, },
child: MultiBlocListener( child: MultiBlocListener(
listeners: [ listeners: [
_BlocListenerT<bool>(
selector: (state) => state.hasMissingVideoPreview,
listener: (context, hasMissingVideoPreview) {
if (hasMissingVideoPreview) {
if (!context
.read<PrefController>()
.isDontShowVideoPreviewHintValue &&
!SessionStorage().hasShownVideoPreviewHint) {
SessionStorage().hasShownVideoPreviewHint = true;
showDialog(
context: context,
builder: (context) => const _VideoPreviewHintDialog(),
);
}
}
},
),
_BlocListenerT<ExceptionEvent?>( _BlocListenerT<ExceptionEvent?>(
selector: (state) => state.error, selector: (state) => state.error,
listener: (context, error) { listener: (context, error) {

View file

@ -35,6 +35,7 @@ abstract class $_StateCopyWithWorker {
List<_MinimapItem>? minimapItems, List<_MinimapItem>? minimapItems,
double? minimapYRatio, double? minimapYRatio,
Date? scrollDate, Date? scrollDate,
bool? hasMissingVideoPreview,
ExceptionEvent? error}); ExceptionEvent? error});
} }
@ -64,6 +65,7 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
dynamic minimapItems = copyWithNull, dynamic minimapItems = copyWithNull,
dynamic minimapYRatio, dynamic minimapYRatio,
dynamic scrollDate = copyWithNull, dynamic scrollDate = copyWithNull,
dynamic hasMissingVideoPreview,
dynamic error = copyWithNull}) { dynamic error = copyWithNull}) {
return _State( return _State(
files: files as List<FileDescriptor>? ?? that.files, files: files as List<FileDescriptor>? ?? that.files,
@ -102,6 +104,8 @@ class _$_StateCopyWithWorkerImpl implements $_StateCopyWithWorker {
minimapYRatio: minimapYRatio as double? ?? that.minimapYRatio, minimapYRatio: minimapYRatio as double? ?? that.minimapYRatio,
scrollDate: scrollDate:
scrollDate == copyWithNull ? that.scrollDate : scrollDate as Date?, scrollDate == copyWithNull ? that.scrollDate : scrollDate as Date?,
hasMissingVideoPreview:
hasMissingVideoPreview as bool? ?? that.hasMissingVideoPreview,
error: error == copyWithNull ? that.error : error as ExceptionEvent?); error: error == copyWithNull ? that.error : error as ExceptionEvent?);
} }
@ -180,7 +184,7 @@ extension _$_ContentListBodyNpLog on _ContentListBody {
extension _$_StateToString on _State { extension _$_StateToString on _State {
String _$toString() { String _$toString() {
// ignore: unnecessary_string_interpolations // ignore: unnecessary_string_interpolations
return "_State {files: [length: ${files.length}], isLoading: $isLoading, transformedItems: [length: ${transformedItems.length}], selectedItems: {length: ${selectedItems.length}}, filesSummary: $filesSummary, visibleDates: {length: ${visibleDates.length}}, queriedDates: {length: ${queriedDates.length}}, isEnableMemoryCollection: $isEnableMemoryCollection, memoryCollections: [length: ${memoryCollections.length}], contentListMaxExtent: ${contentListMaxExtent == null ? null : "${contentListMaxExtent!.toStringAsFixed(3)}"}, syncProgress: $syncProgress, zoom: $zoom, scale: ${scale == null ? null : "${scale!.toStringAsFixed(3)}"}, viewWidth: ${viewWidth == null ? null : "${viewWidth!.toStringAsFixed(3)}"}, viewHeight: ${viewHeight == null ? null : "${viewHeight!.toStringAsFixed(3)}"}, itemPerRow: $itemPerRow, itemSize: ${itemSize == null ? null : "${itemSize!.toStringAsFixed(3)}"}, isScrolling: $isScrolling, minimapItems: ${minimapItems == null ? null : "[length: ${minimapItems!.length}]"}, minimapYRatio: ${minimapYRatio.toStringAsFixed(3)}, scrollDate: $scrollDate, error: $error}"; return "_State {files: [length: ${files.length}], isLoading: $isLoading, transformedItems: [length: ${transformedItems.length}], selectedItems: {length: ${selectedItems.length}}, filesSummary: $filesSummary, visibleDates: {length: ${visibleDates.length}}, queriedDates: {length: ${queriedDates.length}}, isEnableMemoryCollection: $isEnableMemoryCollection, memoryCollections: [length: ${memoryCollections.length}], contentListMaxExtent: ${contentListMaxExtent == null ? null : "${contentListMaxExtent!.toStringAsFixed(3)}"}, syncProgress: $syncProgress, zoom: $zoom, scale: ${scale == null ? null : "${scale!.toStringAsFixed(3)}"}, viewWidth: ${viewWidth == null ? null : "${viewWidth!.toStringAsFixed(3)}"}, viewHeight: ${viewHeight == null ? null : "${viewHeight!.toStringAsFixed(3)}"}, itemPerRow: $itemPerRow, itemSize: ${itemSize == null ? null : "${itemSize!.toStringAsFixed(3)}"}, isScrolling: $isScrolling, minimapItems: ${minimapItems == null ? null : "[length: ${minimapItems!.length}]"}, minimapYRatio: ${minimapYRatio.toStringAsFixed(3)}, scrollDate: $scrollDate, hasMissingVideoPreview: $hasMissingVideoPreview, error: $error}";
} }
} }
@ -360,6 +364,13 @@ extension _$_UpdateMemoriesToString on _UpdateMemories {
} }
} }
extension _$_TripMissingVideoPreviewToString on _TripMissingVideoPreview {
String _$toString() {
// ignore: unnecessary_string_interpolations
return "_TripMissingVideoPreview {}";
}
}
extension _$_SetErrorToString on _SetError { extension _$_SetErrorToString on _SetError {
String _$toString() { String _$toString() {
// ignore: unnecessary_string_interpolations // ignore: unnecessary_string_interpolations

View file

@ -278,6 +278,7 @@ class PhotoListVideo extends StatelessWidget {
required this.account, required this.account,
required this.previewUrl, required this.previewUrl,
this.isFavorite = false, this.isFavorite = false,
this.onError,
}); });
@override @override
@ -293,24 +294,29 @@ class PhotoListVideo extends StatelessWidget {
child: NetworkRectThumbnail( child: NetworkRectThumbnail(
account: account, account: account,
imageUrl: previewUrl, imageUrl: previewUrl,
errorBuilder: (_) => Padding( errorBuilder: (context) {
padding: const EdgeInsets.all(12), onError?.call();
child: Icon( return Padding(
Icons.image_not_supported, padding: const EdgeInsets.all(12),
color: Theme.of(context).listPlaceholderForegroundColor, child: Icon(
), Icons.image_not_supported,
), color: Theme.of(context).listPlaceholderForegroundColor,
),
);
},
), ),
), ),
Container( Positioned.directional(
alignment: AlignmentDirectional.topEnd, textDirection: Directionality.of(context),
padding: const EdgeInsets.all(6), top: 6,
end: 6,
child: const Icon(Icons.play_circle_outlined, size: 17), child: const Icon(Icons.play_circle_outlined, size: 17),
), ),
if (isFavorite) if (isFavorite)
Container( Positioned.directional(
alignment: AlignmentDirectional.bottomStart, textDirection: Directionality.of(context),
padding: const EdgeInsets.all(6), bottom: 6,
start: 6,
child: const Icon(Icons.star, size: 15), child: const Icon(Icons.star, size: 15),
), ),
], ],
@ -322,6 +328,7 @@ class PhotoListVideo extends StatelessWidget {
final Account account; final Account account;
final String previewUrl; final String previewUrl;
final bool isFavorite; final bool isFavorite;
final VoidCallback? onError;
} }
class PhotoListLabel extends StatelessWidget { class PhotoListLabel extends StatelessWidget {