浏览代码

Memories redesign (#1649)

Vishnu Mohandas 1 年之前
父节点
当前提交
a460b6ced2

+ 10 - 0
lib/generated/l10n.dart

@@ -8307,6 +8307,16 @@ class S {
       args: [],
     );
   }
+
+  /// `Memories`
+  String get memories {
+    return Intl.message(
+      'Memories',
+      name: 'memories',
+      desc: '',
+      args: [],
+    );
+  }
 }
 
 class AppLocalizationDelegate extends LocalizationsDelegate<S> {

+ 2 - 1
lib/l10n/intl_cs.arb

@@ -10,5 +10,6 @@
   "selectALocation": "Select a location",
   "selectALocationFirst": "Select a location first",
   "changeLocationOfSelectedItems": "Change location of selected items?",
-  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente"
+  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente",
+  "memories": "Memories"
 }

+ 2 - 1
lib/l10n/intl_de.arb

@@ -1178,5 +1178,6 @@
   "selectALocationFirst": "Wähle zuerst einen Standort",
   "changeLocationOfSelectedItems": "Standort der gewählten Elemente ändern?",
   "editsToLocationWillOnlyBeSeenWithinEnte": "Änderungen des Standorts werden nur in ente sichtbar sein",
-  "cleanUncategorized": "Unkategorisiert leeren"
+  "cleanUncategorized": "Unkategorisiert leeren",
+  "memories": "Memories"
 }

+ 3 - 2
lib/l10n/intl_en.arb

@@ -1187,5 +1187,6 @@
   "selectALocationFirst": "Select a location first",
   "changeLocationOfSelectedItems": "Change location of selected items?",
   "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente",
-  "cleanUncategorized": "Clean Uncategorized"
-}
+  "cleanUncategorized": "Clean Uncategorized",
+  "memories": "Memories"
+}

+ 2 - 1
lib/l10n/intl_es.arb

@@ -973,5 +973,6 @@
   "selectALocation": "Select a location",
   "selectALocationFirst": "Select a location first",
   "changeLocationOfSelectedItems": "Change location of selected items?",
-  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente"
+  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente",
+  "memories": "Memories"
 }

+ 2 - 1
lib/l10n/intl_fr.arb

@@ -1154,5 +1154,6 @@
   "selectALocation": "Select a location",
   "selectALocationFirst": "Select a location first",
   "changeLocationOfSelectedItems": "Change location of selected items?",
-  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente"
+  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente",
+  "memories": "Memories"
 }

+ 2 - 1
lib/l10n/intl_it.arb

@@ -1116,5 +1116,6 @@
   "selectALocation": "Select a location",
   "selectALocationFirst": "Select a location first",
   "changeLocationOfSelectedItems": "Change location of selected items?",
-  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente"
+  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente",
+  "memories": "Memories"
 }

+ 2 - 1
lib/l10n/intl_ko.arb

@@ -10,5 +10,6 @@
   "selectALocation": "Select a location",
   "selectALocationFirst": "Select a location first",
   "changeLocationOfSelectedItems": "Change location of selected items?",
-  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente"
+  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente",
+  "memories": "Memories"
 }

+ 2 - 1
lib/l10n/intl_nl.arb

@@ -1163,5 +1163,6 @@
   "selectALocation": "Select a location",
   "selectALocationFirst": "Select a location first",
   "changeLocationOfSelectedItems": "Change location of selected items?",
-  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente"
+  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente",
+  "memories": "Memories"
 }

+ 2 - 1
lib/l10n/intl_no.arb

@@ -24,5 +24,6 @@
   "selectALocation": "Select a location",
   "selectALocationFirst": "Select a location first",
   "changeLocationOfSelectedItems": "Change location of selected items?",
-  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente"
+  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente",
+  "memories": "Memories"
 }

+ 2 - 1
lib/l10n/intl_pl.arb

@@ -111,5 +111,6 @@
   "selectALocation": "Select a location",
   "selectALocationFirst": "Select a location first",
   "changeLocationOfSelectedItems": "Change location of selected items?",
-  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente"
+  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente",
+  "memories": "Memories"
 }

+ 2 - 1
lib/l10n/intl_pt.arb

@@ -277,5 +277,6 @@
   "selectALocation": "Select a location",
   "selectALocationFirst": "Select a location first",
   "changeLocationOfSelectedItems": "Change location of selected items?",
-  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente"
+  "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente",
+  "memories": "Memories"
 }

+ 2 - 1
lib/l10n/intl_zh.arb

