mirror of
https://github.com/Myzel394/quid_faciam_hodie.git
synced 2025-06-19 15:45:26 +02:00
improvements
This commit is contained in:
parent
8409939825
commit
13ea760cb8
68
lib/models/timeline.dart
Normal file
68
lib/models/timeline.dart
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:share_location/foreign_types/memory.dart';
|
||||||
|
import 'package:share_location/models/memory_pack.dart';
|
||||||
|
|
||||||
|
class TimelineModel extends ChangeNotifier {
|
||||||
|
final Map<String, MemoryPack> _timeline;
|
||||||
|
|
||||||
|
TimelineModel({
|
||||||
|
Map<String, MemoryPack>? timeline,
|
||||||
|
}) : _timeline = timeline ?? {};
|
||||||
|
|
||||||
|
Map<String, MemoryPack> get values => _timeline;
|
||||||
|
|
||||||
|
static TimelineModel fromMemoriesList(
|
||||||
|
final List<Memory> memories,
|
||||||
|
) {
|
||||||
|
final map = <String, List<Memory>>{};
|
||||||
|
|
||||||
|
for (final memory in memories) {
|
||||||
|
final date = DateFormat('yyyy-MM-dd').format(memory.creationDate);
|
||||||
|
if (map.containsKey(date)) {
|
||||||
|
map[date]!.add(memory);
|
||||||
|
} else {
|
||||||
|
map[date] = [memory];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final data = Map.fromEntries(
|
||||||
|
map.entries.map(
|
||||||
|
(entry) => MapEntry<String, MemoryPack>(
|
||||||
|
entry.key,
|
||||||
|
MemoryPack(entry.value),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return TimelineModel(
|
||||||
|
timeline: data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_timeline.values.forEach((memoryPack) {
|
||||||
|
memoryPack.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeEmptyDates() {
|
||||||
|
final previousLength = _timeline.length;
|
||||||
|
|
||||||
|
_timeline.removeWhere((key, value) => value.memories.isEmpty);
|
||||||
|
|
||||||
|
final newLength = _timeline.length;
|
||||||
|
|
||||||
|
if (previousLength != newLength) {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime dateAtIndex(final int index) =>
|
||||||
|
DateTime.parse(_timeline.keys.elementAt(index));
|
||||||
|
|
||||||
|
MemoryPack atIndex(final int index) => _timeline.values.elementAt(index);
|
||||||
|
}
|
@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:share_location/controllers/status_controller.dart';
|
import 'package:share_location/controllers/status_controller.dart';
|
||||||
import 'package:share_location/enums.dart';
|
import 'package:share_location/enums.dart';
|
||||||
import 'package:share_location/foreign_types/memory.dart';
|
import 'package:share_location/foreign_types/memory.dart';
|
||||||
|
import 'package:share_location/models/memory_pack.dart';
|
||||||
import 'package:share_location/models/timeline_overlay.dart';
|
import 'package:share_location/models/timeline_overlay.dart';
|
||||||
import 'package:share_location/widgets/status.dart';
|
import 'package:share_location/widgets/status.dart';
|
||||||
|
|
||||||
@ -59,6 +60,7 @@ class _MemorySlideState extends State<MemorySlide>
|
|||||||
|
|
||||||
void initializeAnimation(final Duration duration) {
|
void initializeAnimation(final Duration duration) {
|
||||||
final timelineOverlay = context.read<TimelineOverlay>();
|
final timelineOverlay = context.read<TimelineOverlay>();
|
||||||
|
final memoryPack = context.read<MemoryPack>();
|
||||||
|
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
|
|
||||||
@ -72,7 +74,8 @@ class _MemorySlideState extends State<MemorySlide>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (controller!.done) {
|
if (controller!.done) {
|
||||||
timelineOverlay.setState(TimelineState.completed);
|
timelineOverlay.reset();
|
||||||
|
memoryPack.next();
|
||||||
}
|
}
|
||||||
}, ['done']);
|
}, ['done']);
|
||||||
|
|
||||||
@ -81,10 +84,10 @@ class _MemorySlideState extends State<MemorySlide>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<TimelineOverlay>(
|
return Consumer<MemoryPack>(
|
||||||
builder: (context, overlayController, _) => Status(
|
builder: (context, memoryPack, _) => Status(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
disabled: !overlayController.showOverlay,
|
disabled: memoryPack.paused,
|
||||||
child: MemoryView(
|
child: MemoryView(
|
||||||
creationDate: widget.memory.creationDate,
|
creationDate: widget.memory.creationDate,
|
||||||
location: widget.memory.location,
|
location: widget.memory.location,
|
||||||
@ -99,17 +102,17 @@ class _MemorySlideState extends State<MemorySlide>
|
|||||||
if (mounted) {
|
if (mounted) {
|
||||||
initializeAnimation(controller.value.duration);
|
initializeAnimation(controller.value.duration);
|
||||||
|
|
||||||
overlayController.addListener(() {
|
memoryPack.addListener(() {
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overlayController.state == TimelineState.playing) {
|
if (memoryPack.paused) {
|
||||||
controller.play();
|
|
||||||
} else {
|
|
||||||
controller.pause();
|
controller.pause();
|
||||||
|
} else {
|
||||||
|
controller.play();
|
||||||
}
|
}
|
||||||
}, ['state']);
|
}, ['paused']);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -13,12 +13,14 @@ class TimelinePage extends StatefulWidget {
|
|||||||
final DateTime date;
|
final DateTime date;
|
||||||
final VoidCallback onPreviousTimeline;
|
final VoidCallback onPreviousTimeline;
|
||||||
final VoidCallback onNextTimeline;
|
final VoidCallback onNextTimeline;
|
||||||
|
final VoidCallback onMemoryRemoved;
|
||||||
|
|
||||||
const TimelinePage({
|
const TimelinePage({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.date,
|
required this.date,
|
||||||
required this.onPreviousTimeline,
|
required this.onPreviousTimeline,
|
||||||
required this.onNextTimeline,
|
required this.onNextTimeline,
|
||||||
|
required this.onMemoryRemoved,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -48,6 +50,18 @@ class _TimelinePageState extends State<TimelinePage> {
|
|||||||
}
|
}
|
||||||
}, ['state']);
|
}, ['state']);
|
||||||
|
|
||||||
|
memoryPack.addListener(() {
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memoryPack.paused) {
|
||||||
|
timelineOverlayController.hideOverlay();
|
||||||
|
} else {
|
||||||
|
timelineOverlayController.restoreOverlay();
|
||||||
|
}
|
||||||
|
}, ['state']);
|
||||||
|
|
||||||
memoryPack.addListener(() {
|
memoryPack.addListener(() {
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
@ -81,7 +95,6 @@ class _TimelinePageState extends State<TimelinePage> {
|
|||||||
final memoryPack = context.read<MemoryPack>();
|
final memoryPack = context.read<MemoryPack>();
|
||||||
|
|
||||||
memoryPack.pause();
|
memoryPack.pause();
|
||||||
timelineOverlayController.hideOverlay();
|
|
||||||
|
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
@ -98,16 +111,15 @@ class _TimelinePageState extends State<TimelinePage> {
|
|||||||
|
|
||||||
memoryPack.removeCurrentMemory();
|
memoryPack.removeCurrentMemory();
|
||||||
memoryPack.resume();
|
memoryPack.resume();
|
||||||
timelineOverlayController.restoreOverlay();
|
|
||||||
|
widget.onMemoryRemoved();
|
||||||
},
|
},
|
||||||
onTapDown: (_) {
|
onTapDown: (_) {
|
||||||
final memoryPack = context.read<MemoryPack>();
|
final memoryPack = context.read<MemoryPack>();
|
||||||
|
|
||||||
memoryPack.pause();
|
|
||||||
|
|
||||||
overlayRemover = Timer(
|
overlayRemover = Timer(
|
||||||
const Duration(milliseconds: 200),
|
const Duration(milliseconds: 200),
|
||||||
timelineOverlayController.hideOverlay,
|
memoryPack.pause,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onTapUp: (_) {
|
onTapUp: (_) {
|
||||||
@ -115,14 +127,11 @@ class _TimelinePageState extends State<TimelinePage> {
|
|||||||
|
|
||||||
overlayRemover?.cancel();
|
overlayRemover?.cancel();
|
||||||
memoryPack.resume();
|
memoryPack.resume();
|
||||||
timelineOverlayController.restoreOverlay();
|
|
||||||
},
|
},
|
||||||
onTapCancel: () {
|
onTapCancel: () {
|
||||||
final memoryPack = context.read<MemoryPack>();
|
final memoryPack = context.read<MemoryPack>();
|
||||||
|
|
||||||
overlayRemover?.cancel();
|
overlayRemover?.cancel();
|
||||||
|
|
||||||
timelineOverlayController.restoreOverlay();
|
|
||||||
memoryPack.resume();
|
memoryPack.resume();
|
||||||
},
|
},
|
||||||
onHorizontalDragEnd: (details) {
|
onHorizontalDragEnd: (details) {
|
||||||
@ -146,34 +155,30 @@ class _TimelinePageState extends State<TimelinePage> {
|
|||||||
},
|
},
|
||||||
child: ChangeNotifierProvider<TimelineOverlay>(
|
child: ChangeNotifierProvider<TimelineOverlay>(
|
||||||
create: (_) => timelineOverlayController,
|
create: (_) => timelineOverlayController,
|
||||||
child: Stack(
|
child: Consumer<MemoryPack>(
|
||||||
fit: StackFit.expand,
|
builder: (_, memoryPack, __) => Stack(
|
||||||
children: <Widget>[
|
fit: StackFit.expand,
|
||||||
Consumer<MemoryPack>(
|
children: <Widget>[
|
||||||
builder: (_, memoryPack, __) {
|
PageView.builder(
|
||||||
return PageView.builder(
|
controller: pageController,
|
||||||
controller: pageController,
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
scrollDirection: Axis.horizontal,
|
||||||
scrollDirection: Axis.horizontal,
|
itemBuilder: (_, index) => MemorySlide(
|
||||||
itemBuilder: (_, index) => MemorySlide(
|
key: Key(memoryPack.memories[index].filename),
|
||||||
key: Key(memoryPack.memories[index].filename),
|
memory: memoryPack.memories[index],
|
||||||
memory: memoryPack.memories[index],
|
),
|
||||||
),
|
itemCount: memoryPack.memories.length,
|
||||||
itemCount: memoryPack.memories.length,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: LARGE_SPACE,
|
|
||||||
left: MEDIUM_SPACE,
|
|
||||||
right: MEDIUM_SPACE,
|
|
||||||
),
|
),
|
||||||
child: Consumer<TimelineOverlay>(
|
Padding(
|
||||||
builder: (context, overlayController, _) => AnimatedOpacity(
|
padding: const EdgeInsets.only(
|
||||||
|
top: LARGE_SPACE,
|
||||||
|
left: MEDIUM_SPACE,
|
||||||
|
right: MEDIUM_SPACE,
|
||||||
|
),
|
||||||
|
child: AnimatedOpacity(
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
curve: Curves.linearToEaseOut,
|
curve: Curves.linearToEaseOut,
|
||||||
opacity: overlayController.showOverlay ? 1.0 : 0.0,
|
opacity: memoryPack.paused ? 0.0 : 1.0,
|
||||||
child: Text(
|
child: Text(
|
||||||
DateFormat('dd. MMMM yyyy').format(widget.date),
|
DateFormat('dd. MMMM yyyy').format(widget.date),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@ -181,8 +186,26 @@ class _TimelinePageState extends State<TimelinePage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Positioned(
|
||||||
],
|
right: SMALL_SPACE,
|
||||||
|
bottom: SMALL_SPACE * 2,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: SMALL_SPACE),
|
||||||
|
child: AnimatedOpacity(
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
|
curve: Curves.linearToEaseOut,
|
||||||
|
opacity: memoryPack.paused ? 0.0 : 1.0,
|
||||||
|
child: Consumer<MemoryPack>(
|
||||||
|
builder: (_, memoryPack, __) => Text(
|
||||||
|
'${memoryPack.currentMemoryIndex + 1}/${memoryPack.memories.length}',
|
||||||
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:share_location/foreign_types/memory.dart';
|
import 'package:share_location/foreign_types/memory.dart';
|
||||||
import 'package:share_location/models/memory_pack.dart';
|
import 'package:share_location/models/timeline.dart';
|
||||||
import 'package:share_location/utils/loadable.dart';
|
import 'package:share_location/utils/loadable.dart';
|
||||||
import 'package:share_location/widgets/timeline_page.dart';
|
import 'package:share_location/widgets/timeline_page.dart';
|
||||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||||
@ -20,7 +19,7 @@ class TimelineScroll extends StatefulWidget {
|
|||||||
|
|
||||||
class _TimelineScrollState extends State<TimelineScroll> with Loadable {
|
class _TimelineScrollState extends State<TimelineScroll> with Loadable {
|
||||||
final pageController = PageController();
|
final pageController = PageController();
|
||||||
Map<String, MemoryPack>? timeline;
|
TimelineModel? timeline;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
@ -28,7 +27,18 @@ class _TimelineScrollState extends State<TimelineScroll> with Loadable {
|
|||||||
loadTimeline();
|
loadTimeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
dispose() {
|
||||||
|
pageController.dispose();
|
||||||
|
|
||||||
|
timeline?.dispose();
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> loadTimeline() async {
|
Future<void> loadTimeline() async {
|
||||||
|
timeline?.dispose();
|
||||||
|
|
||||||
final response = await supabase
|
final response = await supabase
|
||||||
.from('memories')
|
.from('memories')
|
||||||
.select()
|
.select()
|
||||||
@ -36,35 +46,16 @@ class _TimelineScrollState extends State<TimelineScroll> with Loadable {
|
|||||||
.execute();
|
.execute();
|
||||||
final memories = List<Memory>.from(
|
final memories = List<Memory>.from(
|
||||||
List<Map<String, dynamic>>.from(response.data).map(Memory.parse));
|
List<Map<String, dynamic>>.from(response.data).map(Memory.parse));
|
||||||
final timelineMapped = convertMemoriesToTimeline(memories);
|
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
timeline = timelineMapped;
|
timeline = TimelineModel.fromMemoriesList(memories);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, MemoryPack> convertMemoriesToTimeline(
|
timeline!.addListener(() {
|
||||||
final List<Memory> memories,
|
if (mounted) {
|
||||||
) {
|
setState(() {});
|
||||||
final map = <String, List<Memory>>{};
|
|
||||||
|
|
||||||
for (final memory in memories) {
|
|
||||||
final date = DateFormat('yyyy-MM-dd').format(memory.creationDate);
|
|
||||||
if (map.containsKey(date)) {
|
|
||||||
map[date]!.add(memory);
|
|
||||||
} else {
|
|
||||||
map[date] = [memory];
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
return Map.fromEntries(
|
|
||||||
map.entries.map(
|
|
||||||
(entry) => MapEntry<String, MemoryPack>(
|
|
||||||
entry.key,
|
|
||||||
MemoryPack(entry.value),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -79,11 +70,12 @@ class _TimelineScrollState extends State<TimelineScroll> with Loadable {
|
|||||||
body: PageView.builder(
|
body: PageView.builder(
|
||||||
controller: pageController,
|
controller: pageController,
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
itemCount: timeline!.length,
|
itemCount: timeline!.values.length,
|
||||||
itemBuilder: (_, index) => ChangeNotifierProvider<MemoryPack>(
|
itemBuilder: (_, index) => ChangeNotifierProvider.value(
|
||||||
create: (_) => timeline!.values.elementAt(index),
|
value: timeline!.atIndex(index),
|
||||||
child: TimelinePage(
|
child: TimelinePage(
|
||||||
date: DateTime.parse(timeline!.keys.toList()[index]),
|
date: timeline!.dateAtIndex(index),
|
||||||
|
onMemoryRemoved: () => timeline!.removeEmptyDates(),
|
||||||
onNextTimeline: () {
|
onNextTimeline: () {
|
||||||
pageController.nextPage(
|
pageController.nextPage(
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user