import 'package:journaler/db.dart'; import 'package:intl/intl.dart'; class Note { final String date; late final String displayDate; String content; String? snippet; bool isProblematic; String problemReason; Note({ required this.date, required this.content, this.snippet, this.isProblematic = false, this.problemReason = '', }) { final dtUtc = DateFormat('yyyy-MM-dd HH:mm:ss').parse(date, true); final dtLocal = dtUtc.toLocal(); displayDate = DateFormat('yyyy-MM-dd HH:mm:ss').format(dtLocal); } } class Scratch { final String date; String content; Scratch({required this.date, required this.content}); } Future getLatestNote() async { final note = await DB.db.rawQuery( 'SELECT content, date FROM notes ORDER BY date DESC LIMIT 1', ); if (note.isEmpty) { return null; } return Note( date: note[0]['date'] as String, content: note[0]['content'] as String, ); } Future createNote(String content) async { // Trim each line, sometimes we fuck up by doing a lil "foobar " // Maybe I should also look for \s{2,}... final lines = content.split('\n'); final trimmedLines = []; for (final line in lines) { final trimmedContent = line.trim().replaceAll(RegExp(r'\s{2,}'), ' '); if (trimmedContent.isEmpty) { continue; } trimmedLines.add(trimmedContent); } final trimmedContent = trimmedLines.join('\n'); await DB.db.insert('notes', {'content': trimmedContent}); } Future updateNote(Note note) async { // Trim the content to avoid saving just whitespace final trimmedContent = note.content.trim(); if (trimmedContent.isEmpty) { // Delete the note if content is empty await DB.db.delete('notes', where: 'date = ?', whereArgs: [note.date]); return; } await DB.db.update( 'notes', {'content': trimmedContent}, where: 'date = ?', whereArgs: [note.date], ); } Future getLatestScratch() async { final scratch = await DB.db.rawQuery( 'SELECT content, date FROM scratches ORDER BY date DESC LIMIT 1', ); if (scratch.isEmpty) { return null; } else { return Scratch( date: scratch[0]['date'] as String, content: scratch[0]['content'] as String, ); } } Future createScratch(String content) async { // Trim content but allow empty scratch notes (they might be intentionally cleared) final trimmedContent = content.trim(); await DB.db.insert('scratches', {'content': trimmedContent}); } // 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(); } /// Delete a note by its date Future deleteNote(String date) async { final result = await DB.db.delete( 'notes', where: 'date = ?', whereArgs: [date], ); return result > 0; // Return true if a note was deleted } // Search notes using full-text search Future> searchNotes(String query) async { if (query.isEmpty) { return []; } // Call DB search function final List> results = await DB.searchNotes(query); // Convert results to Note objects return results .map( (result) => Note( date: result['date'] as String, content: result['content'] as String, snippet: result['snippet'] as String?, // Highlighted snippets from FTS ), ) .toList(); } // Find potentially problematic entries based on character distribution Future> findProblematicEntries({ double maxCharPercentage = 0.7, }) async { // Simple SQLite query that counts character occurrences using replace final List> results = await DB.db.rawQuery( ''' WITH char_counts AS ( SELECT id, date, content, substr(content, 1, 1) as char, (length(content) - length(replace(content, substr(content, 1, 1), ''))) as char_count, length(content) as total_length, cast(length(content) - length(replace(content, substr(content, 1, 1), '')) as float) / length(content) as percentage FROM notes ) SELECT * FROM char_counts WHERE percentage > ? ORDER BY date DESC ''', [maxCharPercentage], ); return results .map( (row) => Note( date: row['date'] as String, content: row['content'] as String, isProblematic: true, problemReason: 'Character "${row['char']}" makes up ${(row['percentage'] * 100).toStringAsFixed(1)}% of the content', ), ) .toList(); }