diff --git a/lib/models/memory_pack.dart b/lib/models/memory_pack.dart index 23f97bb..8e39de9 100644 --- a/lib/models/memory_pack.dart +++ b/lib/models/memory_pack.dart @@ -6,4 +6,25 @@ class MemoryPack { const MemoryPack(this._memories); List get memories => _memories; + + void orderMemories() { + _memories.sort((a, b) => b.creationDate.compareTo(a.creationDate)); + } + + void updateWithNewMemory(final String memoryID, final Memory memory) { + final index = _memories.indexWhere((memory) => memory.id == memoryID); + + if (index == -1) { + throw Exception('Memory not found'); + } + + _memories[index] = memory; + + orderMemories(); + } + + void addMemory(final Memory memory) { + _memories.add(memory); + orderMemories(); + } } diff --git a/lib/models/timeline.dart b/lib/models/timeline.dart index 3077408..bafdcd1 100644 --- a/lib/models/timeline.dart +++ b/lib/models/timeline.dart @@ -15,7 +15,7 @@ class TimelineModel extends PropertyChangeNotifier { Map? timeline, }) : _timeline = timeline ?? {}; - late RealtimeSubscription _serverSubscription; + RealtimeSubscription? _serverSubscription; int _currentIndex = 0; int _memoryIndex = 0; @@ -29,17 +29,36 @@ class TimelineModel extends PropertyChangeNotifier { bool get paused => _paused; bool get isInitializing => _isInitializing; + DateTime dateAtIndex(final int index) => + DateTime.parse(_timeline.keys.elementAt(index)); + + MemoryPack atIndex(final int index) => _timeline.values.elementAt(index); + + MemoryPack get _currentMemoryPack => atIndex(currentIndex); + bool get _isAtLastMemory => + _memoryIndex == _currentMemoryPack.memories.length - 1; + Memory get currentMemory => + _currentMemoryPack.memories.elementAt(_memoryIndex); + + void _removeEmptyDates() { + _timeline.removeWhere((key, value) => value.memories.isEmpty); + } + + static formatCreationDateKey(final DateTime date) => + DateFormat('yyyy-MM-dd').format(date); + static Map mapFromMemoriesList( final List memories, ) { final map = >{}; for (final memory in memories) { - final date = DateFormat('yyyy-MM-dd').format(memory.creationDate); - if (map.containsKey(date)) { - map[date]!.add(memory); + final key = formatCreationDateKey(memory.creationDate); + + if (map.containsKey(key)) { + map[key]!.add(memory); } else { - map[date] = [memory]; + map[key] = [memory]; } } @@ -55,25 +74,10 @@ class TimelineModel extends PropertyChangeNotifier { @override void dispose() { - _serverSubscription.unsubscribe(timeout: Duration.zero); + _serverSubscription?.unsubscribe(timeout: Duration.zero); super.dispose(); } - DateTime dateAtIndex(final int index) => - DateTime.parse(_timeline.keys.elementAt(index)); - - MemoryPack atIndex(final int index) => _timeline.values.elementAt(index); - - MemoryPack get _currentMemoryPack => atIndex(currentIndex); - bool get _isAtLastMemory => - _memoryIndex == _currentMemoryPack.memories.length - 1; - Memory get currentMemory => - _currentMemoryPack.memories.elementAt(_memoryIndex); - - void _removeEmptyDates() { - _timeline.removeWhere((key, value) => value.memories.isEmpty); - } - void setCurrentIndex(final int index) { _currentIndex = min(_timeline.length - 1, max(0, index)); notifyListeners('currentIndex'); @@ -97,17 +101,6 @@ class TimelineModel extends PropertyChangeNotifier { notifyListeners('isInitializing'); } - void removeMemory( - final int timelineIndex, - final int memoryIndex, - ) { - _timeline.values.elementAt(timelineIndex).memories.removeAt(memoryIndex); - _removeEmptyDates(); - notifyListeners(); - } - - void removeCurrentMemory() => removeMemory(_currentIndex, _memoryIndex); - void pause() => setPaused(true); void resume() => setPaused(false); @@ -153,61 +146,41 @@ class TimelineModel extends PropertyChangeNotifier { } void _insertMemory(final Memory memory) { - final date = DateFormat('yyyy-MM-dd').format(memory.creationDate); - if (_timeline.containsKey(date)) { - _timeline[date]!.memories.add(memory); - // Sort descending based on creation date - _timeline[date]!.memories.sort( - (a, b) => b.creationDate.compareTo(a.creationDate), - ); - } else { - _timeline[date] = MemoryPack([memory]); - } - } + final key = formatCreationDateKey(memory.creationDate); - void _updateMemory(final String id, final Memory memory) { - final date = DateFormat('yyyy-MM-dd').format(memory.creationDate); - if (_timeline.containsKey(date)) { - _timeline[date]!.memories.removeWhere((m) => m.id == id); - _timeline[date]!.memories.add(memory); - // Sort descending based on creation date - _timeline[date]!.memories.sort( - (a, b) => b.creationDate.compareTo(a.creationDate), - ); - } else { - _timeline[date] = MemoryPack([memory]); - } - } - - void _deleteMemory(final String id) { - for (final date in _timeline.keys) { - _timeline[date]!.memories.removeWhere((m) => m.id == id); - } - } - - Future _onMemoriesUpdate( - final SupabaseRealtimePayload response, - ) async { - if (response == null) { + if (!_timeline.containsKey(key)) { + _timeline[key] = MemoryPack([memory]); return; } - switch (response.eventType) { - case 'INSERT': - final memory = Memory.parse(response.newRecord!); - _insertMemory(memory); - break; - case 'UPDATE': - final memory = Memory.parse(response.newRecord!); - _updateMemory(response.oldRecord!['id'], memory); - break; - case 'DELETE': - _deleteMemory(response.oldRecord!['id']); - break; - } + final memoryPack = _timeline[key]!; + + memoryPack.addMemory(memory); } - Future _listenToServer() async { + void _updateMemory(final String id, final Memory memory) { + final key = formatCreationDateKey(memory.creationDate); + + if (!_timeline.containsKey(key)) { + _timeline[key] = MemoryPack([memory]); + return; + } + + final memoryPack = _timeline[key]!; + + memoryPack.updateWithNewMemory(id, memory); + } + + void _deleteMemory(final String id) { + // Search for correct `Memory` and remove it + for (final memories in _timeline.values) { + memories.memories.removeWhere((memory) => memory.id == id); + } + + _removeEmptyDates(); + } + + Future _fetchInitialData() async { final response = await supabase .from('memories') .select() @@ -220,12 +193,44 @@ class TimelineModel extends PropertyChangeNotifier { values ..clear() ..addAll(mapFromMemoriesList(memories)); + } - _serverSubscription = supabase - .from('memories') - .on(SupabaseEventTypes.all, _onMemoriesUpdate) - .subscribe(); + Future _onServerUpdate( + final SupabaseRealtimePayload response, + ) async { + if (response == null) { + return; + } + + switch (response.eventType) { + case 'INSERT': + final memory = Memory.parse(response.newRecord!); + _insertMemory(memory); + break; + case 'UPDATE': + final memoryID = response.oldRecord!['id']; + final memory = Memory.parse(response.newRecord!); + + _updateMemory(memoryID, memory); + break; + case 'DELETE': + final id = response.oldRecord!['id']; + + _deleteMemory(id); + break; + } notifyListeners(); } + + Future _listenToServer() async { + await _fetchInitialData(); + notifyListeners(); + + // Watch new updates + _serverSubscription = supabase + .from('memories') + .on(SupabaseEventTypes.all, _onServerUpdate) + .subscribe(); + } }