From 4063f75d4a0dd3d7052b97c025bb91c54e04b084 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 4 Jan 2024 10:11:24 +0530 Subject: [PATCH 01/20] created a new minimal FullScreenMemoryWidget with only delete option --- .../home/memories/full_screen_memory_new.dart | 78 +++++++++++++++++++ lib/ui/home/memories/memory_cover_widget.dart | 4 +- 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 lib/ui/home/memories/full_screen_memory_new.dart diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart new file mode 100644 index 000000000..240df4bd6 --- /dev/null +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -0,0 +1,78 @@ +import "dart:io"; + +import "package:flutter/cupertino.dart"; +import "package:flutter/material.dart"; +import "package:photos/models/memory.dart"; +import "package:photos/ui/actions/file/file_actions.dart"; +import "package:photos/ui/viewer/file/file_widget.dart"; + +class FullScreenMemoryNew extends StatefulWidget { + final String title; + final List memories; + final int index; + const FullScreenMemoryNew(this.title, this.memories, this.index, {super.key}); + + @override + State createState() => _FullScreenMemoryNewState(); +} + +class _FullScreenMemoryNewState extends State { + late final ValueNotifier _currentIndex; + + @override + void initState() { + super.initState(); + _currentIndex = ValueNotifier(widget.index); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + extendBodyBehindAppBar: true, + appBar: AppBar(), + body: PageView.builder( + itemBuilder: (context, index) { + return Stack( + alignment: Alignment.bottomCenter, + children: [ + FileWidget( + widget.memories[index].file, + autoPlay: false, + tagPrefix: "memories", + backgroundDecoration: const BoxDecoration( + color: Colors.transparent, + ), + ), + IconButton( + icon: Icon( + Platform.isAndroid + ? Icons.delete_outline + : CupertinoIcons.delete, + color: Colors.white, //same for both themes + ), + onPressed: () async { + await showSingleFileDeleteSheet( + context, + widget.memories[_currentIndex.value].file, + onFileRemoved: (file) => + {onFileDeleted(widget.memories[_currentIndex.value])}, + ); + }, + ), + ], + ); + }, + onPageChanged: (index) { + _currentIndex.value = index; + }, + itemCount: widget.memories.length, + ), + ); + } + + Future onFileDeleted(Memory removedMemory) async { + setState(() { + widget.memories.remove(removedMemory); + }); + } +} diff --git a/lib/ui/home/memories/memory_cover_widget.dart b/lib/ui/home/memories/memory_cover_widget.dart index 91e1159d3..bad08dd20 100644 --- a/lib/ui/home/memories/memory_cover_widget.dart +++ b/lib/ui/home/memories/memory_cover_widget.dart @@ -2,7 +2,7 @@ import "package:flutter/material.dart"; import "package:photos/generated/l10n.dart"; import "package:photos/models/memory.dart"; import "package:photos/theme/ente_theme.dart"; -import "package:photos/ui/home/memories/full_screen_memory.dart"; +import "package:photos/ui/home/memories/full_screen_memory_new.dart"; import "package:photos/ui/viewer/file/thumbnail_widget.dart"; import "package:photos/utils/navigation_util.dart"; @@ -27,7 +27,7 @@ class _MemoryCovertWidgetState extends State { onTap: () async { await routeToPage( context, - FullScreenMemory(title, widget.memories, index), + FullScreenMemoryNew(title, widget.memories, index), forceCustomPageRoute: true, ); setState(() {}); From 060b516058b8c8ae59215e0b77e75881d033e54b Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 4 Jan 2024 13:14:56 +0530 Subject: [PATCH 02/20] used an InheritedWidget to hold list of memories and update the InheritedWidget when an item is deleted, which rebuilds widgets that depend on the inheritedWidget --- .../home/memories/full_screen_memory_new.dart | 148 ++++++++++++++---- lib/ui/home/memories/memory_cover_widget.dart | 6 +- 2 files changed, 124 insertions(+), 30 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 240df4bd6..7c718eb54 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -2,31 +2,107 @@ import "dart:io"; import "package:flutter/cupertino.dart"; import "package:flutter/material.dart"; +import "package:photos/core/configuration.dart"; import "package:photos/models/memory.dart"; import "package:photos/ui/actions/file/file_actions.dart"; import "package:photos/ui/viewer/file/file_widget.dart"; +class FullScreenMemoryDataUpdater extends StatefulWidget { + final List memories; + final int initialIndex; + final Widget child; + const FullScreenMemoryDataUpdater({ + required this.memories, + required this.initialIndex, + required this.child, + super.key, + }); + + @override + State createState() => + _FullScreenMemoryDataUpdaterState(); +} + +class _FullScreenMemoryDataUpdaterState + extends State { + late ValueNotifier indexNotifier; + + @override + void initState() { + super.initState(); + indexNotifier = ValueNotifier(widget.initialIndex); + } + + @override + void dispose() { + indexNotifier.dispose(); + super.dispose(); + } + + void removeCurrentMemory() { + setState(() { + widget.memories.removeAt(indexNotifier.value); + }); + } + + @override + Widget build(BuildContext context) { + return FullScreenMemoryData( + memories: widget.memories, + indexNotifier: indexNotifier, + removeCurrentMemory: removeCurrentMemory, + child: widget.child, + ); + } +} + +class FullScreenMemoryData extends InheritedWidget { + final List memories; + final ValueNotifier indexNotifier; + final VoidCallback removeCurrentMemory; + + const FullScreenMemoryData({ + required this.memories, + required this.indexNotifier, + required this.removeCurrentMemory, + required Widget child, + Key? key, + }) : super(child: child, key: key); + + static FullScreenMemoryData? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } + + @override + bool updateShouldNotify(FullScreenMemoryData oldWidget) { + // Checking oldWidget.memories.length != memories.length here doesn't work + //because the old widget and new widget reference the same memories list. + return true; + } +} + class FullScreenMemoryNew extends StatefulWidget { final String title; - final List memories; - final int index; - const FullScreenMemoryNew(this.title, this.memories, this.index, {super.key}); + final int initialIndex; + const FullScreenMemoryNew( + this.title, + this.initialIndex, { + super.key, + }); @override State createState() => _FullScreenMemoryNewState(); } class _FullScreenMemoryNewState extends State { - late final ValueNotifier _currentIndex; - @override void initState() { super.initState(); - _currentIndex = ValueNotifier(widget.index); } @override Widget build(BuildContext context) { + final inheritedData = FullScreenMemoryData.of(context)!; return Scaffold( extendBodyBehindAppBar: true, appBar: AppBar(), @@ -36,43 +112,57 @@ class _FullScreenMemoryNewState extends State { alignment: Alignment.bottomCenter, children: [ FileWidget( - widget.memories[index].file, + inheritedData.memories[index].file, autoPlay: false, tagPrefix: "memories", backgroundDecoration: const BoxDecoration( color: Colors.transparent, ), ), - IconButton( - icon: Icon( - Platform.isAndroid - ? Icons.delete_outline - : CupertinoIcons.delete, - color: Colors.white, //same for both themes - ), - onPressed: () async { - await showSingleFileDeleteSheet( - context, - widget.memories[_currentIndex.value].file, - onFileRemoved: (file) => - {onFileDeleted(widget.memories[_currentIndex.value])}, - ); - }, - ), + Configuration.instance.getUserID() != + inheritedData.memories[index].file.ownerID + ? Padding( + padding: const EdgeInsets.all(64.0), + child: Container( + color: Colors.red, + height: 30, + width: 30, + ), + ) + : const SizedBox.shrink(), + const BottomIcons(), ], ); }, onPageChanged: (index) { - _currentIndex.value = index; + inheritedData.indexNotifier.value = index; }, - itemCount: widget.memories.length, + itemCount: inheritedData.memories.length, ), ); } +} - Future onFileDeleted(Memory removedMemory) async { - setState(() { - widget.memories.remove(removedMemory); - }); +class BottomIcons extends StatelessWidget { + const BottomIcons({super.key}); + + @override + Widget build(BuildContext context) { + final inheritedData = FullScreenMemoryData.of(context)!; + return IconButton( + icon: Icon( + Platform.isAndroid ? Icons.delete_outline : CupertinoIcons.delete, + color: Colors.white, //same for both themes + ), + onPressed: () async { + await showSingleFileDeleteSheet( + context, + inheritedData.memories[inheritedData.indexNotifier.value].file, + onFileRemoved: (file) => { + inheritedData.removeCurrentMemory.call(), + }, + ); + }, + ); } } diff --git a/lib/ui/home/memories/memory_cover_widget.dart b/lib/ui/home/memories/memory_cover_widget.dart index bad08dd20..1be67f8dd 100644 --- a/lib/ui/home/memories/memory_cover_widget.dart +++ b/lib/ui/home/memories/memory_cover_widget.dart @@ -27,7 +27,11 @@ class _MemoryCovertWidgetState extends State { onTap: () async { await routeToPage( context, - FullScreenMemoryNew(title, widget.memories, index), + FullScreenMemoryDataUpdater( + initialIndex: index, + memories: widget.memories, + child: FullScreenMemoryNew(title, index), + ), forceCustomPageRoute: true, ); setState(() {}); From 02263a4d882df1c8433a5e541ae76392000a2145 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 4 Jan 2024 16:00:51 +0530 Subject: [PATCH 03/20] Add bottom icons to memories --- .../home/memories/full_screen_memory_new.dart | 111 ++++++++++++++---- 1 file changed, 86 insertions(+), 25 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 7c718eb54..ba9e61626 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -6,6 +6,8 @@ import "package:photos/core/configuration.dart"; import "package:photos/models/memory.dart"; import "package:photos/ui/actions/file/file_actions.dart"; import "package:photos/ui/viewer/file/file_widget.dart"; +import "package:photos/ui/viewer/file_details/favorite_widget.dart"; +import "package:photos/utils/share_util.dart"; class FullScreenMemoryDataUpdater extends StatefulWidget { final List memories; @@ -95,11 +97,18 @@ class FullScreenMemoryNew extends StatefulWidget { } class _FullScreenMemoryNewState extends State { + PageController? _pageController; @override void initState() { super.initState(); } + @override + void dispose() { + _pageController?.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final inheritedData = FullScreenMemoryData.of(context)!; @@ -107,6 +116,9 @@ class _FullScreenMemoryNewState extends State { extendBodyBehindAppBar: true, appBar: AppBar(), body: PageView.builder( + controller: _pageController ??= PageController( + initialPage: widget.initialIndex, + ), itemBuilder: (context, index) { return Stack( alignment: Alignment.bottomCenter, @@ -119,18 +131,16 @@ class _FullScreenMemoryNewState extends State { color: Colors.transparent, ), ), - Configuration.instance.getUserID() != - inheritedData.memories[index].file.ownerID - ? Padding( - padding: const EdgeInsets.all(64.0), - child: Container( - color: Colors.red, - height: 30, - width: 30, - ), - ) - : const SizedBox.shrink(), - const BottomIcons(), + BottomIcons(index), + Padding( + padding: const EdgeInsets.all(120), + child: Container( + color: Colors.black, + child: Text( + inheritedData.memories[index].file.generatedID.toString(), + ), + ), + ), ], ); }, @@ -144,25 +154,76 @@ class _FullScreenMemoryNewState extends State { } class BottomIcons extends StatelessWidget { - const BottomIcons({super.key}); + final int pageViewIndex; + const BottomIcons(this.pageViewIndex, {super.key}); @override Widget build(BuildContext context) { final inheritedData = FullScreenMemoryData.of(context)!; - return IconButton( - icon: Icon( - Platform.isAndroid ? Icons.delete_outline : CupertinoIcons.delete, - color: Colors.white, //same for both themes + final currentFile = inheritedData.memories[pageViewIndex].file; + + final List rowChildren = [ + IconButton( + icon: Icon( + Platform.isAndroid ? Icons.info_outline : CupertinoIcons.info, + color: Colors.white, //same for both themes + ), + onPressed: () { + showDetailsSheet(context, currentFile); + }, ), - onPressed: () async { - await showSingleFileDeleteSheet( - context, - inheritedData.memories[inheritedData.indexNotifier.value].file, - onFileRemoved: (file) => { - inheritedData.removeCurrentMemory.call(), + ]; + rowChildren.add( + Padding( + padding: const EdgeInsets.all(8), + child: Text(currentFile.generatedID.toString()), + ), + ); + if (currentFile.ownerID == null || + (Configuration.instance.getUserID() ?? 0) == currentFile.ownerID) { + rowChildren.addAll([ + IconButton( + icon: Icon( + Platform.isAndroid ? Icons.delete_outline : CupertinoIcons.delete, + color: Colors.white, //same for both themes + ), + onPressed: () async { + await showSingleFileDeleteSheet( + context, + inheritedData.memories[inheritedData.indexNotifier.value].file, + onFileRemoved: (file) => { + inheritedData.removeCurrentMemory.call(), + }, + ); }, - ); - }, + ), + SizedBox( + height: 32, + child: FavoriteWidget(currentFile), + ), + ]); + } + rowChildren.add( + IconButton( + icon: Icon( + Icons.adaptive.share, + color: Colors.white, //same for both themes + ), + onPressed: () { + share(context, [currentFile]); + }, + ), + ); + + return SafeArea( + child: Container( + alignment: Alignment.bottomCenter, + padding: const EdgeInsets.fromLTRB(12, 0, 12, 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: rowChildren, + ), + ), ); } } From 6faa93c90cc968126fe6d413257988e4aa9feef3 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 4 Jan 2024 19:01:32 +0530 Subject: [PATCH 04/20] pop FullScreenMemory screen when last memory is deleted --- lib/ui/home/memories/full_screen_memory_new.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index ba9e61626..e6e97e926 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -193,6 +193,10 @@ class BottomIcons extends StatelessWidget { inheritedData.memories[inheritedData.indexNotifier.value].file, onFileRemoved: (file) => { inheritedData.removeCurrentMemory.call(), + if (inheritedData.memories.isEmpty) + { + Navigator.of(context).pop(), + }, }, ); }, From f143f90ec0e1c78873e443440fea3fd5e63c2b6a Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 4 Jan 2024 19:09:06 +0530 Subject: [PATCH 05/20] fix: handle range error when all memories are deleted and FullScreenMemory screen is popped --- lib/ui/home/memories/memory_cover_widget.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/ui/home/memories/memory_cover_widget.dart b/lib/ui/home/memories/memory_cover_widget.dart index 1be67f8dd..df1b6d5b3 100644 --- a/lib/ui/home/memories/memory_cover_widget.dart +++ b/lib/ui/home/memories/memory_cover_widget.dart @@ -21,6 +21,11 @@ class MemoryCovertWidget extends StatefulWidget { class _MemoryCovertWidgetState extends State { @override Widget build(BuildContext context) { + //memories will be empty if all memories are deleted and setState is called + //after FullScreenMemory screen is popped + if (widget.memories.isEmpty) { + return const SizedBox.shrink(); + } final index = _getNextMemoryIndex(); final title = _getTitle(widget.memories[index]); return GestureDetector( From 76520c569cad74da888f220baed1086c13a016ab Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 10:43:34 +0530 Subject: [PATCH 06/20] preload thumbnail and file of next memory --- lib/ui/home/memories/full_screen_memory_new.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index e6e97e926..d76757a88 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -7,6 +7,7 @@ import "package:photos/models/memory.dart"; import "package:photos/ui/actions/file/file_actions.dart"; import "package:photos/ui/viewer/file/file_widget.dart"; import "package:photos/ui/viewer/file_details/favorite_widget.dart"; +import "package:photos/utils/file_util.dart"; import "package:photos/utils/share_util.dart"; class FullScreenMemoryDataUpdater extends StatefulWidget { @@ -120,6 +121,11 @@ class _FullScreenMemoryNewState extends State { initialPage: widget.initialIndex, ), itemBuilder: (context, index) { + if (index < inheritedData.memories.length - 1) { + final nextFile = inheritedData.memories[index + 1].file; + preloadThumbnail(nextFile); + preloadFile(nextFile); + } return Stack( alignment: Alignment.bottomCenter, children: [ From aed0a2f3dc45d29b488c8db68db4aa3eca9b76da Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 11:21:43 +0530 Subject: [PATCH 07/20] add memory counter on bottom of FullScreenMemory --- .../home/memories/full_screen_memory_new.dart | 109 ++++++++++++------ 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index d76757a88..61743d100 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -4,6 +4,7 @@ import "package:flutter/cupertino.dart"; import "package:flutter/material.dart"; import "package:photos/core/configuration.dart"; import "package:photos/models/memory.dart"; +import "package:photos/theme/text_style.dart"; import "package:photos/ui/actions/file/file_actions.dart"; import "package:photos/ui/viewer/file/file_widget.dart"; import "package:photos/ui/viewer/file_details/favorite_widget.dart"; @@ -99,6 +100,7 @@ class FullScreenMemoryNew extends StatefulWidget { class _FullScreenMemoryNewState extends State { PageController? _pageController; + @override void initState() { super.initState(); @@ -113,47 +115,61 @@ class _FullScreenMemoryNewState extends State { @override Widget build(BuildContext context) { final inheritedData = FullScreenMemoryData.of(context)!; + return Scaffold( extendBodyBehindAppBar: true, appBar: AppBar(), - body: PageView.builder( - controller: _pageController ??= PageController( - initialPage: widget.initialIndex, - ), - itemBuilder: (context, index) { - if (index < inheritedData.memories.length - 1) { - final nextFile = inheritedData.memories[index + 1].file; - preloadThumbnail(nextFile); - preloadFile(nextFile); - } - return Stack( - alignment: Alignment.bottomCenter, - children: [ - FileWidget( - inheritedData.memories[index].file, - autoPlay: false, - tagPrefix: "memories", - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, - ), - ), - BottomIcons(index), - Padding( - padding: const EdgeInsets.all(120), - child: Container( - color: Colors.black, - child: Text( - inheritedData.memories[index].file.generatedID.toString(), + body: Stack( + alignment: Alignment.bottomCenter, + children: [ + PageView.builder( + controller: _pageController ??= PageController( + initialPage: widget.initialIndex, + ), + itemBuilder: (context, index) { + if (index < inheritedData.memories.length - 1) { + final nextFile = inheritedData.memories[index + 1].file; + preloadThumbnail(nextFile); + preloadFile(nextFile); + } + return Stack( + alignment: Alignment.bottomCenter, + children: [ + FileWidget( + inheritedData.memories[index].file, + autoPlay: false, + tagPrefix: "memories", + backgroundDecoration: const BoxDecoration( + color: Colors.transparent, + ), ), - ), - ), - ], - ); - }, - onPageChanged: (index) { - inheritedData.indexNotifier.value = index; - }, - itemCount: inheritedData.memories.length, + BottomIcons(index), + Padding( + padding: const EdgeInsets.only(bottom: 120, right: 240), + child: Container( + color: Colors.black, + child: Text( + inheritedData.memories[index].file.generatedID + .toString(), + ), + ), + ), + ], + ); + }, + onPageChanged: (index) { + inheritedData.indexNotifier.value = index; + }, + itemCount: inheritedData.memories.length, + ), + const SafeArea( + top: false, + child: Padding( + padding: EdgeInsets.only(bottom: 84), + child: MemoryCounter(), + ), + ), + ], ), ); } @@ -226,6 +242,7 @@ class BottomIcons extends StatelessWidget { ); return SafeArea( + top: false, child: Container( alignment: Alignment.bottomCenter, padding: const EdgeInsets.fromLTRB(12, 0, 12, 20), @@ -237,3 +254,21 @@ class BottomIcons extends StatelessWidget { ); } } + +class MemoryCounter extends StatelessWidget { + const MemoryCounter({super.key}); + + @override + Widget build(BuildContext context) { + final inheritedData = FullScreenMemoryData.of(context)!; + return ValueListenableBuilder( + valueListenable: inheritedData.indexNotifier, + builder: (context, value, _) { + return Text( + "${value + 1}/${inheritedData.memories.length}", + style: darkTextTheme.bodyMuted, + ); + }, + ); + } +} From a808831906fecaf208404da923cb27a190c050de Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 11:37:42 +0530 Subject: [PATCH 08/20] show 'x years ago' title when opening memories --- .../home/memories/full_screen_memory_new.dart | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 61743d100..b470209f4 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -100,15 +100,24 @@ class FullScreenMemoryNew extends StatefulWidget { class _FullScreenMemoryNewState extends State { PageController? _pageController; + final _showTitle = ValueNotifier(true); @override void initState() { + Future.delayed(const Duration(seconds: 3), () { + if (mounted) { + setState(() { + _showTitle.value = false; + }); + } + }); super.initState(); } @override void dispose() { _pageController?.dispose(); + _showTitle.dispose(); super.dispose(); } @@ -162,11 +171,26 @@ class _FullScreenMemoryNewState extends State { }, itemCount: inheritedData.memories.length, ), - const SafeArea( + SafeArea( top: false, child: Padding( - padding: EdgeInsets.only(bottom: 84), - child: MemoryCounter(), + padding: const EdgeInsets.only(bottom: 84), + child: ValueListenableBuilder( + builder: (context, value, _) { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 250), + switchInCurve: Curves.easeIn, + switchOutCurve: Curves.easeOut, + child: value + ? Text( + widget.title, + style: darkTextTheme.h2, + ) + : const MemoryCounter(), + ); + }, + valueListenable: _showTitle, + ), ), ), ], From f05f7a86a7161455d5f894f0047ee6c51f17e7b5 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 11:51:45 +0530 Subject: [PATCH 09/20] add bottom gradient --- .../home/memories/full_screen_memory_new.dart | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index b470209f4..7ac57ab43 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -152,6 +152,7 @@ class _FullScreenMemoryNewState extends State { color: Colors.transparent, ), ), + const BottomGradient(), BottomIcons(index), Padding( padding: const EdgeInsets.only(bottom: 120, right: 240), @@ -174,7 +175,7 @@ class _FullScreenMemoryNewState extends State { SafeArea( top: false, child: Padding( - padding: const EdgeInsets.only(bottom: 84), + padding: const EdgeInsets.only(bottom: 72), child: ValueListenableBuilder( builder: (context, value, _) { return AnimatedSwitcher( @@ -296,3 +297,28 @@ class MemoryCounter extends StatelessWidget { ); } } + +class BottomGradient extends StatelessWidget { + const BottomGradient({super.key}); + + @override + Widget build(BuildContext context) { + return IgnorePointer( + child: Container( + height: 124, + width: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Colors.black.withOpacity(0.5), //same for both themes + Colors.transparent, + ], + stops: const [0, 0.8], + ), + ), + ), + ); + } +} From 212423f955de2baceaf9541088f364f3c44a1181 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 14:36:06 +0530 Subject: [PATCH 10/20] show date and step progress indicator on top of FullScreenMemory screen --- .../home/memories/full_screen_memory_new.dart | 61 ++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 7ac57ab43..d41fec416 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -2,6 +2,7 @@ import "dart:io"; import "package:flutter/cupertino.dart"; import "package:flutter/material.dart"; +import "package:intl/intl.dart"; import "package:photos/core/configuration.dart"; import "package:photos/models/memory.dart"; import "package:photos/theme/text_style.dart"; @@ -10,6 +11,7 @@ import "package:photos/ui/viewer/file/file_widget.dart"; import "package:photos/ui/viewer/file_details/favorite_widget.dart"; import "package:photos/utils/file_util.dart"; import "package:photos/utils/share_util.dart"; +import "package:step_progress_indicator/step_progress_indicator.dart"; class FullScreenMemoryDataUpdater extends StatefulWidget { final List memories; @@ -124,10 +126,65 @@ class _FullScreenMemoryNewState extends State { @override Widget build(BuildContext context) { final inheritedData = FullScreenMemoryData.of(context)!; - + final showStepProgressIndicator = inheritedData.memories.length < 60; return Scaffold( extendBodyBehindAppBar: true, - appBar: AppBar(), + appBar: AppBar( + toolbarHeight: 84, + automaticallyImplyLeading: false, + title: ValueListenableBuilder( + valueListenable: inheritedData.indexNotifier, + child: Padding( + padding: const EdgeInsets.only(right: 16), + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: const Icon( + Icons.close, + color: Colors.white, //same for both themes + ), + ), + ), + builder: (context, value, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + showStepProgressIndicator + ? StepProgressIndicator( + totalSteps: inheritedData.memories.length, + currentStep: value + 1, + size: 2, + selectedColor: Colors.white, //same for both themes + unselectedColor: Colors.white.withOpacity(0.4), + ) + : const SizedBox.shrink(), + const SizedBox( + height: 18, + ), + Row( + children: [ + child!, + Text( + DateFormat.yMMMd( + Localizations.localeOf(context).languageCode, + ).format( + DateTime.fromMicrosecondsSinceEpoch( + inheritedData.memories[value].file.creationTime!, + ), + ), + style: Theme.of(context).textTheme.titleMedium!.copyWith( + fontSize: 14, + color: Colors.white, + ), //same for both themes + ), + ], + ), + ], + ); + }, + ), + ), body: Stack( alignment: Alignment.bottomCenter, children: [ From 8730bc5451e6f983db0e5100ebdc2289d8d913cc Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 14:39:34 +0530 Subject: [PATCH 11/20] don't show memory counter if step progress indicator is visible --- lib/ui/home/memories/full_screen_memory_new.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index d41fec416..75460f2e8 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -244,7 +244,9 @@ class _FullScreenMemoryNewState extends State { widget.title, style: darkTextTheme.h2, ) - : const MemoryCounter(), + : showStepProgressIndicator + ? const SizedBox.shrink() + : const MemoryCounter(), ); }, valueListenable: _showTitle, From c8c421e01157d80a535974a9f2c0cf4b6a5c08e9 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 14:55:06 +0530 Subject: [PATCH 12/20] Add tap to go to previous or next story in FullScreenMemory screen --- .../home/memories/full_screen_memory_new.dart | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 75460f2e8..63addcf36 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -201,12 +201,34 @@ class _FullScreenMemoryNewState extends State { return Stack( alignment: Alignment.bottomCenter, children: [ - FileWidget( - inheritedData.memories[index].file, - autoPlay: false, - tagPrefix: "memories", - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, + GestureDetector( + onTapDown: (TapDownDetails details) { + final screenWidth = MediaQuery.of(context).size.width; + final edgeWidth = screenWidth * 0.33; + if (details.localPosition.dx < edgeWidth) { + if (index > 0) { + _pageController!.previousPage( + duration: const Duration(milliseconds: 250), + curve: Curves.ease, + ); + } + } else if (details.localPosition.dx > + screenWidth - edgeWidth) { + if (index < (inheritedData.memories.length - 1)) { + _pageController!.nextPage( + duration: const Duration(milliseconds: 250), + curve: Curves.ease, + ); + } + } + }, + child: FileWidget( + inheritedData.memories[index].file, + autoPlay: false, + tagPrefix: "memories", + backgroundDecoration: const BoxDecoration( + color: Colors.transparent, + ), ), ), const BottomGradient(), From 45f1fd1bd1ca4f45ef7a203ddf4451fb63a938c4 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 15:00:01 +0530 Subject: [PATCH 13/20] mark inital memory as seen on init --- lib/ui/home/memories/full_screen_memory_new.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 63addcf36..2fe59f54c 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -5,6 +5,7 @@ import "package:flutter/material.dart"; import "package:intl/intl.dart"; import "package:photos/core/configuration.dart"; import "package:photos/models/memory.dart"; +import "package:photos/services/memories_service.dart"; import "package:photos/theme/text_style.dart"; import "package:photos/ui/actions/file/file_actions.dart"; import "package:photos/ui/viewer/file/file_widget.dart"; @@ -37,6 +38,8 @@ class _FullScreenMemoryDataUpdaterState void initState() { super.initState(); indexNotifier = ValueNotifier(widget.initialIndex); + MemoriesService.instance + .markMemoryAsSeen(widget.memories[widget.initialIndex]); } @override @@ -106,6 +109,7 @@ class _FullScreenMemoryNewState extends State { @override void initState() { + super.initState(); Future.delayed(const Duration(seconds: 3), () { if (mounted) { setState(() { @@ -113,7 +117,6 @@ class _FullScreenMemoryNewState extends State { }); } }); - super.initState(); } @override From 59add2ed04c097a0f85437d885ab342b7fcac7de Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 15:06:37 +0530 Subject: [PATCH 14/20] add gradient on top of FullScreenMemory for better visibilty of top widgets --- lib/ui/home/memories/full_screen_memory_new.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 2fe59f54c..ec7747a1e 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -187,6 +187,22 @@ class _FullScreenMemoryNewState extends State { ); }, ), + flexibleSpace: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.black.withOpacity(0.6), + Colors.black.withOpacity(0.5), + Colors.transparent, + ], + stops: const [0, 0.6, 1], + ), + ), + ), + backgroundColor: const Color(0x00000000), + elevation: 0, ), body: Stack( alignment: Alignment.bottomCenter, From e599f2bcec566aff7dc139b0ac60c155f11e323a Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 15:12:49 +0530 Subject: [PATCH 15/20] minor change --- lib/ui/home/memories/full_screen_memory_new.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index ec7747a1e..f67fc8459 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -223,7 +223,7 @@ class _FullScreenMemoryNewState extends State { GestureDetector( onTapDown: (TapDownDetails details) { final screenWidth = MediaQuery.of(context).size.width; - final edgeWidth = screenWidth * 0.33; + final edgeWidth = screenWidth * 0.20; if (details.localPosition.dx < edgeWidth) { if (index > 0) { _pageController!.previousPage( From ae015c7693f135463e367daab130fa7c3fa838b2 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 15:36:49 +0530 Subject: [PATCH 16/20] add hero animation + mark memory as seen when swiping through memories --- lib/ui/home/memories/full_screen_memory_new.dart | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index f67fc8459..af1842b77 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -1,3 +1,4 @@ +import "dart:async"; import "dart:io"; import "package:flutter/cupertino.dart"; @@ -266,6 +267,10 @@ class _FullScreenMemoryNewState extends State { ); }, onPageChanged: (index) { + unawaited( + MemoriesService.instance + .markMemoryAsSeen(inheritedData.memories[index]), + ); inheritedData.indexNotifier.value = index; }, itemCount: inheritedData.memories.length, @@ -281,9 +286,12 @@ class _FullScreenMemoryNewState extends State { switchInCurve: Curves.easeIn, switchOutCurve: Curves.easeOut, child: value - ? Text( - widget.title, - style: darkTextTheme.h2, + ? Hero( + tag: widget.title, + child: Text( + widget.title, + style: darkTextTheme.h2, + ), ) : showStepProgressIndicator ? const SizedBox.shrink() From 6917d5075ce7c102c28e457f62c8a2fbce44d151 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 15:47:00 +0530 Subject: [PATCH 17/20] use BouncingScrollPhysics for list of memories in home tab --- lib/ui/home/memories/memories_widget.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index d5115673f..72f6add5b 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -64,6 +64,7 @@ class _MemoriesWidgetState extends State { } return SingleChildScrollView( scrollDirection: Axis.horizontal, + physics: const BouncingScrollPhysics(), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: memoryWidgets, From 37de9d97eb619228c094ca8bd6dbb56ca1240a1a Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 16:00:44 +0530 Subject: [PATCH 18/20] remove widgets added for debugging + minor change to animation --- .../home/memories/full_screen_memory_new.dart | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index af1842b77..970703e85 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -253,16 +253,6 @@ class _FullScreenMemoryNewState extends State { ), const BottomGradient(), BottomIcons(index), - Padding( - padding: const EdgeInsets.only(bottom: 120, right: 240), - child: Container( - color: Colors.black, - child: Text( - inheritedData.memories[index].file.generatedID - .toString(), - ), - ), - ), ], ); }, @@ -283,8 +273,8 @@ class _FullScreenMemoryNewState extends State { builder: (context, value, _) { return AnimatedSwitcher( duration: const Duration(milliseconds: 250), - switchInCurve: Curves.easeIn, - switchOutCurve: Curves.easeOut, + switchInCurve: Curves.easeOut, + switchOutCurve: Curves.easeIn, child: value ? Hero( tag: widget.title, @@ -328,12 +318,7 @@ class BottomIcons extends StatelessWidget { }, ), ]; - rowChildren.add( - Padding( - padding: const EdgeInsets.all(8), - child: Text(currentFile.generatedID.toString()), - ), - ); + if (currentFile.ownerID == null || (Configuration.instance.getUserID() ?? 0) == currentFile.ownerID) { rowChildren.addAll([ From d896160a5da9b8b17bacb1acd6eeffac9ffeac03 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 16:05:09 +0530 Subject: [PATCH 19/20] delete the old FullScreenMemory widget and file and rename new FullScreenMemoryNew widget and file to 'FullScreenMemory' --- lib/ui/home/memories/full_screen_memory.dart | 513 ++++++++++-------- .../home/memories/full_screen_memory_new.dart | 417 -------------- lib/ui/home/memories/memory_cover_widget.dart | 4 +- 3 files changed, 289 insertions(+), 645 deletions(-) delete mode 100644 lib/ui/home/memories/full_screen_memory_new.dart diff --git a/lib/ui/home/memories/full_screen_memory.dart b/lib/ui/home/memories/full_screen_memory.dart index 7fb2a330d..60f00a56b 100644 --- a/lib/ui/home/memories/full_screen_memory.dart +++ b/lib/ui/home/memories/full_screen_memory.dart @@ -5,7 +5,6 @@ import "package:flutter/cupertino.dart"; import "package:flutter/material.dart"; import "package:intl/intl.dart"; import "package:photos/core/configuration.dart"; -import 'package:photos/models/file/file.dart'; import "package:photos/models/memory.dart"; import "package:photos/services/memories_service.dart"; import "package:photos/theme/text_style.dart"; @@ -16,98 +15,178 @@ import "package:photos/utils/file_util.dart"; import "package:photos/utils/share_util.dart"; import "package:step_progress_indicator/step_progress_indicator.dart"; -class FullScreenMemory extends StatefulWidget { - final String title; +class FullScreenMemoryDataUpdater extends StatefulWidget { final List memories; - final int index; + final int initialIndex; + final Widget child; + const FullScreenMemoryDataUpdater({ + required this.memories, + required this.initialIndex, + required this.child, + super.key, + }); + + @override + State createState() => + _FullScreenMemoryDataUpdaterState(); +} + +class _FullScreenMemoryDataUpdaterState + extends State { + late ValueNotifier indexNotifier; + + @override + void initState() { + super.initState(); + indexNotifier = ValueNotifier(widget.initialIndex); + MemoriesService.instance + .markMemoryAsSeen(widget.memories[widget.initialIndex]); + } + + @override + void dispose() { + indexNotifier.dispose(); + super.dispose(); + } + + void removeCurrentMemory() { + setState(() { + widget.memories.removeAt(indexNotifier.value); + }); + } + + @override + Widget build(BuildContext context) { + return FullScreenMemoryData( + memories: widget.memories, + indexNotifier: indexNotifier, + removeCurrentMemory: removeCurrentMemory, + child: widget.child, + ); + } +} + +class FullScreenMemoryData extends InheritedWidget { + final List memories; + final ValueNotifier indexNotifier; + final VoidCallback removeCurrentMemory; + + const FullScreenMemoryData({ + required this.memories, + required this.indexNotifier, + required this.removeCurrentMemory, + required Widget child, + Key? key, + }) : super(child: child, key: key); + + static FullScreenMemoryData? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } + + @override + bool updateShouldNotify(FullScreenMemoryData oldWidget) { + // Checking oldWidget.memories.length != memories.length here doesn't work + //because the old widget and new widget reference the same memories list. + return true; + } +} - const FullScreenMemory(this.title, this.memories, this.index, {Key? key}) - : super(key: key); +class FullScreenMemory extends StatefulWidget { + final String title; + final int initialIndex; + const FullScreenMemory( + this.title, + this.initialIndex, { + super.key, + }); @override State createState() => _FullScreenMemoryState(); } class _FullScreenMemoryState extends State { - int _index = 0; - double _opacity = 1; - // shows memory counter as index+1/totalFiles for large number of memories - // when the top step indicator isn't visible. - bool _showCounter = false; - bool _showStepIndicator = true; PageController? _pageController; - final bool _shouldDisableScroll = false; - late int currentUserID; - final GlobalKey shareButtonKey = GlobalKey(); + final _showTitle = ValueNotifier(true); @override void initState() { super.initState(); - _index = widget.index; - currentUserID = Configuration.instance.getUserID() ?? 0; - _showStepIndicator = widget.memories.length <= 60; Future.delayed(const Duration(seconds: 3), () { if (mounted) { setState(() { - _opacity = 0; - _showCounter = !_showStepIndicator; + _showTitle.value = false; }); } }); - MemoriesService.instance.markMemoryAsSeen(widget.memories[_index]); + } + + @override + void dispose() { + _pageController?.dispose(); + _showTitle.dispose(); + super.dispose(); } @override Widget build(BuildContext context) { - final file = widget.memories[_index].file; + final inheritedData = FullScreenMemoryData.of(context)!; + final showStepProgressIndicator = inheritedData.memories.length < 60; return Scaffold( + extendBodyBehindAppBar: true, appBar: AppBar( toolbarHeight: 84, automaticallyImplyLeading: false, - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _showStepIndicator - ? StepProgressIndicator( - totalSteps: widget.memories.length, - currentStep: _index + 1, - size: 2, - selectedColor: Colors.white, //same for both themes - unselectedColor: Colors.white.withOpacity(0.4), - ) - : const SizedBox.shrink(), - const SizedBox( - height: 18, + title: ValueListenableBuilder( + valueListenable: inheritedData.indexNotifier, + child: Padding( + padding: const EdgeInsets.only(right: 16), + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: const Icon( + Icons.close, + color: Colors.white, //same for both themes + ), ), - Row( + ), + builder: (context, value, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.only(right: 16), - child: InkWell( - onTap: () { - Navigator.pop(context); - }, - child: const Icon( - Icons.close, - color: Colors.white, //same for both themes - ), - ), + showStepProgressIndicator + ? StepProgressIndicator( + totalSteps: inheritedData.memories.length, + currentStep: value + 1, + size: 2, + selectedColor: Colors.white, //same for both themes + unselectedColor: Colors.white.withOpacity(0.4), + ) + : const SizedBox.shrink(), + const SizedBox( + height: 18, ), - Text( - DateFormat.yMMMd(Localizations.localeOf(context).languageCode) - .format( - DateTime.fromMicrosecondsSinceEpoch( - file.creationTime!, + Row( + children: [ + child!, + Text( + DateFormat.yMMMd( + Localizations.localeOf(context).languageCode, + ).format( + DateTime.fromMicrosecondsSinceEpoch( + inheritedData.memories[value].file.creationTime!, + ), + ), + style: Theme.of(context).textTheme.titleMedium!.copyWith( + fontSize: 14, + color: Colors.white, + ), //same for both themes ), - ), - style: Theme.of(context).textTheme.titleMedium!.copyWith( - fontSize: 14, - color: Colors.white, - ), //same for both themes + ], ), ], - ), - ], + ); + }, ), flexibleSpace: Container( decoration: BoxDecoration( @@ -126,86 +205,108 @@ class _FullScreenMemoryState extends State { backgroundColor: const Color(0x00000000), elevation: 0, ), - extendBodyBehindAppBar: true, - body: Container( - key: ValueKey(widget.memories.length), - color: Colors.black, - child: Stack( - alignment: Alignment.bottomCenter, - children: [ - _buildSwiper(), - bottomGradient(), - _buildInfoText(), - _buildBottomIcons(), - ], - ), + body: Stack( + alignment: Alignment.bottomCenter, + children: [ + PageView.builder( + controller: _pageController ??= PageController( + initialPage: widget.initialIndex, + ), + itemBuilder: (context, index) { + if (index < inheritedData.memories.length - 1) { + final nextFile = inheritedData.memories[index + 1].file; + preloadThumbnail(nextFile); + preloadFile(nextFile); + } + return Stack( + alignment: Alignment.bottomCenter, + children: [ + GestureDetector( + onTapDown: (TapDownDetails 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, + ); + } + } else if (details.localPosition.dx > + screenWidth - edgeWidth) { + if (index < (inheritedData.memories.length - 1)) { + _pageController!.nextPage( + duration: const Duration(milliseconds: 250), + curve: Curves.ease, + ); + } + } + }, + child: FileWidget( + inheritedData.memories[index].file, + autoPlay: false, + tagPrefix: "memories", + backgroundDecoration: const BoxDecoration( + color: Colors.transparent, + ), + ), + ), + const BottomGradient(), + BottomIcons(index), + ], + ); + }, + onPageChanged: (index) { + unawaited( + MemoriesService.instance + .markMemoryAsSeen(inheritedData.memories[index]), + ); + inheritedData.indexNotifier.value = index; + }, + itemCount: inheritedData.memories.length, + ), + SafeArea( + top: false, + child: Padding( + padding: const EdgeInsets.only(bottom: 72), + child: ValueListenableBuilder( + builder: (context, value, _) { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 250), + switchInCurve: Curves.easeOut, + switchOutCurve: Curves.easeIn, + child: value + ? Hero( + tag: widget.title, + child: Text( + widget.title, + style: darkTextTheme.h2, + ), + ) + : showStepProgressIndicator + ? const SizedBox.shrink() + : const MemoryCounter(), + ); + }, + valueListenable: _showTitle, + ), + ), + ), + ], ), ); } +} - @override - void dispose() { - debugPrint("FullScreenMemoryDisposed"); - // _pageController?.dispose(); - _pageController = null; - super.dispose(); - } +class BottomIcons extends StatelessWidget { + final int pageViewIndex; + const BottomIcons(this.pageViewIndex, {super.key}); - Future onFileDeleted(Memory removedMemory) async { - if (!mounted) { - return; - } - final totalFiles = widget.memories.length; - if (totalFiles == 1) { - // Deleted the only file - Navigator.of(context).pop(); // Close pageview - return; - } - if (_index == totalFiles - 1) { - // Deleted the last file - widget.memories.remove(removedMemory); - await _pageController!.previousPage( - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOut, - ); - setState(() {}); - } else { - widget.memories.remove(removedMemory); - await _pageController!.nextPage( - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOut, - ); - setState(() {}); - } - } - - Hero _buildInfoText() { - return Hero( - tag: widget.title, - child: SafeArea( - child: Container( - alignment: Alignment.bottomCenter, - padding: const EdgeInsets.fromLTRB(0, 0, 0, 72), - child: _showCounter - ? Text( - '${_index + 1}/${widget.memories.length}', - style: darkTextTheme.bodyMuted, - ) - : AnimatedOpacity( - opacity: _opacity, - duration: const Duration(milliseconds: 500), - child: Text( - widget.title, - style: darkTextTheme.h2, - ), - ), - ), - ), - ); - } + @override + Widget build(BuildContext context) { + final inheritedData = FullScreenMemoryData.of(context)!; + final currentFile = inheritedData.memories[pageViewIndex].file; - Widget _buildBottomIcons() { - final EnteFile currentFile = widget.memories[_index].file; final List rowChildren = [ IconButton( icon: Icon( @@ -217,31 +318,35 @@ class _FullScreenMemoryState extends State { }, ), ]; - if (currentFile.ownerID == null || currentUserID == currentFile.ownerID) { - rowChildren.addAll( - [ - IconButton( - icon: Icon( - Platform.isAndroid ? Icons.delete_outline : CupertinoIcons.delete, - color: Colors.white, //same for both themes - ), - onPressed: () async { - await showSingleFileDeleteSheet( - context, - currentFile, - onFileRemoved: (file) => - {onFileDeleted(widget.memories[_index])}, - ); - }, - ), - SizedBox( - height: 32, - child: FavoriteWidget(currentFile), + + if (currentFile.ownerID == null || + (Configuration.instance.getUserID() ?? 0) == currentFile.ownerID) { + rowChildren.addAll([ + IconButton( + icon: Icon( + Platform.isAndroid ? Icons.delete_outline : CupertinoIcons.delete, + color: Colors.white, //same for both themes ), - ], - ); + onPressed: () async { + await showSingleFileDeleteSheet( + context, + inheritedData.memories[inheritedData.indexNotifier.value].file, + onFileRemoved: (file) => { + inheritedData.removeCurrentMemory.call(), + if (inheritedData.memories.isEmpty) + { + Navigator.of(context).pop(), + }, + }, + ); + }, + ), + SizedBox( + height: 32, + child: FavoriteWidget(currentFile), + ), + ]); } - rowChildren.add( IconButton( icon: Icon( @@ -255,6 +360,7 @@ class _FullScreenMemoryState extends State { ); return SafeArea( + top: false, child: Container( alignment: Alignment.bottomCenter, padding: const EdgeInsets.fromLTRB(12, 0, 12, 20), @@ -265,8 +371,31 @@ class _FullScreenMemoryState extends State { ), ); } +} + +class MemoryCounter extends StatelessWidget { + const MemoryCounter({super.key}); + + @override + Widget build(BuildContext context) { + final inheritedData = FullScreenMemoryData.of(context)!; + return ValueListenableBuilder( + valueListenable: inheritedData.indexNotifier, + builder: (context, value, _) { + return Text( + "${value + 1}/${inheritedData.memories.length}", + style: darkTextTheme.bodyMuted, + ); + }, + ); + } +} + +class BottomGradient extends StatelessWidget { + const BottomGradient({super.key}); - Widget bottomGradient() { + @override + Widget build(BuildContext context) { return IgnorePointer( child: Container( height: 124, @@ -285,72 +414,4 @@ class _FullScreenMemoryState extends State { ), ); } - - Widget _buildSwiper() { - debugPrint( - "FullScreenbuildSwiper: $_index and total ${widget.memories.length}", - ); - _pageController ??= PageController(initialPage: _index); - return GestureDetector( - behavior: HitTestBehavior.translucent, - onTapDown: (TapDownDetails details) { - if (_shouldDisableScroll) { - return; - } - final screenWidth = MediaQuery.of(context).size.width; - final edgeWidth = screenWidth * 0.20; // 20% of screen width - if (details.localPosition.dx < edgeWidth) { - if (_index > 0) { - _pageController!.previousPage( - duration: const Duration(milliseconds: 250), - curve: Curves.ease, - ); - } - } else if (details.localPosition.dx > screenWidth - edgeWidth) { - if (_index < (widget.memories.length - 1)) { - _pageController!.nextPage( - duration: const Duration(milliseconds: 250), - curve: Curves.ease, - ); - } - } - }, - child: PageView.builder( - itemBuilder: (BuildContext context, int index) { - if (index < widget.memories.length - 1) { - final nextFile = widget.memories[index + 1].file; - preloadThumbnail(nextFile); - preloadFile(nextFile); - } - final file = widget.memories[index].file; - return FileWidget( - file, - autoPlay: false, - tagPrefix: "memories", - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, - ), - ); - }, - itemCount: widget.memories.length, - controller: _pageController!, - onPageChanged: (index) async { - unawaited( - MemoriesService.instance.markMemoryAsSeen(widget.memories[index]), - ); - if (mounted) { - debugPrint( - "FullScreenonPageChanged: $index and total ${widget.memories.length}", - ); - setState(() { - _index = index; - }); - } - }, - physics: _shouldDisableScroll - ? const NeverScrollableScrollPhysics() - : const PageScrollPhysics(), - ), - ); - } } diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart deleted file mode 100644 index 970703e85..000000000 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ /dev/null @@ -1,417 +0,0 @@ -import "dart:async"; -import "dart:io"; - -import "package:flutter/cupertino.dart"; -import "package:flutter/material.dart"; -import "package:intl/intl.dart"; -import "package:photos/core/configuration.dart"; -import "package:photos/models/memory.dart"; -import "package:photos/services/memories_service.dart"; -import "package:photos/theme/text_style.dart"; -import "package:photos/ui/actions/file/file_actions.dart"; -import "package:photos/ui/viewer/file/file_widget.dart"; -import "package:photos/ui/viewer/file_details/favorite_widget.dart"; -import "package:photos/utils/file_util.dart"; -import "package:photos/utils/share_util.dart"; -import "package:step_progress_indicator/step_progress_indicator.dart"; - -class FullScreenMemoryDataUpdater extends StatefulWidget { - final List memories; - final int initialIndex; - final Widget child; - const FullScreenMemoryDataUpdater({ - required this.memories, - required this.initialIndex, - required this.child, - super.key, - }); - - @override - State createState() => - _FullScreenMemoryDataUpdaterState(); -} - -class _FullScreenMemoryDataUpdaterState - extends State { - late ValueNotifier indexNotifier; - - @override - void initState() { - super.initState(); - indexNotifier = ValueNotifier(widget.initialIndex); - MemoriesService.instance - .markMemoryAsSeen(widget.memories[widget.initialIndex]); - } - - @override - void dispose() { - indexNotifier.dispose(); - super.dispose(); - } - - void removeCurrentMemory() { - setState(() { - widget.memories.removeAt(indexNotifier.value); - }); - } - - @override - Widget build(BuildContext context) { - return FullScreenMemoryData( - memories: widget.memories, - indexNotifier: indexNotifier, - removeCurrentMemory: removeCurrentMemory, - child: widget.child, - ); - } -} - -class FullScreenMemoryData extends InheritedWidget { - final List memories; - final ValueNotifier indexNotifier; - final VoidCallback removeCurrentMemory; - - const FullScreenMemoryData({ - required this.memories, - required this.indexNotifier, - required this.removeCurrentMemory, - required Widget child, - Key? key, - }) : super(child: child, key: key); - - static FullScreenMemoryData? of(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType(); - } - - @override - bool updateShouldNotify(FullScreenMemoryData oldWidget) { - // Checking oldWidget.memories.length != memories.length here doesn't work - //because the old widget and new widget reference the same memories list. - return true; - } -} - -class FullScreenMemoryNew extends StatefulWidget { - final String title; - final int initialIndex; - const FullScreenMemoryNew( - this.title, - this.initialIndex, { - super.key, - }); - - @override - State createState() => _FullScreenMemoryNewState(); -} - -class _FullScreenMemoryNewState extends State { - PageController? _pageController; - final _showTitle = ValueNotifier(true); - - @override - void initState() { - super.initState(); - Future.delayed(const Duration(seconds: 3), () { - if (mounted) { - setState(() { - _showTitle.value = false; - }); - } - }); - } - - @override - void dispose() { - _pageController?.dispose(); - _showTitle.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final inheritedData = FullScreenMemoryData.of(context)!; - final showStepProgressIndicator = inheritedData.memories.length < 60; - return Scaffold( - extendBodyBehindAppBar: true, - appBar: AppBar( - toolbarHeight: 84, - automaticallyImplyLeading: false, - title: ValueListenableBuilder( - valueListenable: inheritedData.indexNotifier, - child: Padding( - padding: const EdgeInsets.only(right: 16), - child: InkWell( - onTap: () { - Navigator.pop(context); - }, - child: const Icon( - Icons.close, - color: Colors.white, //same for both themes - ), - ), - ), - builder: (context, value, child) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - showStepProgressIndicator - ? StepProgressIndicator( - totalSteps: inheritedData.memories.length, - currentStep: value + 1, - size: 2, - selectedColor: Colors.white, //same for both themes - unselectedColor: Colors.white.withOpacity(0.4), - ) - : const SizedBox.shrink(), - const SizedBox( - height: 18, - ), - Row( - children: [ - child!, - Text( - DateFormat.yMMMd( - Localizations.localeOf(context).languageCode, - ).format( - DateTime.fromMicrosecondsSinceEpoch( - inheritedData.memories[value].file.creationTime!, - ), - ), - style: Theme.of(context).textTheme.titleMedium!.copyWith( - fontSize: 14, - color: Colors.white, - ), //same for both themes - ), - ], - ), - ], - ); - }, - ), - flexibleSpace: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.black.withOpacity(0.6), - Colors.black.withOpacity(0.5), - Colors.transparent, - ], - stops: const [0, 0.6, 1], - ), - ), - ), - backgroundColor: const Color(0x00000000), - elevation: 0, - ), - body: Stack( - alignment: Alignment.bottomCenter, - children: [ - PageView.builder( - controller: _pageController ??= PageController( - initialPage: widget.initialIndex, - ), - itemBuilder: (context, index) { - if (index < inheritedData.memories.length - 1) { - final nextFile = inheritedData.memories[index + 1].file; - preloadThumbnail(nextFile); - preloadFile(nextFile); - } - return Stack( - alignment: Alignment.bottomCenter, - children: [ - GestureDetector( - onTapDown: (TapDownDetails 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, - ); - } - } else if (details.localPosition.dx > - screenWidth - edgeWidth) { - if (index < (inheritedData.memories.length - 1)) { - _pageController!.nextPage( - duration: const Duration(milliseconds: 250), - curve: Curves.ease, - ); - } - } - }, - child: FileWidget( - inheritedData.memories[index].file, - autoPlay: false, - tagPrefix: "memories", - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, - ), - ), - ), - const BottomGradient(), - BottomIcons(index), - ], - ); - }, - onPageChanged: (index) { - unawaited( - MemoriesService.instance - .markMemoryAsSeen(inheritedData.memories[index]), - ); - inheritedData.indexNotifier.value = index; - }, - itemCount: inheritedData.memories.length, - ), - SafeArea( - top: false, - child: Padding( - padding: const EdgeInsets.only(bottom: 72), - child: ValueListenableBuilder( - builder: (context, value, _) { - return AnimatedSwitcher( - duration: const Duration(milliseconds: 250), - switchInCurve: Curves.easeOut, - switchOutCurve: Curves.easeIn, - child: value - ? Hero( - tag: widget.title, - child: Text( - widget.title, - style: darkTextTheme.h2, - ), - ) - : showStepProgressIndicator - ? const SizedBox.shrink() - : const MemoryCounter(), - ); - }, - valueListenable: _showTitle, - ), - ), - ), - ], - ), - ); - } -} - -class BottomIcons extends StatelessWidget { - final int pageViewIndex; - const BottomIcons(this.pageViewIndex, {super.key}); - - @override - Widget build(BuildContext context) { - final inheritedData = FullScreenMemoryData.of(context)!; - final currentFile = inheritedData.memories[pageViewIndex].file; - - final List rowChildren = [ - IconButton( - icon: Icon( - Platform.isAndroid ? Icons.info_outline : CupertinoIcons.info, - color: Colors.white, //same for both themes - ), - onPressed: () { - showDetailsSheet(context, currentFile); - }, - ), - ]; - - if (currentFile.ownerID == null || - (Configuration.instance.getUserID() ?? 0) == currentFile.ownerID) { - rowChildren.addAll([ - IconButton( - icon: Icon( - Platform.isAndroid ? Icons.delete_outline : CupertinoIcons.delete, - color: Colors.white, //same for both themes - ), - onPressed: () async { - await showSingleFileDeleteSheet( - context, - inheritedData.memories[inheritedData.indexNotifier.value].file, - onFileRemoved: (file) => { - inheritedData.removeCurrentMemory.call(), - if (inheritedData.memories.isEmpty) - { - Navigator.of(context).pop(), - }, - }, - ); - }, - ), - SizedBox( - height: 32, - child: FavoriteWidget(currentFile), - ), - ]); - } - rowChildren.add( - IconButton( - icon: Icon( - Icons.adaptive.share, - color: Colors.white, //same for both themes - ), - onPressed: () { - share(context, [currentFile]); - }, - ), - ); - - return SafeArea( - top: false, - child: Container( - alignment: Alignment.bottomCenter, - padding: const EdgeInsets.fromLTRB(12, 0, 12, 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: rowChildren, - ), - ), - ); - } -} - -class MemoryCounter extends StatelessWidget { - const MemoryCounter({super.key}); - - @override - Widget build(BuildContext context) { - final inheritedData = FullScreenMemoryData.of(context)!; - return ValueListenableBuilder( - valueListenable: inheritedData.indexNotifier, - builder: (context, value, _) { - return Text( - "${value + 1}/${inheritedData.memories.length}", - style: darkTextTheme.bodyMuted, - ); - }, - ); - } -} - -class BottomGradient extends StatelessWidget { - const BottomGradient({super.key}); - - @override - Widget build(BuildContext context) { - return IgnorePointer( - child: Container( - height: 124, - width: double.infinity, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - colors: [ - Colors.black.withOpacity(0.5), //same for both themes - Colors.transparent, - ], - stops: const [0, 0.8], - ), - ), - ), - ); - } -} diff --git a/lib/ui/home/memories/memory_cover_widget.dart b/lib/ui/home/memories/memory_cover_widget.dart index df1b6d5b3..5218ba5ff 100644 --- a/lib/ui/home/memories/memory_cover_widget.dart +++ b/lib/ui/home/memories/memory_cover_widget.dart @@ -2,7 +2,7 @@ import "package:flutter/material.dart"; import "package:photos/generated/l10n.dart"; import "package:photos/models/memory.dart"; import "package:photos/theme/ente_theme.dart"; -import "package:photos/ui/home/memories/full_screen_memory_new.dart"; +import 'package:photos/ui/home/memories/full_screen_memory.dart'; import "package:photos/ui/viewer/file/thumbnail_widget.dart"; import "package:photos/utils/navigation_util.dart"; @@ -35,7 +35,7 @@ class _MemoryCovertWidgetState extends State { FullScreenMemoryDataUpdater( initialIndex: index, memories: widget.memories, - child: FullScreenMemoryNew(title, index), + child: FullScreenMemory(title, index), ), forceCustomPageRoute: true, ); From 7643a29152e6040ce8d30e06084c67b64fb0368f Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 16:26:38 +0530 Subject: [PATCH 20/20] added documentation --- lib/ui/home/memories/full_screen_memory.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/ui/home/memories/full_screen_memory.dart b/lib/ui/home/memories/full_screen_memory.dart index 60f00a56b..bacc6adda 100644 --- a/lib/ui/home/memories/full_screen_memory.dart +++ b/lib/ui/home/memories/full_screen_memory.dart @@ -15,6 +15,22 @@ import "package:photos/utils/file_util.dart"; import "package:photos/utils/share_util.dart"; import "package:step_progress_indicator/step_progress_indicator.dart"; +//There are two states of variables that FullScreenMemory depends on: +//1. The list of memories +//2. The current index of the page view + +//1 +//Only when items are deleted will list of memories change and this requires the +//whole screen to be rebuild. So the InheritedWidget is updated using the Updater +//widget which will then lead to a rebuild of all widgets that call +//InheritedWidget.of(context). + +//2 +//There are widgets that doesn't come inside the PageView that needs to rebuild +//with new state when page index is changed. So the index is stored in a +//ValueNotifier inside the InheritedWidget and the widgets that need to change +//are wrapped in a ValueListenableBuilder. + class FullScreenMemoryDataUpdater extends StatefulWidget { final List memories; final int initialIndex;