diff --git a/lib/main.dart b/lib/main.dart index 1c04f39..fced81a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,6 +6,7 @@ import 'package:journaler/notes.dart'; import 'package:system_tray/system_tray.dart'; import 'package:window_manager/window_manager.dart'; import 'package:audioplayers/audioplayers.dart'; +import 'package:flutter/gestures.dart'; // TODO: Add an icon to the executable, simply use the existing tray icon // TODO: Implement some sort of scroll through notes @@ -137,9 +138,13 @@ class MainPageState extends State with WindowListener { final TextEditingController _soundController = TextEditingController(); Note? previousNote; + Note? _currentlyDisplayedNote; Duration _currentPopupInterval = _defaultPopupInterval; String _currentNotificationSound = _defaultNotificationSound; + bool _canGoPrevious = false; + bool _canGoNext = false; + Timer? _popupTimer; Timer? _debounceTimer; @@ -237,6 +242,50 @@ class MainPageState extends State with WindowListener { } } + Future _checkNavigation() async { + if (_currentlyDisplayedNote == null) { + setState(() { + _canGoPrevious = false; + _canGoNext = false; + }); + return; + } + + final prev = await getPreviousNote(_currentlyDisplayedNote!.date); + final bool isLatest = _currentlyDisplayedNote!.date == previousNote?.date; + + setState(() { + _canGoPrevious = prev != null; + _canGoNext = !isLatest; + }); + } + + Future _goToPreviousNote() async { + if (!_canGoPrevious || _currentlyDisplayedNote == null) return; + + final prevNote = await getPreviousNote(_currentlyDisplayedNote!.date); + if (prevNote != null) { + setState(() { + _currentlyDisplayedNote = prevNote; + _previousEntryController.text = prevNote.content; + }); + await _checkNavigation(); + } + } + + Future _goToNextNote() async { + if (!_canGoNext || _currentlyDisplayedNote == null) return; + + final nextNote = await getNextNote(_currentlyDisplayedNote!.date); + if (nextNote != null) { + setState(() { + _currentlyDisplayedNote = nextNote; + _previousEntryController.text = nextNote.content; + }); + await _checkNavigation(); + } + } + void _loadData() async { String? intervalMinutesStr = await DB.getSetting('popupIntervalMinutes'); String? soundFileStr = await DB.getSetting('notificationSound'); @@ -252,12 +301,15 @@ class MainPageState extends State with WindowListener { final note = await getLatestNote(); previousNote = note; - _previousEntryController.text = note?.content ?? ""; + _currentlyDisplayedNote = note; + _previousEntryController.text = _currentlyDisplayedNote?.content ?? ""; final scratch = await getLatestScratch(); _scratchController.text = scratch?.content ?? ""; _currentEntryController.text = ""; + await _checkNavigation(); + debugPrint("Data loaded."); } @@ -391,35 +443,82 @@ class MainPageState extends State with WindowListener { children: [ Expanded( flex: 9, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded( - child: TextField( - controller: _previousEntryController, - maxLines: null, - expands: true, - style: Theme.of(context).textTheme.bodyMedium, - decoration: const InputDecoration( - labelText: 'Previous Entry', + child: Listener( + behavior: HitTestBehavior.opaque, + onPointerSignal: (pointerSignal) { + if (pointerSignal is PointerScrollEvent) { + if (pointerSignal.scrollDelta.dy < 0) { + if (_canGoPrevious) { + _goToPreviousNote(); + } + } else if (pointerSignal.scrollDelta.dy > 0) { + if (_canGoNext) { + _goToNextNote(); + } + } + } + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Combine Label, Buttons, and TextField for Previous Entry + Row( + children: [ + Expanded( + child: Text( + _currentlyDisplayedNote?.date == previousNote?.date + ? 'Previous Entry (Latest)' + : 'Entry: ${_currentlyDisplayedNote?.date ?? 'N/A'}', + style: TextStyle(fontSize: 18, color: Colors.grey), + ), + ), + IconButton( + icon: const Icon(Icons.arrow_back), + tooltip: 'Previous Note', + onPressed: _canGoPrevious ? _goToPreviousNote : null, + ), + IconButton( + icon: const Icon(Icons.arrow_forward), + tooltip: 'Next Note', + onPressed: _canGoNext ? _goToNextNote : null, + ), + ], + ), + Expanded( + child: TextField( + controller: _previousEntryController, + readOnly: _currentlyDisplayedNote?.date != previousNote?.date, + maxLines: null, + expands: true, + style: Theme.of(context).textTheme.bodyMedium, + decoration: InputDecoration( + hintText: _currentlyDisplayedNote?.date != previousNote?.date + ? 'Viewing note from ${_currentlyDisplayedNote?.date} (Read-Only)' + : 'Latest Note', + border: const OutlineInputBorder(), + filled: _currentlyDisplayedNote?.date != previousNote?.date, + fillColor: _currentlyDisplayedNote?.date != previousNote?.date + ? Colors.grey.withOpacity(0.1) + : null, + ), ), ), - ), - const SizedBox(height: 8), - Expanded( - child: TextField( - controller: _currentEntryController, - focusNode: _currentEntryFocusNode, - maxLines: null, - expands: true, - autofocus: true, - style: Theme.of(context).textTheme.bodyMedium, - decoration: const InputDecoration( - labelText: 'Current Entry (What\'s on your mind?)', + const SizedBox(height: 8), + Expanded( + child: TextField( + controller: _currentEntryController, + focusNode: _currentEntryFocusNode, + maxLines: null, + expands: true, + autofocus: true, + style: Theme.of(context).textTheme.bodyMedium, + decoration: const InputDecoration( + labelText: 'Current Entry (What\'s on your mind?)', + ), ), ), - ), - ], + ], + ), ), ), const SizedBox(width: 8), diff --git a/lib/notes.dart b/lib/notes.dart index 55bce72..b4b254f 100644 --- a/lib/notes.dart +++ b/lib/notes.dart @@ -61,3 +61,42 @@ Future getLatestScratch() async { Future createScratch(String content) async { await DB.db.insert('scratches', {'content': content}); } + +// Get the note immediately older than the given date +Future getPreviousNote(String currentDate) async { + final List> notes = await DB.db.query( + 'notes', + where: 'date < ?', + whereArgs: [currentDate], + orderBy: 'date DESC', + limit: 1, + ); + if (notes.isNotEmpty) { + return Note( + date: notes.first['date'] as String, + content: notes.first['content'] as String, + ); + } + return null; +} + +// Get the note immediately newer than the given date +Future getNextNote(String currentDate) async { + final List> notes = await DB.db.query( + 'notes', + where: 'date > ?', + whereArgs: [currentDate], + orderBy: 'date ASC', + limit: 1, + ); + if (notes.isNotEmpty) { + return Note( + date: notes.first['date'] as String, + content: notes.first['content'] as String, + ); + } + // If there's no newer note, it means we might be at the latest + // but let's double-check by explicitly getting the latest again. + // This handles the case where the `currentDate` might not be the absolute latest. + return getLatestNote(); +}