From 3b71253a5023db45d309f3fb283015cd674afe95 Mon Sep 17 00:00:00 2001 From: Hamlet Jiang Su Date: Tue, 23 Dec 2025 18:33:23 -0800 Subject: [PATCH 1/2] feat: add setting to customize image peek duration --- lib/l10n/app_en.arb | 12 +++++ lib/l10n/generated/app_localizations.dart | 18 +++++++ lib/l10n/generated/app_localizations_ar.dart | 10 ++++ lib/l10n/generated/app_localizations_be.dart | 10 ++++ lib/l10n/generated/app_localizations_cs.dart | 10 ++++ lib/l10n/generated/app_localizations_de.dart | 10 ++++ lib/l10n/generated/app_localizations_en.dart | 10 ++++ lib/l10n/generated/app_localizations_eo.dart | 10 ++++ lib/l10n/generated/app_localizations_es.dart | 10 ++++ lib/l10n/generated/app_localizations_fi.dart | 10 ++++ lib/l10n/generated/app_localizations_fr.dart | 10 ++++ lib/l10n/generated/app_localizations_hu.dart | 10 ++++ lib/l10n/generated/app_localizations_it.dart | 10 ++++ lib/l10n/generated/app_localizations_nb.dart | 10 ++++ lib/l10n/generated/app_localizations_nl.dart | 10 ++++ lib/l10n/generated/app_localizations_pl.dart | 10 ++++ lib/l10n/generated/app_localizations_pt.dart | 10 ++++ lib/l10n/generated/app_localizations_ru.dart | 10 ++++ lib/l10n/generated/app_localizations_sk.dart | 10 ++++ lib/l10n/generated/app_localizations_sv.dart | 10 ++++ lib/l10n/generated/app_localizations_ta.dart | 10 ++++ lib/l10n/generated/app_localizations_tr.dart | 10 ++++ lib/l10n/generated/app_localizations_uk.dart | 10 ++++ lib/l10n/generated/app_localizations_zh.dart | 10 ++++ lib/src/app/bloc/thunder_bloc.dart | 6 +++ lib/src/app/bloc/thunder_state.dart | 16 ++++++ lib/src/core/enums/local_settings.dart | 4 ++ .../pages/gesture_settings_page.dart | 51 ++++++++++++++++-- lib/src/shared/widgets/media/media_view.dart | 54 +++++++++++-------- 29 files changed, 356 insertions(+), 25 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 1ff5871a2..a0b3acc69 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1381,6 +1381,14 @@ "@imageDimensionTimeout": { "description": "Setting for how long to wait for the image dimensions to be fetched" }, + "imagePeekDuration": "Image Peek Duration", + "@imagePeekDuration": { + "description": "Setting for image peek duration" + }, + "imagePeekDurationDescription": "Duration of long press before image peek is triggered", + "@imagePeekDurationDescription": { + "description": "Description for image peek duration setting" + }, "importDatabase": "Import Database", "@importDatabase": { "description": "Name of setting for importing the db" @@ -1693,6 +1701,10 @@ "@me": { "description": "Label for the current user." }, + "media": "Media", + "@media": { + "description": "Describes a piece of media content (e.g., image, video)" + }, "medium": "Medium", "@medium": { "description": "Description for medium font scale" diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 87b895395..9703facb0 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -2188,6 +2188,18 @@ abstract class AppLocalizations { /// **'Image Dimension Timeout'** String get imageDimensionTimeout; + /// Setting for image peek duration + /// + /// In en, this message translates to: + /// **'Image Peek Duration'** + String get imagePeekDuration; + + /// Description for image peek duration setting + /// + /// In en, this message translates to: + /// **'Duration of long press before image peek is triggered'** + String get imagePeekDurationDescription; + /// Name of setting for importing the db /// /// In en, this message translates to: @@ -2638,6 +2650,12 @@ abstract class AppLocalizations { /// **'Me'** String get me; + /// Describes a piece of media content (e.g., image, video) + /// + /// In en, this message translates to: + /// **'Media'** + String get media; + /// Description for medium font scale /// /// In en, this message translates to: diff --git a/lib/l10n/generated/app_localizations_ar.dart b/lib/l10n/generated/app_localizations_ar.dart index 3c5600cca..ca1668463 100644 --- a/lib/l10n/generated/app_localizations_ar.dart +++ b/lib/l10n/generated/app_localizations_ar.dart @@ -1160,6 +1160,13 @@ class AppLocalizationsAr extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1426,6 +1433,9 @@ class AppLocalizationsAr extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Medium'; diff --git a/lib/l10n/generated/app_localizations_be.dart b/lib/l10n/generated/app_localizations_be.dart index b618cbe52..f1d2631b3 100644 --- a/lib/l10n/generated/app_localizations_be.dart +++ b/lib/l10n/generated/app_localizations_be.dart @@ -1168,6 +1168,13 @@ class AppLocalizationsBe extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1434,6 +1441,9 @@ class AppLocalizationsBe extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Medium'; diff --git a/lib/l10n/generated/app_localizations_cs.dart b/lib/l10n/generated/app_localizations_cs.dart index bd0963393..9e226da5b 100644 --- a/lib/l10n/generated/app_localizations_cs.dart +++ b/lib/l10n/generated/app_localizations_cs.dart @@ -1176,6 +1176,13 @@ class AppLocalizationsCs extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1437,6 +1444,9 @@ class AppLocalizationsCs extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Střední'; diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 373fed2e6..858f45a52 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -1185,6 +1185,13 @@ class AppLocalizationsDe extends AppLocalizations { @override String get imageDimensionTimeout => 'Zeitüberschreitung Bild Abmessungen'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Datenbank importieren'; @@ -1455,6 +1462,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get me => 'Ich'; + @override + String get media => 'Media'; + @override String get medium => 'Mittel'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index d6ddb6ab5..1f8952ddf 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -1168,6 +1168,13 @@ class AppLocalizationsEn extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1434,6 +1441,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Medium'; diff --git a/lib/l10n/generated/app_localizations_eo.dart b/lib/l10n/generated/app_localizations_eo.dart index 021e52dd2..66dd00285 100644 --- a/lib/l10n/generated/app_localizations_eo.dart +++ b/lib/l10n/generated/app_localizations_eo.dart @@ -1165,6 +1165,13 @@ class AppLocalizationsEo extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1424,6 +1431,9 @@ class AppLocalizationsEo extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Medium'; diff --git a/lib/l10n/generated/app_localizations_es.dart b/lib/l10n/generated/app_localizations_es.dart index ea72dd8d7..2d2753a65 100644 --- a/lib/l10n/generated/app_localizations_es.dart +++ b/lib/l10n/generated/app_localizations_es.dart @@ -1193,6 +1193,13 @@ class AppLocalizationsEs extends AppLocalizations { @override String get imageDimensionTimeout => 'Tiempo de Espera de Dimensión de Imagen'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Importar Base de Datos'; @@ -1462,6 +1469,9 @@ class AppLocalizationsEs extends AppLocalizations { @override String get me => 'Yo'; + @override + String get media => 'Media'; + @override String get medium => 'Medio'; diff --git a/lib/l10n/generated/app_localizations_fi.dart b/lib/l10n/generated/app_localizations_fi.dart index fb567391c..d4ba51a16 100644 --- a/lib/l10n/generated/app_localizations_fi.dart +++ b/lib/l10n/generated/app_localizations_fi.dart @@ -1165,6 +1165,13 @@ class AppLocalizationsFi extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1424,6 +1431,9 @@ class AppLocalizationsFi extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Medium'; diff --git a/lib/l10n/generated/app_localizations_fr.dart b/lib/l10n/generated/app_localizations_fr.dart index 13f8136a7..08fcea84e 100644 --- a/lib/l10n/generated/app_localizations_fr.dart +++ b/lib/l10n/generated/app_localizations_fr.dart @@ -1195,6 +1195,13 @@ class AppLocalizationsFr extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1456,6 +1463,9 @@ class AppLocalizationsFr extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Medium'; diff --git a/lib/l10n/generated/app_localizations_hu.dart b/lib/l10n/generated/app_localizations_hu.dart index 9f9e0325d..266698fdf 100644 --- a/lib/l10n/generated/app_localizations_hu.dart +++ b/lib/l10n/generated/app_localizations_hu.dart @@ -1168,6 +1168,13 @@ class AppLocalizationsHu extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1434,6 +1441,9 @@ class AppLocalizationsHu extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Medium'; diff --git a/lib/l10n/generated/app_localizations_it.dart b/lib/l10n/generated/app_localizations_it.dart index f7adf7dd1..afa697b96 100644 --- a/lib/l10n/generated/app_localizations_it.dart +++ b/lib/l10n/generated/app_localizations_it.dart @@ -1174,6 +1174,13 @@ class AppLocalizationsIt extends AppLocalizations { @override String get imageDimensionTimeout => 'Timeout Dimensione Immagine'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Importa Database'; @@ -1434,6 +1441,9 @@ class AppLocalizationsIt extends AppLocalizations { @override String get me => 'Io'; + @override + String get media => 'Media'; + @override String get medium => 'Medio'; diff --git a/lib/l10n/generated/app_localizations_nb.dart b/lib/l10n/generated/app_localizations_nb.dart index 4adec4457..ad60e7318 100644 --- a/lib/l10n/generated/app_localizations_nb.dart +++ b/lib/l10n/generated/app_localizations_nb.dart @@ -1163,6 +1163,13 @@ class AppLocalizationsNb extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1415,6 +1422,9 @@ class AppLocalizationsNb extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Medium'; diff --git a/lib/l10n/generated/app_localizations_nl.dart b/lib/l10n/generated/app_localizations_nl.dart index 930994500..f1f349da3 100644 --- a/lib/l10n/generated/app_localizations_nl.dart +++ b/lib/l10n/generated/app_localizations_nl.dart @@ -1179,6 +1179,13 @@ class AppLocalizationsNl extends AppLocalizations { @override String get imageDimensionTimeout => 'Time-out voor afbeeldings­afmetingen'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Database importeren'; @@ -1446,6 +1453,9 @@ class AppLocalizationsNl extends AppLocalizations { @override String get me => 'Ik'; + @override + String get media => 'Media'; + @override String get medium => 'Gemiddeld'; diff --git a/lib/l10n/generated/app_localizations_pl.dart b/lib/l10n/generated/app_localizations_pl.dart index 0be8f8335..ab96c21ef 100644 --- a/lib/l10n/generated/app_localizations_pl.dart +++ b/lib/l10n/generated/app_localizations_pl.dart @@ -1179,6 +1179,13 @@ class AppLocalizationsPl extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1439,6 +1446,9 @@ class AppLocalizationsPl extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Średnie'; diff --git a/lib/l10n/generated/app_localizations_pt.dart b/lib/l10n/generated/app_localizations_pt.dart index af3145564..e086895da 100644 --- a/lib/l10n/generated/app_localizations_pt.dart +++ b/lib/l10n/generated/app_localizations_pt.dart @@ -1177,6 +1177,13 @@ class AppLocalizationsPt extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Importar base de dados'; @@ -1443,6 +1450,9 @@ class AppLocalizationsPt extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Medium'; diff --git a/lib/l10n/generated/app_localizations_ru.dart b/lib/l10n/generated/app_localizations_ru.dart index 627cc19f4..021d1c889 100644 --- a/lib/l10n/generated/app_localizations_ru.dart +++ b/lib/l10n/generated/app_localizations_ru.dart @@ -1179,6 +1179,13 @@ class AppLocalizationsRu extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1438,6 +1445,9 @@ class AppLocalizationsRu extends AppLocalizations { @override String get me => 'Я'; + @override + String get media => 'Media'; + @override String get medium => 'Средний'; diff --git a/lib/l10n/generated/app_localizations_sk.dart b/lib/l10n/generated/app_localizations_sk.dart index c510372e4..db0cfb392 100644 --- a/lib/l10n/generated/app_localizations_sk.dart +++ b/lib/l10n/generated/app_localizations_sk.dart @@ -1176,6 +1176,13 @@ class AppLocalizationsSk extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1445,6 +1452,9 @@ class AppLocalizationsSk extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Medium'; diff --git a/lib/l10n/generated/app_localizations_sv.dart b/lib/l10n/generated/app_localizations_sv.dart index 88e4259ac..6d4bd92a8 100644 --- a/lib/l10n/generated/app_localizations_sv.dart +++ b/lib/l10n/generated/app_localizations_sv.dart @@ -1162,6 +1162,13 @@ class AppLocalizationsSv extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1421,6 +1428,9 @@ class AppLocalizationsSv extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Medium'; diff --git a/lib/l10n/generated/app_localizations_ta.dart b/lib/l10n/generated/app_localizations_ta.dart index 3860c2a01..5ce16f39c 100644 --- a/lib/l10n/generated/app_localizations_ta.dart +++ b/lib/l10n/generated/app_localizations_ta.dart @@ -1184,6 +1184,13 @@ class AppLocalizationsTa extends AppLocalizations { @override String get imageDimensionTimeout => 'பட பரிமாண நேரம் முடிந்தது'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'தரவுத்தளத்தை இறக்குமதி செய்யுங்கள்'; @@ -1455,6 +1462,9 @@ class AppLocalizationsTa extends AppLocalizations { @override String get me => 'நான்'; + @override + String get media => 'Media'; + @override String get medium => 'சராசரி'; diff --git a/lib/l10n/generated/app_localizations_tr.dart b/lib/l10n/generated/app_localizations_tr.dart index 75c1cec5d..c54f4a65a 100644 --- a/lib/l10n/generated/app_localizations_tr.dart +++ b/lib/l10n/generated/app_localizations_tr.dart @@ -1176,6 +1176,13 @@ class AppLocalizationsTr extends AppLocalizations { @override String get imageDimensionTimeout => 'Resim Boyutu Zaman Aşımı'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Veritabanını İçe Aktar'; @@ -1442,6 +1449,9 @@ class AppLocalizationsTr extends AppLocalizations { @override String get me => 'Ben'; + @override + String get media => 'Media'; + @override String get medium => 'Orta'; diff --git a/lib/l10n/generated/app_localizations_uk.dart b/lib/l10n/generated/app_localizations_uk.dart index 03ee13d3d..b35157046 100644 --- a/lib/l10n/generated/app_localizations_uk.dart +++ b/lib/l10n/generated/app_localizations_uk.dart @@ -1170,6 +1170,13 @@ class AppLocalizationsUk extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1429,6 +1436,9 @@ class AppLocalizationsUk extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Medium'; diff --git a/lib/l10n/generated/app_localizations_zh.dart b/lib/l10n/generated/app_localizations_zh.dart index 42b20338f..2ef89ade9 100644 --- a/lib/l10n/generated/app_localizations_zh.dart +++ b/lib/l10n/generated/app_localizations_zh.dart @@ -1168,6 +1168,13 @@ class AppLocalizationsZh extends AppLocalizations { @override String get imageDimensionTimeout => 'Image Dimension Timeout'; + @override + String get imagePeekDuration => 'Image Peek Duration'; + + @override + String get imagePeekDurationDescription => + 'Duration of long press before image peek is triggered'; + @override String get importDatabase => 'Import Database'; @@ -1434,6 +1441,9 @@ class AppLocalizationsZh extends AppLocalizations { @override String get me => 'Me'; + @override + String get media => 'Media'; + @override String get medium => 'Medium'; diff --git a/lib/src/app/bloc/thunder_bloc.dart b/lib/src/app/bloc/thunder_bloc.dart index fc664602e..bd6ce7e4b 100644 --- a/lib/src/app/bloc/thunder_bloc.dart +++ b/lib/src/app/bloc/thunder_bloc.dart @@ -232,6 +232,9 @@ class ThunderBloc extends Bloc { bool enableFullScreenSwipeNavigationGesture = UserPreferences.getLocalSetting(LocalSettings.enableFullScreenSwipeNavigationGesture) ?? true; + // Image Peek Settings + int imagePeekDuration = UserPreferences.getLocalSetting(LocalSettings.imagePeekDuration) ?? 500; + /// -------------------------- FAB Related Settings -------------------------- bool enableFeedsFab = UserPreferences.getLocalSetting(LocalSettings.enableFeedsFab) ?? true; bool enablePostsFab = UserPreferences.getLocalSetting(LocalSettings.enablePostsFab) ?? true; @@ -401,6 +404,9 @@ class ThunderBloc extends Bloc { enableFullScreenSwipeNavigationGesture: enableFullScreenSwipeNavigationGesture, + // Image Peek Settings + imagePeekDuration: imagePeekDuration, + /// -------------------------- FAB Related Settings -------------------------- enablePostsFab: enablePostsFab, enableFeedsFab: enableFeedsFab, diff --git a/lib/src/app/bloc/thunder_state.dart b/lib/src/app/bloc/thunder_state.dart index 898126f24..bbc2ab619 100644 --- a/lib/src/app/bloc/thunder_state.dart +++ b/lib/src/app/bloc/thunder_state.dart @@ -119,6 +119,9 @@ class ThunderState extends Equatable { this.rightSecondaryCommentGesture = SwipeAction.save, this.enableFullScreenSwipeNavigationGesture = true, + // Image Peek Settings + this.imagePeekDuration = 500, + // Theme Settings this.themeType = ThemeType.system, this.selectedTheme = CustomThemeType.deepBlue, @@ -316,6 +319,10 @@ class ThunderState extends Equatable { final bool enableFullScreenSwipeNavigationGesture; + // Image Peek Settings + /// Duration in milliseconds before image peek is triggered (default: 500ms) + final int imagePeekDuration; + /// -------------------------- FAB Related Settings -------------------------- final bool enableFeedsFab; final bool enablePostsFab; @@ -498,6 +505,9 @@ class ThunderState extends Equatable { SwipeAction? rightSecondaryCommentGesture, bool? enableFullScreenSwipeNavigationGesture, + // Image Peek Settings + int? imagePeekDuration, + /// -------------------------- FAB Related Settings -------------------------- bool? enableFeedsFab, bool? enablePostsFab, @@ -669,6 +679,9 @@ class ThunderState extends Equatable { enableFullScreenSwipeNavigationGesture: enableFullScreenSwipeNavigationGesture ?? this.enableFullScreenSwipeNavigationGesture, + // Image Peek Settings + imagePeekDuration: imagePeekDuration ?? this.imagePeekDuration, + // Comment Gestures enableCommentGestures: enableCommentGestures ?? this.enableCommentGestures, leftPrimaryCommentGesture: leftPrimaryCommentGesture ?? this.leftPrimaryCommentGesture, @@ -859,6 +872,9 @@ class ThunderState extends Equatable { enableFullScreenSwipeNavigationGesture, + // Image Peek Settings + imagePeekDuration, + /// -------------------------- FAB Related Settings -------------------------- enableFeedsFab, enablePostsFab, diff --git a/lib/src/core/enums/local_settings.dart b/lib/src/core/enums/local_settings.dart index e6894b4bb..2b5fbaa68 100644 --- a/lib/src/core/enums/local_settings.dart +++ b/lib/src/core/enums/local_settings.dart @@ -357,6 +357,9 @@ enum LocalSettings { enableFullScreenSwipeNavigationGesture( name: 'setting_gesture_enable_fullscreen_navigation_gesture', key: 'fullscreenSwipeGestures', category: LocalSettingsCategories.gestures, subCategory: LocalSettingsSubCategories.navigation), + // Image Peek Settings + imagePeekDuration(name: 'setting_gesture_image_peek_duration', key: 'imagePeekDuration', category: LocalSettingsCategories.gestures, subCategory: LocalSettingsSubCategories.posts), + /// -------------------------- FAB Related Settings -------------------------- enableFeedsFab(name: 'setting_enable_feed_fab', key: 'enableFloatingButtonOnFeeds', category: LocalSettingsCategories.floatingActionButton, subCategory: LocalSettingsSubCategories.feed), enablePostsFab(name: 'setting_enable_post_fab', key: 'enableFloatingButtonOnPosts', category: LocalSettingsCategories.floatingActionButton, subCategory: LocalSettingsSubCategories.feed), @@ -577,6 +580,7 @@ extension LocalizationExt on AppLocalizations { 'rightLongSwipe': rightLongSwipe, 'commentSwipeActions': commentSwipeActions, 'fullscreenSwipeGestures': fullscreenSwipeGestures, + 'imagePeekDuration': imagePeekDuration, 'enableFloatingButtonOnFeeds': enableFloatingButtonOnFeeds, 'enableFloatingButtonOnPosts': enableFloatingButtonOnPosts, 'backToTop': backToTop, diff --git a/lib/src/features/settings/presentation/pages/gesture_settings_page.dart b/lib/src/features/settings/presentation/pages/gesture_settings_page.dart index f7c7716fb..fc455f37d 100644 --- a/lib/src/features/settings/presentation/pages/gesture_settings_page.dart +++ b/lib/src/features/settings/presentation/pages/gesture_settings_page.dart @@ -45,6 +45,9 @@ class _GestureSettingsPageState extends State with TickerPr bool enableFullScreenSwipeNavigationGesture = true; + // Image Peek Settings + int imagePeekDuration = 500; + /// Loading bool isLoading = true; @@ -131,6 +134,10 @@ class _GestureSettingsPageState extends State with TickerPr await prefs.setBool(LocalSettings.enableFullScreenSwipeNavigationGesture.name, value); setState(() => enableFullScreenSwipeNavigationGesture = value); break; + case LocalSettings.imagePeekDuration: + await prefs.setInt(LocalSettings.imagePeekDuration.name, value); + setState(() => imagePeekDuration = value); + break; default: break; } @@ -165,6 +172,9 @@ class _GestureSettingsPageState extends State with TickerPr enableFullScreenSwipeNavigationGesture = prefs.getBool(LocalSettings.enableFullScreenSwipeNavigationGesture.name) ?? true; + // Image Peek Settings + imagePeekDuration = prefs.getInt(LocalSettings.imagePeekDuration.name) ?? 500; + isLoading = false; }); } @@ -233,7 +243,7 @@ class _GestureSettingsPageState extends State with TickerPr padding: const EdgeInsets.only(left: 16.0, bottom: 8.0), child: Text( l10n.navigation, - style: theme.textTheme.titleLarge, + style: theme.textTheme.titleMedium, ), ), ToggleOption( @@ -260,7 +270,7 @@ class _GestureSettingsPageState extends State with TickerPr padding: const EdgeInsets.only(left: 16.0, bottom: 8.0), child: Text( l10n.sidebar, - style: theme.textTheme.titleLarge, + style: theme.textTheme.titleMedium, ), ), ToggleOption( @@ -288,6 +298,39 @@ class _GestureSettingsPageState extends State with TickerPr ], ), ), + Container( + padding: const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 16.0, bottom: 8.0), + child: Text( + l10n.media, + style: theme.textTheme.titleMedium, + ), + ), + ListOption( + description: l10n.imagePeekDuration, + subtitle: l10n.imagePeekDurationDescription, + value: ListPickerItem(label: '${imagePeekDuration}ms', icon: Icons.touch_app_rounded, payload: imagePeekDuration), + options: [ + ListPickerItem(icon: Icons.touch_app_rounded, label: '100ms', payload: 100), + ListPickerItem(icon: Icons.touch_app_rounded, label: '200ms', payload: 200), + ListPickerItem(icon: Icons.touch_app_rounded, label: '300ms', payload: 300), + ListPickerItem(icon: Icons.touch_app_rounded, label: '400ms', payload: 400), + ListPickerItem(icon: Icons.touch_app_rounded, label: '500ms', payload: 500), + ], + icon: Icons.touch_app_rounded, + onChanged: (value) async => setPreferences(LocalSettings.imagePeekDuration, value.payload), + highlightKey: settingToHighlightKey, + setting: LocalSettings.imagePeekDuration, + highlightedSetting: settingToHighlight, + ), + ], + ), + ), Container( padding: const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 8.0), child: Column( @@ -298,7 +341,7 @@ class _GestureSettingsPageState extends State with TickerPr padding: const EdgeInsets.only(left: 16.0, bottom: 8.0), child: Text( l10n.posts, - style: theme.textTheme.titleLarge, + style: theme.textTheme.titleMedium, ), ), Padding( @@ -405,7 +448,7 @@ class _GestureSettingsPageState extends State with TickerPr padding: const EdgeInsets.only(left: 16.0, bottom: 8.0), child: Text( l10n.comments, - style: theme.textTheme.titleLarge, + style: theme.textTheme.titleMedium, ), ), Padding( diff --git a/lib/src/shared/widgets/media/media_view.dart b/lib/src/shared/widgets/media/media_view.dart index 93b761545..624e49858 100644 --- a/lib/src/shared/widgets/media/media_view.dart +++ b/lib/src/shared/widgets/media/media_view.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -198,6 +199,7 @@ class _MediaViewState extends State with TickerProviderStateMixin { final l10n = AppLocalizations.of(context)!; final state = context.read().state; + final imagePeekDurationMs = context.select((ThunderBloc bloc) => bloc.state.imagePeekDuration); final blurNSFWPreviews = widget.hideNsfwPreviews && widget.media.nsfw; double? width; @@ -248,6 +250,8 @@ class _MediaViewState extends State with TickerProviderStateMixin { // For images, add hold to peek gesture (only when image is loaded successfully) if (widget.media.mediaType == MediaType.image && _imagePreviewState == ImagePreviewState.success) { + final imagePeekDuration = Duration(milliseconds: imagePeekDurationMs); + child = InkWell( splashColor: theme.colorScheme.primary.withValues(alpha: 0.4), borderRadius: BorderRadius.circular((widget.edgeToEdgeImages ? 0 : 12)), @@ -255,28 +259,36 @@ class _MediaViewState extends State with TickerProviderStateMixin { handleTap(); showImage(); }, - child: GestureDetector( - onLongPressStart: (_) { - _overlayEntry = OverlayEntry( - builder: (context) { - return FadeTransition( - opacity: _overlayAnimationController, - child: ImageViewer( - url: widget.media.thumbnailUrl ?? widget.media.mediaUrl, - postId: widget.postId, - navigateToPost: widget.navigateToPost, - isPeek: true, - ), - ); + child: RawGestureDetector( + gestures: { + LongPressGestureRecognizer: GestureRecognizerFactoryWithHandlers( + () => LongPressGestureRecognizer(duration: imagePeekDuration), + (LongPressGestureRecognizer instance) { + instance + ..onLongPressStart = (_) { + _overlayEntry = OverlayEntry( + builder: (context) { + return FadeTransition( + opacity: _overlayAnimationController, + child: ImageViewer( + url: widget.media.thumbnailUrl ?? widget.media.mediaUrl, + postId: widget.postId, + navigateToPost: widget.navigateToPost, + isPeek: true, + ), + ); + }, + ); + Overlay.of(context).insert(_overlayEntry!); + _overlayAnimationController.forward(); + } + ..onLongPressEnd = (_) async { + await _overlayAnimationController.reverse(); + _overlayEntry?.remove(); + _overlayEntry = null; + }; }, - ); - Overlay.of(context).insert(_overlayEntry!); - _overlayAnimationController.forward(); - }, - onLongPressEnd: (_) async { - await _overlayAnimationController.reverse(); - _overlayEntry?.remove(); - _overlayEntry = null; + ), }, ), ); From a52246294d5c1bc792831c46eb3a73c1c109df4e Mon Sep 17 00:00:00 2001 From: Hamlet Jiang Su Date: Wed, 24 Dec 2025 20:52:11 -0800 Subject: [PATCH 2/2] feat: reduce default hold to peek duration --- lib/src/app/bloc/thunder_bloc.dart | 2 +- lib/src/app/bloc/thunder_state.dart | 4 ++-- .../settings/presentation/pages/gesture_settings_page.dart | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/app/bloc/thunder_bloc.dart b/lib/src/app/bloc/thunder_bloc.dart index bd6ce7e4b..6adf96753 100644 --- a/lib/src/app/bloc/thunder_bloc.dart +++ b/lib/src/app/bloc/thunder_bloc.dart @@ -233,7 +233,7 @@ class ThunderBloc extends Bloc { bool enableFullScreenSwipeNavigationGesture = UserPreferences.getLocalSetting(LocalSettings.enableFullScreenSwipeNavigationGesture) ?? true; // Image Peek Settings - int imagePeekDuration = UserPreferences.getLocalSetting(LocalSettings.imagePeekDuration) ?? 500; + int imagePeekDuration = UserPreferences.getLocalSetting(LocalSettings.imagePeekDuration) ?? 300; /// -------------------------- FAB Related Settings -------------------------- bool enableFeedsFab = UserPreferences.getLocalSetting(LocalSettings.enableFeedsFab) ?? true; diff --git a/lib/src/app/bloc/thunder_state.dart b/lib/src/app/bloc/thunder_state.dart index bbc2ab619..727404b99 100644 --- a/lib/src/app/bloc/thunder_state.dart +++ b/lib/src/app/bloc/thunder_state.dart @@ -120,7 +120,7 @@ class ThunderState extends Equatable { this.enableFullScreenSwipeNavigationGesture = true, // Image Peek Settings - this.imagePeekDuration = 500, + this.imagePeekDuration = 300, // Theme Settings this.themeType = ThemeType.system, @@ -320,7 +320,7 @@ class ThunderState extends Equatable { final bool enableFullScreenSwipeNavigationGesture; // Image Peek Settings - /// Duration in milliseconds before image peek is triggered (default: 500ms) + /// Duration in milliseconds before image peek is triggered (default: 300ms) final int imagePeekDuration; /// -------------------------- FAB Related Settings -------------------------- diff --git a/lib/src/features/settings/presentation/pages/gesture_settings_page.dart b/lib/src/features/settings/presentation/pages/gesture_settings_page.dart index fc455f37d..b97ba6e82 100644 --- a/lib/src/features/settings/presentation/pages/gesture_settings_page.dart +++ b/lib/src/features/settings/presentation/pages/gesture_settings_page.dart @@ -46,7 +46,7 @@ class _GestureSettingsPageState extends State with TickerPr bool enableFullScreenSwipeNavigationGesture = true; // Image Peek Settings - int imagePeekDuration = 500; + int imagePeekDuration = 300; /// Loading bool isLoading = true; @@ -173,7 +173,7 @@ class _GestureSettingsPageState extends State with TickerPr enableFullScreenSwipeNavigationGesture = prefs.getBool(LocalSettings.enableFullScreenSwipeNavigationGesture.name) ?? true; // Image Peek Settings - imagePeekDuration = prefs.getInt(LocalSettings.imagePeekDuration.name) ?? 500; + imagePeekDuration = prefs.getInt(LocalSettings.imagePeekDuration.name) ?? 300; isLoading = false; });