adding packages

This commit is contained in:
2026-01-15 14:38:46 -08:00
parent ef86e3ab6a
commit 6869cf47e5
5253 changed files with 726695 additions and 34 deletions
@@ -0,0 +1,111 @@
import 'dart:convert';
import 'package:drift/drift.dart';
import '../sync_repository.dart';
import 'schema.dart'; // Will export schema.g.dart artifacts via internal generation users
/// A Drift (SQLite) implementation of SyncRepository.
/// Requires a [SyncDatabase] interface which exposes the underlying tables.
class DriftSyncRepository<T> implements SyncRepository<T> {
final SyncDatabase dbWrapper;
final String collectionName;
final Map<String, dynamic> Function(T) toJson;
final T Function(Map<String, dynamic>) fromJson;
DriftSyncRepository({
required this.dbWrapper,
required this.collectionName,
required this.toJson,
required this.fromJson,
});
GeneratedDatabase get db => dbWrapper.db;
@override
Future<List<SyncRecord<T>>> getAll() async {
// Explicitly type the select to ensure 't' is 'SyncEntries'
final query = db.select<SyncEntries, SyncEntry>(dbWrapper.syncEntries)
..where((t) => t.collection.equals(collectionName));
final rows = await query.get();
return rows.map((row) {
return SyncRecord<T>(
id: row.id,
data: fromJson(jsonDecode(row.data)),
baseData:
row.baseData != null ? fromJson(jsonDecode(row.baseData!)) : null,
serverUpdatedAt: row.serverUpdatedAt,
isDirty: row.isDirty,
isDeleted: row.isDeleted,
deletedAt: row.deletedAt,
);
}).toList();
}
@override
Future<SyncRecord<T>?> get(String id) async {
final query = db.select<SyncEntries, SyncEntry>(dbWrapper.syncEntries)
..where((t) => t.collection.equals(collectionName) & t.id.equals(id));
final row = await query.getSingleOrNull();
if (row == null) return null;
return SyncRecord<T>(
id: row.id,
data: fromJson(jsonDecode(row.data)),
baseData:
row.baseData != null ? fromJson(jsonDecode(row.baseData!)) : null,
serverUpdatedAt: row.serverUpdatedAt,
isDirty: row.isDirty,
isDeleted: row.isDeleted,
deletedAt: row.deletedAt,
);
}
@override
Future<void> save(SyncRecord<T> record) async {
// Note: SyncEntriesCompanion is generated in schema.g.dart
await db.into(dbWrapper.syncEntries).insertOnConflictUpdate(
SyncEntriesCompanion(
collection: Value(collectionName),
id: Value(record.id),
data: Value(jsonEncode(toJson(record.data))),
baseData: Value(
record.baseData != null
? jsonEncode(toJson(record.baseData as T))
: null,
),
serverUpdatedAt: Value(record.serverUpdatedAt),
isDirty: Value(record.isDirty),
isDeleted: Value(record.isDeleted),
deletedAt: Value(record.deletedAt),
),
);
}
@override
Future<void> delete(String id) async {
await (db.delete<SyncEntries, SyncEntry>(dbWrapper.syncEntries)
..where((t) => t.collection.equals(collectionName) & t.id.equals(id)))
.go();
}
@override
Future<DateTime> getLastSyncTime() async {
final query = db.select<SyncMeta, SyncMetaData>(dbWrapper.syncMeta)
..where((t) => t.collection.equals(collectionName));
final row = await query.getSingleOrNull();
return row?.lastSync ?? DateTime.fromMillisecondsSinceEpoch(0);
}
@override
Future<void> setLastSyncTime(DateTime time) async {
await db.into(dbWrapper.syncMeta).insertOnConflictUpdate(
SyncMetaCompanion(
collection: Value(collectionName),
lastSync: Value(time),
),
);
}
}
@@ -0,0 +1,55 @@
import 'package:drift/drift.dart';
part 'schema.g.dart';
/// Stores the actual data records + sync metadata
/// We use a composite primary key of (collection, id) to store multiple collections in one table
class SyncEntries extends Table {
TextColumn get collection => text()();
TextColumn get id => text()();
/// serialized JSON of the current local data
TextColumn get data => text()();
/// serialized JSON of the base data (snapshot)
TextColumn get baseData => text().nullable()();
DateTimeColumn get serverUpdatedAt => dateTime().nullable()();
BoolColumn get isDirty => boolean().withDefault(const Constant(false))();
BoolColumn get isDeleted => boolean().withDefault(const Constant(false))();
DateTimeColumn get deletedAt => dateTime().nullable()();
@override
Set<Column> get primaryKey => {collection, id};
}
/// Stores global sync state per collection (e.g. last sync time)
class SyncMeta extends Table {
TextColumn get collection => text()();
DateTimeColumn get lastSync => dateTime().withDefault(
Constant(DateTime.fromMillisecondsSinceEpoch(0)),
)();
@override
Set<Column> get primaryKey => {collection};
}
/// Dummy database class to trigger generation of Companions and Data classes
/// which are used by DriftSyncRepository.
@DriftDatabase(tables: [SyncEntries, SyncMeta])
class PackageDatabase extends _$PackageDatabase implements SyncDatabase {
PackageDatabase(super.e);
@override
int get schemaVersion => 1;
@override
GeneratedDatabase get db => this;
}
/// Interface that your AppDatabase must implement to be used with DriftSyncRepository.
/// This allows the repository to be agnostic of the specific generated database class.
abstract class SyncDatabase {
GeneratedDatabase get db;
TableInfo<SyncEntries, SyncEntry> get syncEntries;
TableInfo<SyncMeta, SyncMetaData> get syncMeta;
}
File diff suppressed because it is too large Load Diff