Refactor note handling to include ID generation and improve initialization logic

This commit is contained in:
2025-05-23 22:40:19 +02:00
parent 89f8889f1e
commit dfe1c2b34c
3 changed files with 95 additions and 16 deletions

View File

@@ -66,7 +66,8 @@ Future<void> runPrimaryInstance(File ipcFile) async {
startIpcWatcher(ipcFile); startIpcWatcher(ipcFile);
// Initialize the app // Initialize the app
await DB.init(); // await DB.init();
await init();
await windowManager.ensureInitialized(); await windowManager.ensureInitialized();
WindowOptions windowOptions = const WindowOptions( WindowOptions windowOptions = const WindowOptions(

View File

@@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:core'; import 'dart:core';
import 'dart:math';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:journaler/notes.dart'; import 'package:journaler/notes.dart';
@@ -7,7 +8,15 @@ const endpoint = 'https://meili.site.quack-lab.dev/';
const noteIndex = 'notes'; const noteIndex = 'notes';
const scratchIndex = 'scratch'; const scratchIndex = 'scratch';
const settingsIndex = 'settings'; const settingsIndex = 'settings';
const apiKey = String.fromEnvironment('MEILISEARCH_API_KEY', defaultValue: ''); const apiKey = String.fromEnvironment(
'MEILISEARCH_API_KEY',
defaultValue:
'',
);
const header = {
'Authorization': 'Bearer $apiKey',
'Content-Type': 'application/json',
};
class MeilisearchQuery { class MeilisearchQuery {
String q; String q;
@@ -82,12 +91,60 @@ class MeilisearchResponse {
} }
} }
Future<void> init() async {
if (!await indexExists(noteIndex)) {
await http.post(
Uri.parse('$endpoint/indexes'),
headers: header,
body: jsonEncode({'uid': noteIndex, 'primaryKey': 'id'}),
);
await http.put(
Uri.parse('$endpoint/indexes/$noteIndex/settings/sortable-attributes'),
headers: header,
body: jsonEncode({
'attributes': ['date'],
}),
);
}
if (!await indexExists(scratchIndex)) {
await http.post(
Uri.parse('$endpoint/indexes'),
headers: header,
body: jsonEncode({'uid': scratchIndex, 'primaryKey': 'id'}),
);
await http.put(
Uri.parse('$endpoint/indexes/$scratchIndex/settings/sortable-attributes'),
headers: header,
body: jsonEncode({
'attributes': ['date'],
}),
);
}
if (!await indexExists(settingsIndex)) {
await http.post(
Uri.parse('$endpoint/indexes'),
headers: header,
body: jsonEncode({'uid': settingsIndex, 'primaryKey': 'key'}),
);
}
}
Future<bool> indexExists(String index) async {
final response = await http.get(
Uri.parse('$endpoint/indexes/$index'),
headers: header,
);
return response.statusCode == 200;
}
// Settings Management // Settings Management
Future<String?> getSetting(String key) async { Future<String?> getSetting(String key) async {
final searchCondition = MeilisearchQuery(q: '', filter: 'key = $key'); final searchCondition = MeilisearchQuery(q: '', filter: 'key = $key');
final response = await http.post( final response = await http.post(
Uri.parse('$endpoint/indexes/$settingsIndex/search'), Uri.parse('$endpoint/indexes/$settingsIndex/search'),
headers: {'Authorization': 'Bearer $apiKey'}, headers: header,
body: jsonEncode(searchCondition.toJson()), body: jsonEncode(searchCondition.toJson()),
); );
if (response.statusCode != 200) { if (response.statusCode != 200) {
@@ -104,7 +161,7 @@ Future<void> setSetting(String key, String value) async {
final document = {'key': key, 'value': value}; final document = {'key': key, 'value': value};
final response = await http.post( final response = await http.post(
Uri.parse('$endpoint/indexes/$settingsIndex/documents'), Uri.parse('$endpoint/indexes/$settingsIndex/documents'),
headers: {'Authorization': 'Bearer $apiKey'}, headers: header,
body: jsonEncode(document), body: jsonEncode(document),
); );
if (response.statusCode != 200) { if (response.statusCode != 200) {
@@ -125,7 +182,7 @@ Future<List<Note>> searchNotes(String query) async {
); );
final response = await http.post( final response = await http.post(
Uri.parse('$endpoint/indexes/$noteIndex/search'), Uri.parse('$endpoint/indexes/$noteIndex/search'),
headers: {'Authorization': 'Bearer $apiKey'}, headers: header,
body: jsonEncode(searchCondition.toJson()), body: jsonEncode(searchCondition.toJson()),
); );
if (response.statusCode != 200) { if (response.statusCode != 200) {
@@ -135,6 +192,7 @@ Future<List<Note>> searchNotes(String query) async {
return responseJson.hits return responseJson.hits
.map( .map(
(hit) => Note( (hit) => Note(
id: hit['id'] as String,
date: hit['date'] as String, date: hit['date'] as String,
content: hit['content'] as String, content: hit['content'] as String,
), ),
@@ -151,7 +209,7 @@ Future<Note?> getPreviousTo(String date) async {
); );
final response = await http.post( final response = await http.post(
Uri.parse('$endpoint/indexes/$noteIndex/search'), Uri.parse('$endpoint/indexes/$noteIndex/search'),
headers: {'Authorization': 'Bearer $apiKey'}, headers: header,
body: jsonEncode(searchCondition.toJson()), body: jsonEncode(searchCondition.toJson()),
); );
if (response.statusCode != 200) { if (response.statusCode != 200) {
@@ -162,6 +220,7 @@ Future<Note?> getPreviousTo(String date) async {
return null; return null;
} }
return Note( return Note(
id: responseJson.hits.first['id'] as String,
date: responseJson.hits.first['date'] as String, date: responseJson.hits.first['date'] as String,
content: responseJson.hits.first['content'] as String, content: responseJson.hits.first['content'] as String,
); );
@@ -176,7 +235,7 @@ Future<Note?> getNextTo(String date) async {
); );
final response = await http.post( final response = await http.post(
Uri.parse('$endpoint/indexes/$noteIndex/search'), Uri.parse('$endpoint/indexes/$noteIndex/search'),
headers: {'Authorization': 'Bearer $apiKey'}, headers: header,
body: jsonEncode(searchCondition.toJson()), body: jsonEncode(searchCondition.toJson()),
); );
if (response.statusCode != 200) { if (response.statusCode != 200) {
@@ -187,6 +246,7 @@ Future<Note?> getNextTo(String date) async {
return null; return null;
} }
return Note( return Note(
id: responseJson.hits.first['id'] as String,
date: responseJson.hits.first['date'] as String, date: responseJson.hits.first['date'] as String,
content: responseJson.hits.first['content'] as String, content: responseJson.hits.first['content'] as String,
); );
@@ -195,12 +255,12 @@ Future<Note?> getNextTo(String date) async {
Future<Note?> getLatest() async { Future<Note?> getLatest() async {
final searchCondition = MeilisearchQuery( final searchCondition = MeilisearchQuery(
q: '', q: '',
sort: ['date DESC'], sort: ['date:desc'],
limit: 1, limit: 1,
); );
final response = await http.post( final response = await http.post(
Uri.parse('$endpoint/indexes/$noteIndex/search'), Uri.parse('$endpoint/indexes/$noteIndex/search'),
headers: {'Authorization': 'Bearer $apiKey'}, headers: header,
body: jsonEncode(searchCondition.toJson()), body: jsonEncode(searchCondition.toJson()),
); );
if (response.statusCode != 200) { if (response.statusCode != 200) {
@@ -211,11 +271,23 @@ Future<Note?> getLatest() async {
return null; return null;
} }
return Note( return Note(
id: responseJson.hits.first['id'] as String,
date: responseJson.hits.first['date'] as String, date: responseJson.hits.first['date'] as String,
content: responseJson.hits.first['content'] as String, content: responseJson.hits.first['content'] as String,
); );
} }
String generateRandomString(int length) {
const characters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var result = '';
for (var i = 0; i < length; i++) {
final randomIndex = Random().nextInt(characters.length);
result += characters[randomIndex];
}
return result;
}
Future<Note> createNote(String content) async { Future<Note> createNote(String content) async {
final lines = content.split('\n'); final lines = content.split('\n');
final trimmedLines = <String>[]; final trimmedLines = <String>[];
@@ -238,20 +310,22 @@ Future<Note> createNote(String content) async {
final mostFrequentLetterCount = letterFrequency[mostFrequentLetter]; final mostFrequentLetterCount = letterFrequency[mostFrequentLetter];
final document = { final document = {
'id': DateTime.now().toIso8601String(), 'id': generateRandomString(32),
'date': DateTime.now().toIso8601String(),
'content': content, 'content': content,
'topLetter': mostFrequentLetter, 'topLetter': mostFrequentLetter,
'topLetterFrequency': mostFrequentLetterCount, 'topLetterFrequency': mostFrequentLetterCount,
}; };
final response = await http.post( final response = await http.post(
Uri.parse('$endpoint/indexes/$noteIndex/documents'), Uri.parse('$endpoint/indexes/$noteIndex/documents'),
headers: {'Authorization': 'Bearer $apiKey'}, headers: header,
body: jsonEncode(document), body: jsonEncode(document),
); );
if (response.statusCode != 200) { if (response.statusCode != 200) {
throw Exception('Failed to create note'); throw Exception('Failed to create note');
} }
return Note( return Note(
id: document['id'] as String,
date: document['id'] as String, date: document['id'] as String,
content: document['content'] as String, content: document['content'] as String,
); );
@@ -264,7 +338,7 @@ Future<List<Note>> getProblematic({double threshold = 0.7}) async {
); );
final response = await http.post( final response = await http.post(
Uri.parse('$endpoint/indexes/$noteIndex/search'), Uri.parse('$endpoint/indexes/$noteIndex/search'),
headers: {'Authorization': 'Bearer $apiKey'}, headers: header,
body: jsonEncode(searchCondition.toJson()), body: jsonEncode(searchCondition.toJson()),
); );
if (response.statusCode != 200) { if (response.statusCode != 200) {
@@ -274,6 +348,7 @@ Future<List<Note>> getProblematic({double threshold = 0.7}) async {
return responseJson.hits return responseJson.hits
.map( .map(
(hit) => Note( (hit) => Note(
id: hit['id'] as String,
date: hit['date'] as String, date: hit['date'] as String,
content: hit['content'] as String, content: hit['content'] as String,
isProblematic: true, isProblematic: true,
@@ -285,10 +360,11 @@ Future<List<Note>> getProblematic({double threshold = 0.7}) async {
} }
Future<void> updateNote(String id, String content) async { Future<void> updateNote(String id, String content) async {
// TODO: Trim and calculate frequency
final document = {'id': id, 'content': content}; final document = {'id': id, 'content': content};
final response = await http.post( final response = await http.post(
Uri.parse('$endpoint/indexes/$noteIndex/documents'), Uri.parse('$endpoint/indexes/$noteIndex/documents'),
headers: {'Authorization': 'Bearer $apiKey'}, headers: header,
body: jsonEncode(document), body: jsonEncode(document),
); );
if (response.statusCode != 200) { if (response.statusCode != 200) {
@@ -299,7 +375,7 @@ Future<void> updateNote(String id, String content) async {
Future<void> deleteNote(String id) async { Future<void> deleteNote(String id) async {
final response = await http.delete( final response = await http.delete(
Uri.parse('$endpoint/indexes/$noteIndex/documents/$id'), Uri.parse('$endpoint/indexes/$noteIndex/documents/$id'),
headers: {'Authorization': 'Bearer $apiKey'}, headers: header,
); );
if (response.statusCode != 200) { if (response.statusCode != 200) {
throw Exception('Failed to delete note'); throw Exception('Failed to delete note');
@@ -314,7 +390,7 @@ Future<Scratch?> getLatestScratch() async {
); );
final response = await http.post( final response = await http.post(
Uri.parse('$endpoint/indexes/$scratchIndex/search'), Uri.parse('$endpoint/indexes/$scratchIndex/search'),
headers: {'Authorization': 'Bearer $apiKey'}, headers: header,
body: jsonEncode(searchCondition.toJson()), body: jsonEncode(searchCondition.toJson()),
); );
if (response.statusCode != 200) { if (response.statusCode != 200) {
@@ -334,7 +410,7 @@ Future<void> createScratch(String content) async {
final document = {'id': DateTime.now().toIso8601String(), 'content': content}; final document = {'id': DateTime.now().toIso8601String(), 'content': content};
final response = await http.post( final response = await http.post(
Uri.parse('$endpoint/indexes/$scratchIndex/documents'), Uri.parse('$endpoint/indexes/$scratchIndex/documents'),
headers: {'Authorization': 'Bearer $apiKey'}, headers: header,
body: jsonEncode(document), body: jsonEncode(document),
); );
if (response.statusCode != 200) { if (response.statusCode != 200) {

View File

@@ -1,6 +1,7 @@
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
class Note { class Note {
final String id;
final String date; final String date;
late final String displayDate; late final String displayDate;
String content; String content;
@@ -9,6 +10,7 @@ class Note {
String problemReason; String problemReason;
Note({ Note({
required this.id,
required this.date, required this.date,
required this.content, required this.content,
this.snippet, this.snippet,