[mob][photos] Memory swipe (#6245)
## Description Enable swipe between memories.
This commit is contained in:
@@ -75,6 +75,8 @@ PODS:
|
||||
- Flutter
|
||||
- flutter_sodium (0.0.1):
|
||||
- Flutter
|
||||
- flutter_timezone (0.0.1):
|
||||
- Flutter
|
||||
- fluttertoast (0.0.2):
|
||||
- Flutter
|
||||
- GoogleDataTransport (10.1.0):
|
||||
@@ -259,6 +261,7 @@ DEPENDENCIES:
|
||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- flutter_sodium (from `.symlinks/plugins/flutter_sodium/ios`)
|
||||
- flutter_timezone (from `.symlinks/plugins/flutter_timezone/ios`)
|
||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||
- home_widget (from `.symlinks/plugins/home_widget/ios`)
|
||||
- image_editor_common (from `.symlinks/plugins/image_editor_common/ios`)
|
||||
@@ -298,7 +301,7 @@ DEPENDENCIES:
|
||||
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/ente-io/ffmpeg-kit-custom-repo-ios:
|
||||
https://github.com/ente-io/ffmpeg-kit-custom-repo-ios.git:
|
||||
- ffmpeg_kit_custom
|
||||
trunk:
|
||||
- Firebase
|
||||
@@ -359,6 +362,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||
flutter_sodium:
|
||||
:path: ".symlinks/plugins/flutter_sodium/ios"
|
||||
flutter_timezone:
|
||||
:path: ".symlinks/plugins/flutter_timezone/ios"
|
||||
fluttertoast:
|
||||
:path: ".symlinks/plugins/fluttertoast/ios"
|
||||
home_widget:
|
||||
@@ -435,81 +440,82 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/wakelock_plus/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
app_links: f3e17e4ee5e357b39d8b95290a9b2c299fca71c6
|
||||
background_fetch: 39f11371c0dce04b001c4bfd5e782bcccb0a85e2
|
||||
battery_info: b6c551049266af31556b93c9d9b9452cfec0219f
|
||||
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
|
||||
cupertino_http: 947a233f40cfea55167a49f2facc18434ea117ba
|
||||
dart_ui_isolate: d5bcda83ca4b04f129d70eb90110b7a567aece14
|
||||
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
|
||||
app_links: 76b66b60cc809390ca1ad69bfd66b998d2387ac7
|
||||
background_fetch: 94b36ee293e82972852dba8ede1fbcd3bd3d9d57
|
||||
battery_info: 83f3aae7be2fccefab1d2bf06b8aa96f11c8bcdd
|
||||
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||
cupertino_http: 94ac07f5ff090b8effa6c5e2c47871d48ab7c86c
|
||||
dart_ui_isolate: 46f6714abe6891313267153ef6f9748d8ecfcab1
|
||||
device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89
|
||||
ffmpeg_kit_custom: 682b4f2f1ff1f8abae5a92f6c3540f2441d5be99
|
||||
ffmpeg_kit_flutter: 9dce4803991478c78c6fb9f972703301101095fe
|
||||
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
|
||||
ffmpeg_kit_flutter: 915b345acc97d4142e8a9a8549d177ff10f043f5
|
||||
file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
|
||||
Firebase: d80354ed7f6df5f9aca55e9eb47cc4b634735eaf
|
||||
firebase_core: 6e223dfa350b2edceb729cea505eaaef59330682
|
||||
firebase_messaging: 07fde77ae28c08616a1d4d870450efc2b38cf40d
|
||||
firebase_core: 6cbed78b4f298ed103a9fd034e6dbc846320480f
|
||||
firebase_messaging: 5e0adf2eb18b0ee59aa0c109314c091a0497ecac
|
||||
FirebaseCore: 99fe0c4b44a39f37d99e6404e02009d2db5d718d
|
||||
FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629
|
||||
FirebaseInstallations: 6c963bd2a86aca0481eef4f48f5a4df783ae5917
|
||||
FirebaseMessaging: 487b634ccdf6f7b7ff180fdcb2a9935490f764e8
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_email_sender: e03bdda7637bcd3539bfe718fddd980e9508efaa
|
||||
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
|
||||
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
|
||||
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
|
||||
flutter_native_splash: f71420956eb811e6d310720fee915f1d42852e7a
|
||||
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
||||
flutter_sodium: a00383520fc689c688b66fd3092984174712493e
|
||||
fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f
|
||||
flutter_email_sender: aa1e9772696691d02cd91fea829856c11efb8e58
|
||||
flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1
|
||||
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||
flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100
|
||||
flutter_native_splash: 6cad9122ea0fad137d23137dd14b937f3e90b145
|
||||
flutter_secure_storage: 2c2ff13db9e0a5647389bff88b0ecac56e3f3418
|
||||
flutter_sodium: 7e4621538491834eba53bd524547854bcbbd6987
|
||||
flutter_timezone: 7c838e17ffd4645d261e87037e5bebf6d38fe544
|
||||
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
|
||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
||||
home_widget: 0434835a4c9a75704264feff6be17ea40e0f0d57
|
||||
image_editor_common: d6f6644ae4a6de80481e89fe6d0a8c49e30b4b43
|
||||
in_app_purchase_storekit: a1ce04056e23eecc666b086040239da7619cd783
|
||||
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
|
||||
launcher_icon_switcher: 8e0ad2131a20c51c1dd939896ee32e70cd845b37
|
||||
home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f
|
||||
image_editor_common: 3de87e7c4804f4ae24c8f8a998362b98c105cac1
|
||||
in_app_purchase_storekit: d1a48cb0f8b29dbf5f85f782f5dd79b21b90a5e6
|
||||
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
|
||||
launcher_icon_switcher: 84c218d233505aa7d8655d8fa61a3ba802c022da
|
||||
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
|
||||
local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3
|
||||
local_auth_ios: 5046a18c018dd973247a0564496c8898dbb5adf9
|
||||
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
|
||||
local_auth_ios: f7a1841beef3151d140a967c2e46f30637cdf451
|
||||
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
||||
maps_launcher: 2e5b6a2d664ec6c27f82ffa81b74228d770ab203
|
||||
media_extension: 6618f07abd762cdbfaadf1b0c56a287e820f0c84
|
||||
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
||||
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
|
||||
motion_sensors: 03f55b7c637a7e365a0b5f9697a449f9059d5d91
|
||||
motionphoto: 8b65ce50c7d7ff3c767534fc3768b2eed9ac24e4
|
||||
move_to_background: cd3091014529ec7829e342ad2d75c0a11f4378a5
|
||||
maps_launcher: edf829809ba9e894d70e569bab11c16352dedb45
|
||||
media_extension: 671e2567880d96c95c65c9a82ccceed8f2e309fd
|
||||
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
|
||||
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
|
||||
motion_sensors: 741e702c17467b9569a92165dda8d4d88c6167f1
|
||||
motionphoto: 23e2aeb5c6380112f69468d71f970fa7438e5ed1
|
||||
move_to_background: 7e3467dd2a1d1013e98c9c1cb93fd53cd7ef9d84
|
||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||
native_video_player: 5d36066807b680e181473e6890dde643ac85380d
|
||||
objective_c: 77e887b5ba1827970907e10e832eec1683f3431d
|
||||
onnxruntime: e7c2ae44385191eaad5ae64c935a72debaddc997
|
||||
native_video_player: e363dd14f6a498ad8a8f7e6486a0db046ad19f13
|
||||
objective_c: 89e720c30d716b036faf9c9684022048eee1eee2
|
||||
onnxruntime: f9b296392c96c42882be020a59dbeac6310d81b2
|
||||
onnxruntime-c: a909204639a1f035f575127ac406f781ac797c9c
|
||||
onnxruntime-objc: b6fab0f1787aa6f7190c2013f03037df4718bd8b
|
||||
open_mail_app: 70273c53f768beefdafbe310c3d9086e4da3cb02
|
||||
open_mail_app: 7314a609e88eed22d53671279e189af7a0ab0f11
|
||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||
photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a
|
||||
privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||
photo_manager: d2fbcc0f2d82458700ee6256a15018210a81d413
|
||||
privacy_screen: 3159a541f5d3a31bea916cfd4e58f9dc722b3fd4
|
||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||
receive_sharing_intent: 79c848f5b045674ad60b9fea3bafea59962ad2c1
|
||||
receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
|
||||
SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868
|
||||
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
|
||||
Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854
|
||||
sentry_flutter: 6a134f9d381e49f22ea25a67736cf0cf4d02ec9c
|
||||
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
|
||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
||||
sentry_flutter: 942017adbe00f963061cb11ec260414a990b7a42
|
||||
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
||||
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||
sqlite3: fc1400008a9b3525f5914ed715a5d1af0b8f4983
|
||||
sqlite3_flutter_libs: 069c435986dd4b63461aecd68f4b30be4a9e9daa
|
||||
system_info_plus: 5393c8da281d899950d751713575fbf91c7709aa
|
||||
ua_client_hints: aeabd123262c087f0ce151ef96fa3ab77bfc8b38
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
|
||||
video_thumbnail: 94ba6705afbaa120b77287080424930f23ea0c40
|
||||
volume_controller: 2e3de73d6e7e81a0067310d17fb70f2f86d71ac7
|
||||
wakelock_plus: 373cfe59b235a6dd5837d0fb88791d2f13a90d56
|
||||
sqlite3_flutter_libs: 3c323550ef3b928bc0aa9513c841e45a7d242832
|
||||
system_info_plus: 555ce7047fbbf29154726db942ae785c29211740
|
||||
ua_client_hints: 92fe0d139619b73ec9fcb46cc7e079a26178f586
|
||||
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
|
||||
video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b
|
||||
video_thumbnail: 584ccfa55d8fd2f3d5507218b0a18d84c839c620
|
||||
volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12
|
||||
wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49
|
||||
|
||||
PODFILE CHECKSUM: a8ef88ad74ba499756207e7592c6071a96756d18
|
||||
|
||||
|
||||
93
mobile/lib/ui/home/memories/all_memories_page.dart
Normal file
93
mobile/lib/ui/home/memories/all_memories_page.dart
Normal file
@@ -0,0 +1,93 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:photos/models/memories/memory.dart";
|
||||
import "package:photos/ui/home/memories/full_screen_memory.dart";
|
||||
|
||||
class AllMemoriesPage extends StatefulWidget {
|
||||
final int initialPageIndex;
|
||||
final List<List<Memory>> allMemories;
|
||||
final List<String> allTitles;
|
||||
|
||||
const AllMemoriesPage({
|
||||
super.key,
|
||||
required this.allMemories,
|
||||
required this.allTitles,
|
||||
required this.initialPageIndex,
|
||||
});
|
||||
|
||||
@override
|
||||
State<AllMemoriesPage> createState() => _AllMemoriesPageState();
|
||||
}
|
||||
|
||||
class _AllMemoriesPageState extends State<AllMemoriesPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late PageController pageController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
pageController = PageController(initialPage: widget.initialPageIndex);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
pageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: PageView.builder(
|
||||
controller: pageController,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
hitTestBehavior: HitTestBehavior.translucent,
|
||||
itemCount: widget.allMemories.length,
|
||||
itemBuilder: (context, index) {
|
||||
final initialMemoryIndex = _getNextMemoryIndex(index);
|
||||
return FullScreenMemoryDataUpdater(
|
||||
initialIndex: initialMemoryIndex,
|
||||
memories: widget.allMemories[index],
|
||||
child: FullScreenMemory(
|
||||
widget.allTitles[index],
|
||||
initialMemoryIndex,
|
||||
onNextMemory: index < widget.allMemories.length - 1
|
||||
? () => pageController.nextPage(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.ease,
|
||||
)
|
||||
: null,
|
||||
onPreviousMemory: index > 0
|
||||
? () => pageController.previousPage(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.ease,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
int _getNextMemoryIndex(int currentIndex) {
|
||||
int lastSeenIndex = 0;
|
||||
int lastSeenTimestamp = 0;
|
||||
for (var index = 0;
|
||||
index < widget.allMemories[currentIndex].length;
|
||||
index++) {
|
||||
final memory = widget.allMemories[currentIndex][index];
|
||||
if (!memory.isSeen()) {
|
||||
return index;
|
||||
} else {
|
||||
if (memory.seenTime() > lastSeenTimestamp) {
|
||||
lastSeenIndex = index;
|
||||
lastSeenTimestamp = memory.seenTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastSeenIndex == widget.allMemories[currentIndex].length - 1) {
|
||||
return 0;
|
||||
}
|
||||
return lastSeenIndex + 1;
|
||||
}
|
||||
}
|
||||
@@ -123,9 +123,14 @@ class FullScreenMemoryData extends InheritedWidget {
|
||||
class FullScreenMemory extends StatefulWidget {
|
||||
final String title;
|
||||
final int initialIndex;
|
||||
final VoidCallback? onNextMemory;
|
||||
final VoidCallback? onPreviousMemory;
|
||||
|
||||
const FullScreenMemory(
|
||||
this.title,
|
||||
this.initialIndex, {
|
||||
this.onNextMemory,
|
||||
this.onPreviousMemory,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@@ -134,7 +139,6 @@ class FullScreenMemory extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _FullScreenMemoryState extends State<FullScreenMemory> {
|
||||
PageController? _pageController;
|
||||
final _showTitle = ValueNotifier<bool>(true);
|
||||
AnimationController? _progressAnimationController;
|
||||
AnimationController? _zoomAnimationController;
|
||||
@@ -145,36 +149,24 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future.delayed(const Duration(seconds: 3), () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_showTitle.value = false;
|
||||
});
|
||||
}
|
||||
if (mounted) _showTitle.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pageController?.dispose();
|
||||
_showTitle.dispose();
|
||||
durationNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _toggleAnimation(bool pause) {
|
||||
if (_progressAnimationController != null) {
|
||||
if (pause) {
|
||||
_progressAnimationController!.stop();
|
||||
} else {
|
||||
_progressAnimationController!.forward();
|
||||
}
|
||||
}
|
||||
if (_zoomAnimationController != null) {
|
||||
if (pause) {
|
||||
_zoomAnimationController!.stop();
|
||||
} else {
|
||||
_zoomAnimationController!.forward();
|
||||
}
|
||||
if (pause) {
|
||||
_progressAnimationController?.stop();
|
||||
_zoomAnimationController?.stop();
|
||||
} else {
|
||||
_progressAnimationController?.forward();
|
||||
_zoomAnimationController?.forward();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +180,7 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
||||
}
|
||||
|
||||
void onFinalFileLoad(int duration) {
|
||||
if (_progressAnimationController!.isAnimating == true) {
|
||||
if (_progressAnimationController?.isAnimating == true) {
|
||||
_progressAnimationController!.stop();
|
||||
}
|
||||
durationNotifier.value = Duration(seconds: duration);
|
||||
@@ -203,6 +195,37 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
||||
..forward();
|
||||
}
|
||||
|
||||
void _goToNext(FullScreenMemoryData inheritedData) {
|
||||
final currentIndex = inheritedData.indexNotifier.value;
|
||||
if (currentIndex < inheritedData.memories.length - 1) {
|
||||
inheritedData.indexNotifier.value += 1;
|
||||
_onPageChange(inheritedData, currentIndex + 1);
|
||||
} else if (widget.onNextMemory != null) {
|
||||
widget.onNextMemory!();
|
||||
}
|
||||
}
|
||||
|
||||
void _goToPrevious(FullScreenMemoryData inheritedData) {
|
||||
final currentIndex = inheritedData.indexNotifier.value;
|
||||
if (currentIndex > 0) {
|
||||
inheritedData.indexNotifier.value -= 1;
|
||||
_onPageChange(inheritedData, currentIndex - 1);
|
||||
} else if (widget.onPreviousMemory != null) {
|
||||
widget.onPreviousMemory!();
|
||||
}
|
||||
}
|
||||
|
||||
void _onPageChange(FullScreenMemoryData inheritedData, int index) {
|
||||
unawaited(
|
||||
memoriesCacheService.markMemoryAsSeen(
|
||||
inheritedData.memories[index],
|
||||
false,
|
||||
),
|
||||
);
|
||||
inheritedData.indexNotifier.value = index;
|
||||
_resetAnimation();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final inheritedData = FullScreenMemoryData.of(context)!;
|
||||
@@ -217,15 +240,10 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
||||
title: ValueListenableBuilder(
|
||||
valueListenable: inheritedData.indexNotifier,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.fromLTRB(4, 8, 8, 8),
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
color: Colors.white, //same for both themes
|
||||
),
|
||||
child: Icon(Icons.close, color: Colors.white),
|
||||
),
|
||||
),
|
||||
builder: (context, value, child) {
|
||||
@@ -246,15 +264,7 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
||||
_progressAnimationController = controller;
|
||||
},
|
||||
onComplete: () {
|
||||
final currentIndex =
|
||||
inheritedData.indexNotifier.value;
|
||||
if (currentIndex <
|
||||
inheritedData.memories.length - 1) {
|
||||
_pageController!.nextPage(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.ease,
|
||||
);
|
||||
}
|
||||
_goToNext(inheritedData);
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -273,7 +283,7 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||
fontSize: 14,
|
||||
color: Colors.white,
|
||||
), //same for both themes
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -282,66 +292,51 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
||||
},
|
||||
),
|
||||
flexibleSpace: Container(
|
||||
decoration: BoxDecoration(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Colors.black.withOpacity(0.6),
|
||||
Colors.black.withOpacity(0.5),
|
||||
Colors.black54,
|
||||
Colors.black45,
|
||||
Colors.transparent,
|
||||
],
|
||||
stops: const [0, 0.6, 1],
|
||||
stops: [0, 0.6, 1],
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: const Color(0x00000000),
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
),
|
||||
body: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
const MemoryBackDrop(),
|
||||
PageView.builder(
|
||||
controller: _pageController ??= PageController(
|
||||
initialPage: widget.initialIndex,
|
||||
),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
ValueListenableBuilder<int>(
|
||||
valueListenable: inheritedData.indexNotifier,
|
||||
builder: (context, index, _) {
|
||||
if (index < inheritedData.memories.length - 1) {
|
||||
final nextFile = inheritedData.memories[index + 1].file;
|
||||
preloadThumbnail(nextFile);
|
||||
preloadFile(nextFile);
|
||||
}
|
||||
final currentFile = inheritedData.memories[index].file;
|
||||
final isVideo = currentFile.fileType == FileType.video;
|
||||
final currentMemory = inheritedData.memories[index];
|
||||
final isVideo = currentMemory.file.fileType == FileType.video;
|
||||
final currentFile = currentMemory.file;
|
||||
|
||||
return GestureDetector(
|
||||
onTapDown: (TapDownDetails details) {
|
||||
onTapUp: (TapUpDetails details) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
final edgeWidth = screenWidth * 0.20;
|
||||
if (details.localPosition.dx < edgeWidth) {
|
||||
if (index > 0) {
|
||||
_pageController!.previousPage(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.ease,
|
||||
);
|
||||
}
|
||||
_goToPrevious(inheritedData);
|
||||
} else if (details.localPosition.dx >
|
||||
screenWidth - edgeWidth) {
|
||||
if (index < (inheritedData.memories.length - 1)) {
|
||||
_pageController!.nextPage(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.ease,
|
||||
);
|
||||
}
|
||||
_goToNext(inheritedData);
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
isVideo ? null: _toggleAnimation(true);
|
||||
},
|
||||
onLongPressUp: () {
|
||||
isVideo ? null : _toggleAnimation(false);
|
||||
},
|
||||
onLongPress: () => isVideo ? null : _toggleAnimation(true),
|
||||
onLongPressUp: () => isVideo ? null : _toggleAnimation(false),
|
||||
child: MemoriesZoomWidget(
|
||||
scaleController: (controller) {
|
||||
_zoomAnimationController = controller;
|
||||
@@ -349,12 +344,11 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
||||
zoomIn: index % 2 == 0,
|
||||
isVideo: isVideo,
|
||||
child: FileWidget(
|
||||
inheritedData.memories[index].file,
|
||||
currentFile,
|
||||
autoPlay: false,
|
||||
tagPrefix: "memories",
|
||||
backgroundDecoration: const BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
backgroundDecoration:
|
||||
const BoxDecoration(color: Colors.transparent),
|
||||
isFromMemories: true,
|
||||
playbackCallback: (isPlaying) {
|
||||
_toggleAnimation(!isPlaying);
|
||||
@@ -366,23 +360,13 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
||||
),
|
||||
);
|
||||
},
|
||||
onPageChanged: (index) async {
|
||||
unawaited(
|
||||
memoriesCacheService.markMemoryAsSeen(
|
||||
inheritedData.memories[index],
|
||||
inheritedData.memories.length == index + 1,
|
||||
),
|
||||
);
|
||||
inheritedData.indexNotifier.value = index;
|
||||
_resetAnimation();
|
||||
},
|
||||
itemCount: inheritedData.memories.length,
|
||||
),
|
||||
SafeArea(
|
||||
top: false,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 72),
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: _showTitle,
|
||||
builder: (context, value, _) {
|
||||
return AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
@@ -398,8 +382,7 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
||||
style: getEnteTextTheme(context)
|
||||
.largeBold
|
||||
.copyWith(
|
||||
color:
|
||||
Colors.white, //same for both themes
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -409,7 +392,6 @@ class _FullScreenMemoryState extends State<FullScreenMemory> {
|
||||
: const MemoryCounter(),
|
||||
);
|
||||
},
|
||||
valueListenable: _showTitle,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -427,6 +409,8 @@ class BottomIcons extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final inheritedData = FullScreenMemoryData.of(context)!;
|
||||
final fullScreenState =
|
||||
context.findAncestorStateOfType<_FullScreenMemoryState>();
|
||||
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: inheritedData.indexNotifier,
|
||||
@@ -438,8 +422,10 @@ class BottomIcons extends StatelessWidget {
|
||||
Platform.isAndroid ? Icons.info_outline : CupertinoIcons.info,
|
||||
color: Colors.white, //same for both themes
|
||||
),
|
||||
onPressed: () {
|
||||
showDetailsSheet(context, currentFile);
|
||||
onPressed: () async {
|
||||
fullScreenState?._toggleAnimation(true);
|
||||
await showDetailsSheet(context, currentFile);
|
||||
fullScreenState?._toggleAnimation(false);
|
||||
},
|
||||
),
|
||||
];
|
||||
@@ -455,6 +441,7 @@ class BottomIcons extends StatelessWidget {
|
||||
color: Colors.white, //same for both themes
|
||||
),
|
||||
onPressed: () async {
|
||||
fullScreenState?._toggleAnimation(true);
|
||||
await showSingleFileDeleteSheet(
|
||||
context,
|
||||
inheritedData
|
||||
@@ -467,6 +454,7 @@ class BottomIcons extends StatelessWidget {
|
||||
},
|
||||
},
|
||||
);
|
||||
fullScreenState?._toggleAnimation(false);
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
@@ -481,8 +469,10 @@ class BottomIcons extends StatelessWidget {
|
||||
Icons.adaptive.share,
|
||||
color: Colors.white, //same for both themes
|
||||
),
|
||||
onPressed: () {
|
||||
share(context, [currentFile]);
|
||||
onPressed: () async {
|
||||
fullScreenState?._toggleAnimation(true);
|
||||
await share(context, [currentFile]);
|
||||
fullScreenState?._toggleAnimation(false);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -132,10 +132,13 @@ class _MemoriesWidgetState extends State<MemoriesWidget> {
|
||||
itemBuilder: (context, itemIndex) {
|
||||
return MemoryCoverWidget(
|
||||
memories: collatedMemories[itemIndex].$1,
|
||||
allMemories: collatedMemories.map((e) => e.$1).toList(),
|
||||
controller: _controller,
|
||||
maxHeight: _maxHeight,
|
||||
maxWidth: _maxWidth,
|
||||
title: collatedMemories[itemIndex].$2,
|
||||
allTitle: collatedMemories.map((e) => e.$2).toList(),
|
||||
currentMemoryIndex: itemIndex,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -4,12 +4,13 @@ import "package:photos/models/memories/memory.dart";
|
||||
import "package:photos/theme/colors.dart";
|
||||
import "package:photos/theme/effects.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import "package:photos/ui/home/memories/full_screen_memory.dart";
|
||||
import "package:photos/ui/home/memories/all_memories_page.dart";
|
||||
import "package:photos/ui/viewer/file/thumbnail_widget.dart";
|
||||
import "package:photos/utils/navigation_util.dart";
|
||||
|
||||
class MemoryCoverWidget extends StatefulWidget {
|
||||
final List<Memory> memories;
|
||||
final List<List<Memory>> allMemories;
|
||||
final ScrollController controller;
|
||||
final double maxHeight;
|
||||
final double maxWidth;
|
||||
@@ -17,13 +18,18 @@ class MemoryCoverWidget extends StatefulWidget {
|
||||
static const aspectRatio = 0.68;
|
||||
static const horizontalPadding = 2.5;
|
||||
final String title;
|
||||
final List<String> allTitle;
|
||||
final int currentMemoryIndex;
|
||||
|
||||
const MemoryCoverWidget({
|
||||
required this.memories,
|
||||
required this.allMemories,
|
||||
required this.controller,
|
||||
required this.maxHeight,
|
||||
required this.maxWidth,
|
||||
required this.title,
|
||||
required this.allTitle,
|
||||
required this.currentMemoryIndex,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@@ -56,15 +62,14 @@ class _MemoryCoverWidgetState extends State<MemoryCoverWidget> {
|
||||
horizontal: MemoryCoverWidget.horizontalPadding,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
onTap: () async {
|
||||
await routeToPage(
|
||||
context,
|
||||
FullScreenMemoryDataUpdater(
|
||||
initialIndex: index,
|
||||
memories: widget.memories,
|
||||
child: FullScreenMemory(title, index),
|
||||
AllMemoriesPage(
|
||||
initialPageIndex: widget.currentMemoryIndex,
|
||||
allMemories: widget.allMemories,
|
||||
allTitles: widget.allTitle,
|
||||
),
|
||||
forceCustomPageRoute: true,
|
||||
);
|
||||
setState(() {});
|
||||
}, //Adding this row is a workaround for making height of memory cover
|
||||
@@ -73,8 +78,8 @@ class _MemoryCoverWidgetState extends State<MemoryCoverWidget> {
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
height: widget.maxHeight ,
|
||||
width: widget.maxWidth ,
|
||||
height: widget.maxHeight,
|
||||
width: widget.maxWidth,
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: brightness == Brightness.dark
|
||||
? [
|
||||
@@ -114,7 +119,7 @@ class _MemoryCoverWidgetState extends State<MemoryCoverWidget> {
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 8 ,
|
||||
bottom: 8,
|
||||
child: SizedBox(
|
||||
width: widget.maxWidth,
|
||||
child: Padding(
|
||||
@@ -162,7 +167,7 @@ class _MemoryCoverWidgetState extends State<MemoryCoverWidget> {
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 8 ,
|
||||
bottom: 8,
|
||||
child: SizedBox(
|
||||
width: widget.maxWidth,
|
||||
child: Padding(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/l10n/l10n.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
import 'package:photos/ui/components/buttons/button_widget.dart';
|
||||
|
||||
@@ -99,15 +99,17 @@ class _VideoWidgetState extends State<VideoWidget> {
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
if (widget.isFromMemories) return;
|
||||
showControlsNotifier.value = !showControlsNotifier.value;
|
||||
if (widget.playbackCallback != null) {
|
||||
widget.playbackCallback!(
|
||||
!showControlsNotifier.value,
|
||||
);
|
||||
}
|
||||
},
|
||||
onTap: widget.isFromMemories
|
||||
? null
|
||||
: () {
|
||||
showControlsNotifier.value =
|
||||
!showControlsNotifier.value;
|
||||
if (widget.playbackCallback != null) {
|
||||
widget.playbackCallback!(
|
||||
!showControlsNotifier.value,
|
||||
);
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
if (widget.isFromMemories) {
|
||||
widget.playbackCallback?.call(false);
|
||||
|
||||
@@ -284,14 +284,16 @@ class _VideoWidgetNativeState extends State<VideoWidgetNative>
|
||||
),
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
if (widget.isFromMemories) return;
|
||||
_showControls.value = !_showControls.value;
|
||||
if (widget.playbackCallback != null) {
|
||||
widget.playbackCallback!(!_showControls.value);
|
||||
}
|
||||
_elTooltipController.hide();
|
||||
},
|
||||
onTap: widget.isFromMemories
|
||||
? null
|
||||
: () {
|
||||
_showControls.value = !_showControls.value;
|
||||
if (widget.playbackCallback != null) {
|
||||
widget
|
||||
.playbackCallback!(!_showControls.value);
|
||||
}
|
||||
_elTooltipController.hide();
|
||||
},
|
||||
onLongPress: () {
|
||||
if (widget.isFromMemories) {
|
||||
widget.playbackCallback?.call(false);
|
||||
|
||||
Reference in New Issue
Block a user