diff --git a/lib/data/db/database.dart b/lib/data/db/database.dart index dc18862..0f569d4 100644 --- a/lib/data/db/database.dart +++ b/lib/data/db/database.dart @@ -6,8 +6,40 @@ import 'package:drift/native.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; +import 'package:sharedinbox/core/models/email.dart'; + part 'database.g.dart'; +// ── TypeConverters ──────────────────────────────────────────────────────────── + +class EmailAddressListConverter + extends TypeConverter, String> { + const EmailAddressListConverter(); + + @override + List fromSql(String fromDb) { + final list = jsonDecode(fromDb) as List; + return list + .map((e) => EmailAddress.fromJson(e as Map)) + .toList(); + } + + @override + String toSql(List value) => + jsonEncode(value.map((e) => e.toJson()).toList()); +} + +class StringListConverter extends TypeConverter, String> { + const StringListConverter(); + + @override + List fromSql(String fromDb) => + List.from(jsonDecode(fromDb) as List); + + @override + String toSql(List value) => jsonEncode(value); +} + // ── Tables ──────────────────────────────────────────────────────────────────── class Accounts extends Table { @@ -123,11 +155,14 @@ class Threads extends Table { IntColumn get messageCount => integer().withDefault(const Constant(1))(); BoolColumn get hasUnread => boolean().withDefault(const Constant(false))(); BoolColumn get isFlagged => boolean().withDefault(const Constant(false))(); - // JSON-encoded List<{name,email}> - TextColumn get participantsJson => text().withDefault(const Constant('[]'))(); + TextColumn get participantsJson => text() + .withDefault(const Constant('[]')) + .map(const EmailAddressListConverter())(); TextColumn get preview => text().nullable()(); TextColumn get latestEmailId => text()(); - TextColumn get emailIdsJson => text().withDefault(const Constant('[]'))(); + TextColumn get emailIdsJson => text() + .withDefault(const Constant('[]')) + .map(const StringListConverter())(); @override Set get primaryKey => {accountId, mailboxPath, id}; @@ -411,10 +446,18 @@ class AppDatabase extends _$AppDatabase { preview: Value(latest.preview), latestEmailId: latest.id, emailIdsJson: Value( - jsonEncode(threadEmails.map((e) => e.id).toList()), + threadEmails.map((e) => e.id).toList(), ), participantsJson: Value( - latest.fromJson, + (jsonDecode(latest.fromJson) as List) + .map( + (e) => EmailAddress( + name: + (e as Map)['name'] as String?, + email: e['email'] as String, + ), + ) + .toList(), ), // Good enough for migration ), ); diff --git a/lib/data/repositories/email_repository_impl.dart b/lib/data/repositories/email_repository_impl.dart index 09b6ed1..c0ab8f0 100644 --- a/lib/data/repositories/email_repository_impl.dart +++ b/lib/data/repositories/email_repository_impl.dart @@ -92,18 +92,6 @@ class EmailRepositoryImpl implements EmailRepository { } model.EmailThread _threadRowToModel(ThreadRow row) { - List parseAddresses(String json) { - final list = jsonDecode(json) as List; - return list - .map( - (e) => model.EmailAddress( - name: (e as Map)['name'] as String?, - email: e['email'] as String, - ), - ) - .toList(); - } - return model.EmailThread( threadId: row.id, accountId: row.accountId, @@ -113,10 +101,10 @@ class EmailRepositoryImpl implements EmailRepository { messageCount: row.messageCount, hasUnread: row.hasUnread, isFlagged: row.isFlagged, - participants: parseAddresses(row.participantsJson), + participants: row.participantsJson, preview: row.preview, latestEmailId: row.latestEmailId, - emailIds: List.from(jsonDecode(row.emailIdsJson) as List), + emailIds: row.emailIdsJson, ); } @@ -156,13 +144,11 @@ class EmailRepositoryImpl implements EmailRepository { // Collect unique participants across the whole thread. final seen = {}; - final participants = >[]; + final participants = []; for (final e in threadEmails) { - final from = jsonDecode(e.fromJson) as List; - for (final a in from.cast>()) { - final email = a['email'] as String; - if (seen.add(email)) { - participants.add({'name': a['name'], 'email': email}); + for (final a in _parseAddresses(e.fromJson)) { + if (seen.add(a.email)) { + participants.add(a); } } } @@ -177,12 +163,10 @@ class EmailRepositoryImpl implements EmailRepository { messageCount: Value(threadEmails.length), hasUnread: Value(threadEmails.any((e) => !e.isSeen)), isFlagged: Value(threadEmails.any((e) => e.isFlagged)), - participantsJson: Value(jsonEncode(participants)), + participantsJson: Value(participants), preview: Value(latest.preview), latestEmailId: latest.id, - emailIdsJson: Value( - jsonEncode(threadEmails.map((e) => e.id).toList()), - ), + emailIdsJson: Value(threadEmails.map((e) => e.id).toList()), ), ); } @@ -2708,18 +2692,6 @@ class EmailRepositoryImpl implements EmailRepository { } model.Email _toModel(Email row) { - List parseAddresses(String json) { - final list = jsonDecode(json) as List; - return list - .map( - (e) => model.EmailAddress( - name: (e as Map)['name'] as String?, - email: e['email'] as String, - ), - ) - .toList(); - } - return model.Email( id: row.id, accountId: row.accountId, @@ -2728,9 +2700,9 @@ class EmailRepositoryImpl implements EmailRepository { subject: row.subject, sentAt: row.sentAt, receivedAt: row.receivedAt, - from: parseAddresses(row.fromJson), - to: parseAddresses(row.toAddresses), - cc: parseAddresses(row.ccJson), + from: _parseAddresses(row.fromJson), + to: _parseAddresses(row.toAddresses), + cc: _parseAddresses(row.ccJson), preview: row.preview, isSeen: row.isSeen, isFlagged: row.isFlagged, @@ -2766,6 +2738,18 @@ class EmailRepositoryImpl implements EmailRepository { } } + List _parseAddresses(String json) { + final list = jsonDecode(json) as List; + return list + .map( + (e) => model.EmailAddress( + name: (e as Map)['name'] as String?, + email: e['email'] as String, + ), + ) + .toList(); + } + List _parseAttachments(String json) { final list = jsonDecode(json) as List; return list diff --git a/test/unit/email_repository_impl_test.dart b/test/unit/email_repository_impl_test.dart index 9fa472b..a7f5ea4 100644 --- a/test/unit/email_repository_impl_test.dart +++ b/test/unit/email_repository_impl_test.dart @@ -332,7 +332,7 @@ void main() { messageCount: const Value(2), hasUnread: const Value(true), latestEmailId: 'acc-1:2', - emailIdsJson: const Value('["acc-1:1", "acc-1:2"]'), + emailIdsJson: const Value(['acc-1:1', 'acc-1:2']), ), );