@@ -1186,5 +1186,6 @@
   "selectALocationFirst": "首先选择一个位置",
   "changeLocationOfSelectedItems": "确定要更改所选项目的位置吗?",
   "editsToLocationWillOnlyBeSeenWithinEnte": "对位置的编辑只能在 Ente 内看到",
-  "cleanUncategorized": "清除未分类的"
+  "cleanUncategorized": "清除未分类的",
+  "memories": "Memories"
 }

+ 51 - 12
lib/ui/home/memories/memories_widget.dart

@@ -3,9 +3,11 @@ import "dart:async";
 import 'package:flutter/material.dart';
 import "package:photos/core/event_bus.dart";
 import "package:photos/events/memories_setting_changed.dart";
+import "package:photos/generated/l10n.dart";
 import 'package:photos/models/memory.dart';
 import 'package:photos/services/memories_service.dart';
-import "package:photos/ui/home/memories/memory_cover_widget.dart";
+import "package:photos/theme/ente_theme.dart";
+import 'package:photos/ui/home/memories/memory_cover_widget.dart';
 
 class MemoriesWidget extends StatefulWidget {
   const MemoriesWidget({Key? key}) : super(key: key);
@@ -15,6 +17,8 @@ class MemoriesWidget extends StatefulWidget {
 }
 
 class _MemoriesWidgetState extends State<MemoriesWidget> {
+  final double _widthOfItem = 85;
+  late ScrollController _controller;
   late StreamSubscription<MemoriesSettingChanged> _subscription;
 
   @override
@@ -25,11 +29,13 @@ class _MemoriesWidgetState extends State<MemoriesWidget> {
         setState(() {});
       }
     });
+    _controller = ScrollController();
   }
 
   @override
   void dispose() {
     _subscription.cancel();
+    _controller.dispose();
     super.dispose();
   }
 
@@ -47,8 +53,34 @@ class _MemoriesWidgetState extends State<MemoriesWidget> {
           return Column(
             crossAxisAlignment: CrossAxisAlignment.start,
             children: [
+              Padding(
+                padding: const EdgeInsets.symmetric(horizontal: 12.0),
+                child: Stack(
+                  alignment: Alignment.centerRight,
+                  children: [
+                    const RotationTransition(
+                      turns: AlwaysStoppedAnimation(20 / 360),
+                      child: Icon(
+                        Icons.favorite_rounded,
+                        color: Color.fromRGBO(0, 179, 60, 0.3),
+                        size: 32,
+                      ),
+                    ),
+                    Padding(
+                      padding: const EdgeInsets.only(right: 16),
+                      child: Text(
+                        S.of(context).memories,
+                        style: getEnteTextTheme(context).body,
+                      ),
+                    ),
+                  ],
+                ),
+              ),
+              const SizedBox(
+                height: 12,
+              ),
               _buildMemories(snapshot.data!),
-              const Divider(),
+              const SizedBox(height: 10),
             ],
           );
         }
