Implement whole ass "backend"
This commit is contained in:
86
lib/db.dart
Normal file
86
lib/db.dart
Normal file
@@ -0,0 +1,86 @@
|
||||
import 'dart:io' show Platform, Directory;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
|
||||
const settingsDir = '.journaler';
|
||||
const dbFileName = 'journaler.db';
|
||||
|
||||
class DB {
|
||||
static late Database db;
|
||||
|
||||
static const String _schema = '''
|
||||
CREATE TABLE IF NOT EXISTS notes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
date TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
content TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_notes_date ON notes (date);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_notes_date_unique ON notes (date);
|
||||
-- Todos are "static", we are only interested in the latest entry
|
||||
-- ie. they're not really a list but instead a string
|
||||
-- But we will also keep a history of all todos
|
||||
-- Because we might as well
|
||||
CREATE TABLE IF NOT EXISTS todos (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
date TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
content TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_todos_date ON todos (date);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_todos_date_unique ON todos (date);
|
||||
''';
|
||||
|
||||
static Future<String> _getDatabasePath() async {
|
||||
debugPrint('Attempting to get database path...');
|
||||
if (Platform.isWindows || Platform.isLinux) {
|
||||
// Get user's home directory
|
||||
final home =
|
||||
Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];
|
||||
if (home == null) {
|
||||
throw Exception('Could not find home directory');
|
||||
}
|
||||
debugPrint('Home directory found: home');
|
||||
|
||||
final dbDir = Directory(path.join(home, settingsDir));
|
||||
if (!await dbDir.exists()) {
|
||||
await dbDir.create(recursive: true);
|
||||
debugPrint('$settingsDir directory created');
|
||||
} else {
|
||||
debugPrint('$settingsDir directory already exists');
|
||||
}
|
||||
|
||||
return path.join(dbDir.path, dbFileName);
|
||||
} else {
|
||||
// Default path for other platforms
|
||||
final databasesPath = await databaseFactoryFfi.getDatabasesPath();
|
||||
debugPrint('Using default databases path: databasesPath');
|
||||
return path.join(databasesPath, dbFileName);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> init() async {
|
||||
debugPrint('Starting database initialization...');
|
||||
sqfliteFfiInit();
|
||||
|
||||
final dbPath = await _getDatabasePath();
|
||||
debugPrint('Database path: dbPath');
|
||||
|
||||
try {
|
||||
db = await databaseFactoryFfi.openDatabase(
|
||||
dbPath,
|
||||
options: OpenDatabaseOptions(
|
||||
version: 1,
|
||||
onCreate: (db, version) async {
|
||||
debugPrint('Creating database schema...');
|
||||
await db.execute(_schema);
|
||||
debugPrint('Database schema created successfully');
|
||||
},
|
||||
),
|
||||
);
|
||||
debugPrint('Database opened and initialized');
|
||||
} catch (e) {
|
||||
debugPrint('Failed to initialize database: e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:journaler/db.dart';
|
||||
import 'package:journaler/notes.dart';
|
||||
import 'package:system_tray/system_tray.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:audioplayers/audioplayers.dart';
|
||||
@@ -9,6 +11,7 @@ const Duration popupInterval = Duration(hours: 1);
|
||||
const String notificationSound = 'MeetTheSniper.mp3';
|
||||
|
||||
void main() async {
|
||||
await DB.init();
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await windowManager.ensureInitialized();
|
||||
|
||||
@@ -125,6 +128,8 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
||||
final TextEditingController _currentEntryController = TextEditingController();
|
||||
final TextEditingController _todoController = TextEditingController();
|
||||
|
||||
Note? previousNote;
|
||||
|
||||
Timer? _popupTimer;
|
||||
Timer? _debounceTimer;
|
||||
|
||||
@@ -180,6 +185,7 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
||||
}
|
||||
|
||||
Future<void> _showWindow() async {
|
||||
_loadData();
|
||||
bool wasVisible = await windowManager.isVisible();
|
||||
if (!wasVisible) {
|
||||
await windowManager.setSize(const Size(1600, 900));
|
||||
@@ -193,47 +199,38 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
||||
}
|
||||
|
||||
Future<void> _playSound() async {
|
||||
try {
|
||||
await _audioPlayer.stop();
|
||||
await _audioPlayer.play(AssetSource('sounds/$notificationSound'));
|
||||
debugPrint("Played sound: $notificationSound");
|
||||
} catch (e, stackTrace) {
|
||||
debugPrint("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
debugPrint("Error playing sound '$notificationSound': $e");
|
||||
debugPrint("Stack trace: $stackTrace");
|
||||
debugPrint(
|
||||
"Ensure file exists, is valid audio, and assets/sounds/ is in pubspec.yaml",
|
||||
);
|
||||
debugPrint("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
}
|
||||
await _audioPlayer.stop();
|
||||
await _audioPlayer.play(AssetSource('sounds/$notificationSound'));
|
||||
debugPrint("Played sound: $notificationSound");
|
||||
}
|
||||
|
||||
void _loadData() {
|
||||
_previousEntryController.text =
|
||||
"This is a placeholder for the previous entry.";
|
||||
_todoController.text = "- Placeholder Todo 1\n- Placeholder Todo 2";
|
||||
void _loadData() async {
|
||||
final note = await getLatestNote();
|
||||
previousNote = note;
|
||||
_previousEntryController.text = note?.content ?? "";
|
||||
|
||||
final todo = await getLatestTodo();
|
||||
_todoController.text = todo?.content ?? "";
|
||||
_currentEntryController.text = "";
|
||||
|
||||
debugPrint("Data loaded (placeholder).");
|
||||
}
|
||||
|
||||
void _saveData() {
|
||||
void _saveData() async {
|
||||
String previousEntry = _previousEntryController.text;
|
||||
String currentEntry = _currentEntryController.text;
|
||||
String todoList = _todoController.text;
|
||||
|
||||
print(
|
||||
await createNote(currentEntry);
|
||||
await createTodo(todoList);
|
||||
previousNote!.content = previousEntry;
|
||||
await updateNote(previousNote!);
|
||||
|
||||
debugPrint(
|
||||
"Saving data (placeholder)... Current Entry: [${currentEntry.length} chars], Todo: [${todoList.length} chars]",
|
||||
);
|
||||
}
|
||||
|
||||
void _saveTodoList() {
|
||||
if (_debounceTimer?.isActive ?? false) _debounceTimer!.cancel();
|
||||
|
||||
_debounceTimer = Timer(const Duration(milliseconds: 500), () {
|
||||
String todoList = _todoController.text;
|
||||
print("Debounced Save: Saving Todo list... [${todoList.length} chars]");
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _setWindowConfig() async {
|
||||
await windowManager.setAspectRatio(16 / 9);
|
||||
}
|
||||
@@ -247,7 +244,7 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
||||
if (event is KeyDownEvent &&
|
||||
event.logicalKey == LogicalKeyboardKey.escape) {
|
||||
debugPrint("Escape key pressed - hiding window.");
|
||||
windowManager.hide();
|
||||
onWindowClose();
|
||||
}
|
||||
},
|
||||
child: Scaffold(
|
||||
@@ -302,14 +299,10 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
||||
context,
|
||||
).textTheme.bodyMedium, // Apply theme text style
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Todo List',
|
||||
labelText: 'Todo',
|
||||
// border: OutlineInputBorder(), // Handled by theme
|
||||
// contentPadding: EdgeInsets.all(8.0), // Handled by theme or default
|
||||
),
|
||||
onChanged: (text) {
|
||||
// Auto-save Todo list changes (consider debouncing)
|
||||
_saveTodoList();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
|
64
lib/notes.dart
Normal file
64
lib/notes.dart
Normal file
@@ -0,0 +1,64 @@
|
||||
import 'package:journaler/db.dart';
|
||||
|
||||
class Note {
|
||||
final String date;
|
||||
String content;
|
||||
|
||||
Note({required this.date, required this.content});
|
||||
}
|
||||
|
||||
class Todo {
|
||||
final String date;
|
||||
final String content;
|
||||
|
||||
Todo({required this.date, required this.content});
|
||||
}
|
||||
|
||||
Future<Note?> 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<void> createNote(String content) async {
|
||||
if (content.isEmpty) {
|
||||
return;
|
||||
}
|
||||
await DB.db.insert('notes', {'content': content});
|
||||
}
|
||||
|
||||
Future<void> updateNote(Note note) async {
|
||||
await DB.db.update(
|
||||
'notes',
|
||||
{'content': note.content},
|
||||
where: 'date = ?',
|
||||
whereArgs: [note.date],
|
||||
);
|
||||
}
|
||||
|
||||
Future<Todo?> getLatestTodo() async {
|
||||
final todo = await DB.db.rawQuery(
|
||||
'SELECT content, date FROM todos ORDER BY date DESC LIMIT 1',
|
||||
);
|
||||
if (todo.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return Todo(
|
||||
date: todo[0]['date'] as String,
|
||||
content: todo[0]['content'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> createTodo(String content) async {
|
||||
if (content.isEmpty) {
|
||||
return;
|
||||
}
|
||||
await DB.db.insert('todos', {'content': content});
|
||||
}
|
24
pubspec.lock
24
pubspec.lock
@@ -365,6 +365,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.5"
|
||||
sqflite_common_ffi:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sqflite_common_ffi
|
||||
sha256: "1f3ef3888d3bfbb47785cc1dda0dc7dd7ebd8c1955d32a9e8e9dae1e38d1c4c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.5"
|
||||
sqlite3:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqlite3
|
||||
sha256: "310af39c40dd0bb2058538333c9d9840a2725ae0b9f77e4fd09ad6696aa8f66e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.5"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@@ -37,6 +37,7 @@ dependencies:
|
||||
system_tray: ^2.0.3
|
||||
window_manager: ^0.4.3
|
||||
audioplayers: ^6.4.0
|
||||
sqflite_common_ffi: ^2.3.5
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Reference in New Issue
Block a user