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/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); @override State createState() => _MemoriesWidgetState(); } class _MemoriesWidgetState extends State { final double _widthOfItem = 85; late ScrollController _controller; late StreamSubscription _subscription; @override void initState() { super.initState(); _subscription = Bus.instance.on().listen((event) { if (mounted) { setState(() {}); } }); _controller = ScrollController(); } @override void dispose() { _subscription.cancel(); _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { if (!MemoriesService.instance.showMemories) { return const SizedBox.shrink(); } return FutureBuilder>( future: MemoriesService.instance.getMemories(), builder: (context, snapshot) { if (snapshot.hasError || !snapshot.hasData || snapshot.data!.isEmpty) { return const SizedBox.shrink(); } else { 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 SizedBox(height: 10), ], ); } }, ); } Widget _buildMemories(List memories) { final collatedMemories = _collateMemories(memories); 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, ); }, ), ); } List> _collateMemories(List memories) { final List yearlyMemories = []; final List> collatedMemories = []; for (int index = 0; index < memories.length; index++) { if (index > 0 && !_areMemoriesFromSameYear(memories[index - 1], memories[index])) { final List collatedYearlyMemories = []; collatedYearlyMemories.addAll(yearlyMemories); collatedMemories.add(collatedYearlyMemories); yearlyMemories.clear(); } yearlyMemories.add(memories[index]); } if (yearlyMemories.isNotEmpty) { collatedMemories.add(yearlyMemories); } return collatedMemories.reversed.toList(); } bool _areMemoriesFromSameYear(Memory first, Memory second) { final firstDate = DateTime.fromMicrosecondsSinceEpoch(first.file.creationTime!); final secondDate = DateTime.fromMicrosecondsSinceEpoch(second.file.creationTime!); return firstDate.year == secondDate.year; } }