@@ -58,16 +90,22 @@ class _MemoriesWidgetState extends State<MemoriesWidget> {
 
   Widget _buildMemories(List<Memory> memories) {
     final collatedMemories = _collateMemories(memories);
-    final List<Widget> memoryWidgets = [];
-    for (final memories in collatedMemories) {
-      memoryWidgets.add(MemoryCovertWidget(memories: memories));
-    }
-    return SingleChildScrollView(
-      scrollDirection: Axis.horizontal,
-      physics: const BouncingScrollPhysics(),
-      child: Row(
-        crossAxisAlignment: CrossAxisAlignment.start,
-        children: memoryWidgets,
+
+    return SizedBox(
+      height: MemoryCoverWidget.height,
+      child: ListView.builder(
+        physics: const BouncingScrollPhysics(),
+        scrollDirection: Axis.horizontal,
+        controller: _controller,
+        itemCount: collatedMemories.length,
+        itemBuilder: (context, itemIndex) {
+          final offsetOfItem = _widthOfItem * itemIndex;
+          return MemoryCoverWidget(
+            memories: collatedMemories[itemIndex],
+            controller: _controller,
+            offsetOfItem: offsetOfItem,
+          );
+        },
       ),
     );
   }
@@ -81,6 +119,7 @@ class _MemoriesWidgetState extends State<MemoriesWidget> {
         final List<Memory> collatedYearlyMemories = [];
         collatedYearlyMemories.addAll(yearlyMemories);
         collatedMemories.add(collatedYearlyMemories);
+
         yearlyMemories.clear();
       }
       yearlyMemories.add(memories[index]);

+ 186 - 68
lib/ui/home/memories/memory_cover_widget.dart

@@ -2,23 +2,30 @@ 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.dart";
 import "package:photos/ui/viewer/file/thumbnail_widget.dart";
 import "package:photos/utils/navigation_util.dart";
 
-class MemoryCovertWidget extends StatefulWidget {
-  const MemoryCovertWidget({
-    Key? key,
-    required this.memories,
-  }) : super(key: key);
-
+class MemoryCoverWidget extends StatefulWidget {
   final List<Memory> memories;
+  final ScrollController controller;
+  final double offsetOfItem;
+  static const centerStrokeWidth = 1.0;
+  static const width = 85.0;
+  static const height = 125.0;
+
+  const MemoryCoverWidget({
+    required this.memories,
+    required this.controller,
+    required this.offsetOfItem,
+    super.key,
+  });
 
   @override
-  State<MemoryCovertWidget> createState() => _MemoryCovertWidgetState();
+  State<MemoryCoverWidget> createState() => _MemoryCoverWidgetState();
 }
 
-class _MemoryCovertWidgetState extends State<MemoryCovertWidget> {
+class _MemoryCoverWidgetState extends State<MemoryCoverWidget> {
   @override
   Widget build(BuildContext context) {
     //memories will be empty if all memories are deleted and setState is called
@@ -26,75 +33,186 @@ class _MemoryCovertWidgetState extends State<MemoryCovertWidget> {
     if (widget.memories.isEmpty) {
       return const SizedBox.shrink();
     }
+
+    final widthOfScreen = MediaQuery.sizeOf(context).width;
     final index = _getNextMemoryIndex();
     final title = _getTitle(widget.memories[index]);
-    return GestureDetector(
-      onTap: () async {
-        await routeToPage(
-          context,
-          FullScreenMemoryDataUpdater(
-            initialIndex: index,
-            memories: widget.memories,
-            child: FullScreenMemory(title, index),
-          ),
-          forceCustomPageRoute: true,
-        );
-        setState(() {});
-      },
-      child: Row(
-        children: [
-          Padding(
-            padding: const EdgeInsets.all(8.0),
-            child: Column(
+    final memory = widget.memories[index];
+    final isSeen = memory.isSeen();
+    final currentTheme = MediaQuery.platformBrightnessOf(context);
+
+    return AnimatedBuilder(
+      animation: widget.controller,
+      builder: (context, child) {
+        final diff = (widget.controller.offset - widget.offsetOfItem) +
+            widthOfScreen / 7;
+        final scale = 1 - (diff / widthOfScreen).abs() / 3;
+        return Padding(
+          padding: const EdgeInsets.symmetric(horizontal: 2.5),
+          //Adding this row is a workaround for making height of memory cover
+          //render as [MemoryCoverWidgetNew.height] * scale. Without this, height of rendered memory
+          //cover will be [MemoryCoverWidgetNew.height].
+          child: GestureDetector(
+            onTap: () async {
+              await routeToPage(
+                context,
+                FullScreenMemoryDataUpdater(
+                  initialIndex: index,
+                  memories: widget.memories,
+                  child: FullScreenMemory(title, index),
+                ),
+                forceCustomPageRoute: true,
+              );
+              setState(() {});
+            },
+            child: Row(
               children: [
-                _buildMemoryItem(context, index),
-                const Padding(padding: EdgeInsets.all(4)),
-                Hero(
-                  tag: title,
-                  child: Material(
-                    type: MaterialType.transparency,
-                    child: ConstrainedBox(
-                      constraints: const BoxConstraints(maxWidth: 84),
-                      child: Text(
-                        title,
-                        style: getEnteTextTheme(context).mini,
-                        textAlign: TextAlign.center,
+                Container(
+                  height: MemoryCoverWidget.height * scale,
+                  width: MemoryCoverWidget.width * scale,
+                  decoration: BoxDecoration(
+                    boxShadow: [
+                      BoxShadow(
+                        color: isSeen
+                            ? currentTheme == Brightness.dark
+                                ? const Color.fromRGBO(104, 104, 104, 0.32)
+                                : Colors.transparent
+                            : const Color.fromRGBO(1, 222, 77, 0.11),
+                        spreadRadius: MemoryCoverWidget.centerStrokeWidth / 2,
+                        blurRadius: 0,
+                      ),
+                      const BoxShadow(
+                        color: Color.fromRGBO(0, 0, 0, 0.13),
+                        blurRadius: 3,
+                        offset: Offset(1, 1),
                       ),
+                    ],
+                    borderRadius: BorderRadius.circular(5),
+                  ),
+                  child: ClipRRect(
+                    borderRadius: BorderRadius.circular(5),
+                    child: Stack(
+                      fit: StackFit.expand,
+                      alignment: Alignment.bottomCenter,
+                      children: [
+                        child!,
+                        Container(
+                          decoration: BoxDecoration(
+                            border: Border.all(
+                              color: isSeen
+                                  ? currentTheme == Brightness.dark
+                                      ? const Color.fromRGBO(
+                                          104,
+                                          104,
+                                          104,
+                                          0.32,
+                                        )
+                                      : Colors.transparent
+                                  : const Color.fromRGBO(1, 222, 77, 0.11),
+                              width: MemoryCoverWidget.centerStrokeWidth / 2,
+                            ),
+                            borderRadius: BorderRadius.circular(5),
+                          ),
+                        ),
+                        Container(
+                          decoration: BoxDecoration(
+                            gradient: LinearGradient(
+                              colors: [
+                                Colors.black.withOpacity(0.5),
+                                Colors.transparent,
+                              ],
+                              stops: const [0, 0.85],
+                              begin: Alignment.bottomCenter,
+                              end: Alignment.topCenter,
+                            ),
+                          ),
+                        ),
+                        isSeen
+                            ? const SizedBox.shrink()
+                            : Container(
+                                decoration: const BoxDecoration(
+                                  gradient: LinearGradient(
+                                    stops: [0, 0.35, 0.5],
+                                    colors: [
+                                      Color.fromARGB(71, 1, 222, 78),
+                                      Color(0x1901DE4D),
+                                      Color(0x0001DE4D),
+                                    ],
+                                    transform: GradientRotation(-1.2),
+                                  ),
+                                ),
+                              ),
+                        isSeen
+                            ? const SizedBox.shrink()
+                            : Stack(
+                                fit: StackFit.expand,
+                                alignment: Alignment.bottomCenter,
+                                children: [
+                                  Row(
+                                    mainAxisAlignment: MainAxisAlignment.center,
+                                    crossAxisAlignment: CrossAxisAlignment.end,
+                                    children: [
+                                      Transform.scale(
+                                        scale: scale,
+                                        child: Container(
+                                          decoration: const BoxDecoration(
+                                            gradient: LinearGradient(
+                                              stops: [0, 0.5, 1],
+                                              colors: [
+                                                Colors.transparent,
+                                                Color(0xFF01DE4D),
+                                                Colors.transparent,
+                                              ],
+                                            ),
+                                          ),
+                                          height: 1 * scale,
+                                          width: 68 * scale,
+                                        ),
+                                      ),
+                                    ],
+                                  ),
+                                ],
+                              ),
+                        Positioned(
+                          bottom: 8 * scale,
+                          child: Transform.scale(
+                            scale: scale,
+                            child: SizedBox(
+                              width: MemoryCoverWidget.width,
+                              child: Padding(
+                                padding: const EdgeInsets.symmetric(
+                                  horizontal: 8.0,
+                                ),
+                                child: Hero(
+                                  tag: title,
+                                  child: Text(
+                                    title,
+                                    style: getEnteTextTheme(context)
+                                        .miniBold
+                                        .copyWith(
+                                          color: Colors.white,
+                                        ),
+                                  ),
+                                ),
+                              ),
+                            ),
+                          ),
+                        ),
+                      ],
                     ),
                   ),
                 ),
               ],
             ),
           ),
-        ],
-      ),
-    );
-  }
-
-  Container _buildMemoryItem(BuildContext context, int index) {
-    final colorScheme = getEnteColorScheme(context);
-    final memory = widget.memories[index];
-    final isSeen = memory.isSeen();
-    return Container(
-      decoration: BoxDecoration(
-        border: Border.all(
-          color: isSeen ? colorScheme.strokeFaint : colorScheme.primary500,
-          width: 2,
-        ),
-        borderRadius: BorderRadius.circular(40),
-      ),
-      child: ClipOval(
-        child: SizedBox(
-          width: 56,
-          height: 56,
-          child: Hero(
-            tag: "memories" + memory.file.tag,
-            child: ThumbnailWidget(
-              memory.file,
-              shouldShowSyncStatus: false,
-              key: Key("memories" + memory.file.tag),
-            ),
-          ),
+        );
+      },
+      child: Hero(
+        tag: "memories" + memory.file.tag,
+        child: ThumbnailWidget(
+          memory.file,
+          shouldShowArchiveStatus: false,
+          key: Key("memories" + memory.file.tag),
         ),
       ),
     );

+ 1 - 1
pubspec.yaml

@@ -12,7 +12,7 @@ description: ente photos application
 # Read more about iOS versioning at
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 
-version: 0.8.35+555
+version: 0.8.36+556
 
 environment:
   sdk: ">=3.0.0 <4.0.0"