feat(offline): tier 2 phase 3 — write queue with optimistic UI
Offline writes now queue + apply optimistically instead of throwing. On reconnect, the queue drains automatically; conflicts and rejections surface as one-time SnackBars so the user knows their edit didn't land. - Drift schema v3: `pending_writes` table (verb + payload + baseline + tries + last_error). Negative ids serve as cache placeholders for offline-created rows until replay assigns the server id. - Each write repository (notes, tasks, projects, milestones, events) now catches NetworkException, applies the change to cache (with a fresh `nextTempId()` for creates), and enqueues the API call. - Edits to a still-queued offline-created row coalesce into the original create payload — only one server call per row, in order. - Deletes of still-queued offline-created rows drop the queue entry and the cache row; no server call ever happens. - New WriteQueue service drains the queue oldest-first. Server-wins on `updated_at` baseline check (notes/tasks/projects); last-writer-wins for milestones/events (no getOne available). 4xx → drop + surface as `rejected`; conflict → drop + `overwritten`; 404 on update → drop + `missing`; 5xx/network → keep + retry next online cycle. - Replay fires on AuthStatus → authenticated transitions (cold-start, came-back-online, login). - OfflineBanner shows "Retry (N)" with the pending-writes count. - Distinct ServerException added so 4xx no longer masquerade as NetworkException — the queue can drop them instead of looping. flutter analyze clean; 21 tests pass (3 new for QueueFailure messaging). Phase 4 (read-only UI indicators on cached/temp rows) still ahead. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -136,6 +136,29 @@ class SyncMetadata extends Table {
|
||||
Set<Column> get primaryKey => {domain};
|
||||
}
|
||||
|
||||
/// Phase 3 — generic offline-write queue. One row per pending API call;
|
||||
/// drained in oldest-first order when the device comes back online.
|
||||
///
|
||||
/// `targetId` is the server id for updates/deletes; `tempId` is the
|
||||
/// client-allocated negative placeholder for offline creates. `payloadJson`
|
||||
/// is the typed args the API method needs (verb-specific shape).
|
||||
/// `baselineUpdatedAt` snapshots the cached row's `updated_at` at edit
|
||||
/// time so the replayer can drop the op if the server has moved on
|
||||
/// (server-wins conflict resolution).
|
||||
class PendingWrites extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
TextColumn get domain => text()();
|
||||
TextColumn get verb => text()();
|
||||
IntColumn get targetId => integer().nullable()();
|
||||
IntColumn get tempId => integer().nullable()();
|
||||
TextColumn get payloadJson => text().withDefault(const Constant('{}'))();
|
||||
DateTimeColumn get baselineUpdatedAt => dateTime().nullable()();
|
||||
DateTimeColumn get createdAt =>
|
||||
dateTime().clientDefault(() => DateTime.now())();
|
||||
IntColumn get tries => integer().withDefault(const Constant(0))();
|
||||
TextColumn get lastError => text().nullable()();
|
||||
}
|
||||
|
||||
const String kSyncDomainNotes = 'notes';
|
||||
const String kSyncDomainTasks = 'tasks';
|
||||
const String kSyncDomainProjects = 'projects';
|
||||
@@ -143,6 +166,10 @@ const String kSyncDomainMilestones = 'milestones';
|
||||
const String kSyncDomainEvents = 'events';
|
||||
const String kSyncDomainConversations = 'conversations';
|
||||
|
||||
const String kWriteVerbCreate = 'create';
|
||||
const String kWriteVerbUpdate = 'update';
|
||||
const String kWriteVerbDelete = 'delete';
|
||||
|
||||
@DriftDatabase(tables: [
|
||||
CachedNotes,
|
||||
CachedTasks,
|
||||
@@ -151,13 +178,14 @@ const String kSyncDomainConversations = 'conversations';
|
||||
CachedCalendarEvents,
|
||||
CachedConversations,
|
||||
SyncMetadata,
|
||||
PendingWrites,
|
||||
])
|
||||
class FabledDatabase extends _$FabledDatabase {
|
||||
FabledDatabase() : super(_openConnection());
|
||||
FabledDatabase.forTesting(super.executor);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 2;
|
||||
int get schemaVersion => 3;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration => MigrationStrategy(
|
||||
@@ -171,6 +199,10 @@ class FabledDatabase extends _$FabledDatabase {
|
||||
await m.createTable(cachedCalendarEvents);
|
||||
await m.createTable(cachedConversations);
|
||||
}
|
||||
// v2 → v3: added the offline write queue.
|
||||
if (from < 3) {
|
||||
await m.createTable(pendingWrites);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -448,6 +480,99 @@ class FabledDatabase extends _$FabledDatabase {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ── Pending writes (Phase 3 offline queue) ───────────────────────────────
|
||||
|
||||
/// Allocate a fresh negative id used as a placeholder for an
|
||||
/// offline-created row until its server id arrives via replay.
|
||||
Future<int> nextTempId() async {
|
||||
final query = customSelect(
|
||||
'SELECT MIN(temp_id) AS m FROM pending_writes WHERE temp_id IS NOT NULL',
|
||||
);
|
||||
final row = await query.getSingleOrNull();
|
||||
final current = row?.read<int?>('m');
|
||||
return (current ?? 0) - 1;
|
||||
}
|
||||
|
||||
Future<int> enqueuePending({
|
||||
required String domain,
|
||||
required String verb,
|
||||
int? targetId,
|
||||
int? tempId,
|
||||
Map<String, dynamic> payload = const {},
|
||||
DateTime? baselineUpdatedAt,
|
||||
}) {
|
||||
return into(pendingWrites).insert(
|
||||
PendingWritesCompanion.insert(
|
||||
domain: domain,
|
||||
verb: verb,
|
||||
targetId: Value(targetId),
|
||||
tempId: Value(tempId),
|
||||
payloadJson: Value(jsonEncode(payload)),
|
||||
baselineUpdatedAt: Value(baselineUpdatedAt),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Find the queued create-op for an offline-created row so a follow-up
|
||||
/// edit can be coalesced into the original payload (no separate update
|
||||
/// op gets queued). Returns null if no matching create is queued.
|
||||
Future<PendingWrite?> findQueuedCreate(String domain, int tempId) {
|
||||
return (select(pendingWrites)
|
||||
..where((t) =>
|
||||
t.domain.equals(domain) &
|
||||
t.verb.equals(kWriteVerbCreate) &
|
||||
t.tempId.equals(tempId)))
|
||||
.getSingleOrNull();
|
||||
}
|
||||
|
||||
Future<void> updatePendingPayload(int opId, Map<String, dynamic> payload) {
|
||||
return (update(pendingWrites)..where((t) => t.id.equals(opId))).write(
|
||||
PendingWritesCompanion(payloadJson: Value(jsonEncode(payload))),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> deletePending(int opId) {
|
||||
return (delete(pendingWrites)..where((t) => t.id.equals(opId))).go();
|
||||
}
|
||||
|
||||
/// Pending ops oldest first — replay order.
|
||||
Future<List<PendingWrite>> listPending() {
|
||||
return (select(pendingWrites)
|
||||
..orderBy([(t) => OrderingTerm.asc(t.id)]))
|
||||
.get();
|
||||
}
|
||||
|
||||
Future<int> queueDepth() async {
|
||||
final row = await customSelect('SELECT COUNT(*) AS c FROM pending_writes')
|
||||
.getSingle();
|
||||
return row.read<int>('c');
|
||||
}
|
||||
|
||||
Future<void> markPendingFailed(int opId, String error) {
|
||||
return (update(pendingWrites)..where((t) => t.id.equals(opId))).write(
|
||||
PendingWritesCompanion(
|
||||
tries: const Value.absent(),
|
||||
lastError: Value(error),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> incrementPendingTries(int opId) {
|
||||
return customStatement(
|
||||
'UPDATE pending_writes SET tries = tries + 1 WHERE id = ?',
|
||||
[opId],
|
||||
);
|
||||
}
|
||||
|
||||
/// Watch queue depth — used by OfflineBanner for the "Retry (N)" affordance.
|
||||
Stream<int> watchQueueDepth() {
|
||||
final query = customSelect(
|
||||
'SELECT COUNT(*) AS c FROM pending_writes',
|
||||
readsFrom: {pendingWrites},
|
||||
);
|
||||
return query.watchSingle().map((row) => row.read<int>('c'));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Row ↔ model converters ──────────────────────────────────────────────────
|
||||
|
||||
@@ -3831,6 +3831,609 @@ class SyncMetadataCompanion extends UpdateCompanion<SyncMetadataData> {
|
||||
}
|
||||
}
|
||||
|
||||
class $PendingWritesTable extends PendingWrites
|
||||
with TableInfo<$PendingWritesTable, PendingWrite> {
|
||||
@override
|
||||
final GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
$PendingWritesTable(this.attachedDatabase, [this._alias]);
|
||||
static const VerificationMeta _idMeta = const VerificationMeta('id');
|
||||
@override
|
||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
||||
'id',
|
||||
aliasedName,
|
||||
false,
|
||||
hasAutoIncrement: true,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'PRIMARY KEY AUTOINCREMENT',
|
||||
),
|
||||
);
|
||||
static const VerificationMeta _domainMeta = const VerificationMeta('domain');
|
||||
@override
|
||||
late final GeneratedColumn<String> domain = GeneratedColumn<String>(
|
||||
'domain',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
static const VerificationMeta _verbMeta = const VerificationMeta('verb');
|
||||
@override
|
||||
late final GeneratedColumn<String> verb = GeneratedColumn<String>(
|
||||
'verb',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
static const VerificationMeta _targetIdMeta = const VerificationMeta(
|
||||
'targetId',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<int> targetId = GeneratedColumn<int>(
|
||||
'target_id',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const VerificationMeta _tempIdMeta = const VerificationMeta('tempId');
|
||||
@override
|
||||
late final GeneratedColumn<int> tempId = GeneratedColumn<int>(
|
||||
'temp_id',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const VerificationMeta _payloadJsonMeta = const VerificationMeta(
|
||||
'payloadJson',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<String> payloadJson = GeneratedColumn<String>(
|
||||
'payload_json',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: const Constant('{}'),
|
||||
);
|
||||
static const VerificationMeta _baselineUpdatedAtMeta = const VerificationMeta(
|
||||
'baselineUpdatedAt',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<DateTime> baselineUpdatedAt =
|
||||
GeneratedColumn<DateTime>(
|
||||
'baseline_updated_at',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const VerificationMeta _createdAtMeta = const VerificationMeta(
|
||||
'createdAt',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
|
||||
'created_at',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
clientDefault: () => DateTime.now(),
|
||||
);
|
||||
static const VerificationMeta _triesMeta = const VerificationMeta('tries');
|
||||
@override
|
||||
late final GeneratedColumn<int> tries = GeneratedColumn<int>(
|
||||
'tries',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: const Constant(0),
|
||||
);
|
||||
static const VerificationMeta _lastErrorMeta = const VerificationMeta(
|
||||
'lastError',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<String> lastError = GeneratedColumn<String>(
|
||||
'last_error',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [
|
||||
id,
|
||||
domain,
|
||||
verb,
|
||||
targetId,
|
||||
tempId,
|
||||
payloadJson,
|
||||
baselineUpdatedAt,
|
||||
createdAt,
|
||||
tries,
|
||||
lastError,
|
||||
];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'pending_writes';
|
||||
@override
|
||||
VerificationContext validateIntegrity(
|
||||
Insertable<PendingWrite> instance, {
|
||||
bool isInserting = false,
|
||||
}) {
|
||||
final context = VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('id')) {
|
||||
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
|
||||
}
|
||||
if (data.containsKey('domain')) {
|
||||
context.handle(
|
||||
_domainMeta,
|
||||
domain.isAcceptableOrUnknown(data['domain']!, _domainMeta),
|
||||
);
|
||||
} else if (isInserting) {
|
||||
context.missing(_domainMeta);
|
||||
}
|
||||
if (data.containsKey('verb')) {
|
||||
context.handle(
|
||||
_verbMeta,
|
||||
verb.isAcceptableOrUnknown(data['verb']!, _verbMeta),
|
||||
);
|
||||
} else if (isInserting) {
|
||||
context.missing(_verbMeta);
|
||||
}
|
||||
if (data.containsKey('target_id')) {
|
||||
context.handle(
|
||||
_targetIdMeta,
|
||||
targetId.isAcceptableOrUnknown(data['target_id']!, _targetIdMeta),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('temp_id')) {
|
||||
context.handle(
|
||||
_tempIdMeta,
|
||||
tempId.isAcceptableOrUnknown(data['temp_id']!, _tempIdMeta),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('payload_json')) {
|
||||
context.handle(
|
||||
_payloadJsonMeta,
|
||||
payloadJson.isAcceptableOrUnknown(
|
||||
data['payload_json']!,
|
||||
_payloadJsonMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('baseline_updated_at')) {
|
||||
context.handle(
|
||||
_baselineUpdatedAtMeta,
|
||||
baselineUpdatedAt.isAcceptableOrUnknown(
|
||||
data['baseline_updated_at']!,
|
||||
_baselineUpdatedAtMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('created_at')) {
|
||||
context.handle(
|
||||
_createdAtMeta,
|
||||
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('tries')) {
|
||||
context.handle(
|
||||
_triesMeta,
|
||||
tries.isAcceptableOrUnknown(data['tries']!, _triesMeta),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('last_error')) {
|
||||
context.handle(
|
||||
_lastErrorMeta,
|
||||
lastError.isAcceptableOrUnknown(data['last_error']!, _lastErrorMeta),
|
||||
);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<GeneratedColumn> get $primaryKey => {id};
|
||||
@override
|
||||
PendingWrite map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return PendingWrite(
|
||||
id: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}id'],
|
||||
)!,
|
||||
domain: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}domain'],
|
||||
)!,
|
||||
verb: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}verb'],
|
||||
)!,
|
||||
targetId: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}target_id'],
|
||||
),
|
||||
tempId: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}temp_id'],
|
||||
),
|
||||
payloadJson: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}payload_json'],
|
||||
)!,
|
||||
baselineUpdatedAt: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}baseline_updated_at'],
|
||||
),
|
||||
createdAt: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}created_at'],
|
||||
)!,
|
||||
tries: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}tries'],
|
||||
)!,
|
||||
lastError: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}last_error'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
$PendingWritesTable createAlias(String alias) {
|
||||
return $PendingWritesTable(attachedDatabase, alias);
|
||||
}
|
||||
}
|
||||
|
||||
class PendingWrite extends DataClass implements Insertable<PendingWrite> {
|
||||
final int id;
|
||||
final String domain;
|
||||
final String verb;
|
||||
final int? targetId;
|
||||
final int? tempId;
|
||||
final String payloadJson;
|
||||
final DateTime? baselineUpdatedAt;
|
||||
final DateTime createdAt;
|
||||
final int tries;
|
||||
final String? lastError;
|
||||
const PendingWrite({
|
||||
required this.id,
|
||||
required this.domain,
|
||||
required this.verb,
|
||||
this.targetId,
|
||||
this.tempId,
|
||||
required this.payloadJson,
|
||||
this.baselineUpdatedAt,
|
||||
required this.createdAt,
|
||||
required this.tries,
|
||||
this.lastError,
|
||||
});
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
map['id'] = Variable<int>(id);
|
||||
map['domain'] = Variable<String>(domain);
|
||||
map['verb'] = Variable<String>(verb);
|
||||
if (!nullToAbsent || targetId != null) {
|
||||
map['target_id'] = Variable<int>(targetId);
|
||||
}
|
||||
if (!nullToAbsent || tempId != null) {
|
||||
map['temp_id'] = Variable<int>(tempId);
|
||||
}
|
||||
map['payload_json'] = Variable<String>(payloadJson);
|
||||
if (!nullToAbsent || baselineUpdatedAt != null) {
|
||||
map['baseline_updated_at'] = Variable<DateTime>(baselineUpdatedAt);
|
||||
}
|
||||
map['created_at'] = Variable<DateTime>(createdAt);
|
||||
map['tries'] = Variable<int>(tries);
|
||||
if (!nullToAbsent || lastError != null) {
|
||||
map['last_error'] = Variable<String>(lastError);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
PendingWritesCompanion toCompanion(bool nullToAbsent) {
|
||||
return PendingWritesCompanion(
|
||||
id: Value(id),
|
||||
domain: Value(domain),
|
||||
verb: Value(verb),
|
||||
targetId: targetId == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(targetId),
|
||||
tempId: tempId == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(tempId),
|
||||
payloadJson: Value(payloadJson),
|
||||
baselineUpdatedAt: baselineUpdatedAt == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(baselineUpdatedAt),
|
||||
createdAt: Value(createdAt),
|
||||
tries: Value(tries),
|
||||
lastError: lastError == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(lastError),
|
||||
);
|
||||
}
|
||||
|
||||
factory PendingWrite.fromJson(
|
||||
Map<String, dynamic> json, {
|
||||
ValueSerializer? serializer,
|
||||
}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return PendingWrite(
|
||||
id: serializer.fromJson<int>(json['id']),
|
||||
domain: serializer.fromJson<String>(json['domain']),
|
||||
verb: serializer.fromJson<String>(json['verb']),
|
||||
targetId: serializer.fromJson<int?>(json['targetId']),
|
||||
tempId: serializer.fromJson<int?>(json['tempId']),
|
||||
payloadJson: serializer.fromJson<String>(json['payloadJson']),
|
||||
baselineUpdatedAt: serializer.fromJson<DateTime?>(
|
||||
json['baselineUpdatedAt'],
|
||||
),
|
||||
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
||||
tries: serializer.fromJson<int>(json['tries']),
|
||||
lastError: serializer.fromJson<String?>(json['lastError']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'id': serializer.toJson<int>(id),
|
||||
'domain': serializer.toJson<String>(domain),
|
||||
'verb': serializer.toJson<String>(verb),
|
||||
'targetId': serializer.toJson<int?>(targetId),
|
||||
'tempId': serializer.toJson<int?>(tempId),
|
||||
'payloadJson': serializer.toJson<String>(payloadJson),
|
||||
'baselineUpdatedAt': serializer.toJson<DateTime?>(baselineUpdatedAt),
|
||||
'createdAt': serializer.toJson<DateTime>(createdAt),
|
||||
'tries': serializer.toJson<int>(tries),
|
||||
'lastError': serializer.toJson<String?>(lastError),
|
||||
};
|
||||
}
|
||||
|
||||
PendingWrite copyWith({
|
||||
int? id,
|
||||
String? domain,
|
||||
String? verb,
|
||||
Value<int?> targetId = const Value.absent(),
|
||||
Value<int?> tempId = const Value.absent(),
|
||||
String? payloadJson,
|
||||
Value<DateTime?> baselineUpdatedAt = const Value.absent(),
|
||||
DateTime? createdAt,
|
||||
int? tries,
|
||||
Value<String?> lastError = const Value.absent(),
|
||||
}) => PendingWrite(
|
||||
id: id ?? this.id,
|
||||
domain: domain ?? this.domain,
|
||||
verb: verb ?? this.verb,
|
||||
targetId: targetId.present ? targetId.value : this.targetId,
|
||||
tempId: tempId.present ? tempId.value : this.tempId,
|
||||
payloadJson: payloadJson ?? this.payloadJson,
|
||||
baselineUpdatedAt: baselineUpdatedAt.present
|
||||
? baselineUpdatedAt.value
|
||||
: this.baselineUpdatedAt,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
tries: tries ?? this.tries,
|
||||
lastError: lastError.present ? lastError.value : this.lastError,
|
||||
);
|
||||
PendingWrite copyWithCompanion(PendingWritesCompanion data) {
|
||||
return PendingWrite(
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
domain: data.domain.present ? data.domain.value : this.domain,
|
||||
verb: data.verb.present ? data.verb.value : this.verb,
|
||||
targetId: data.targetId.present ? data.targetId.value : this.targetId,
|
||||
tempId: data.tempId.present ? data.tempId.value : this.tempId,
|
||||
payloadJson: data.payloadJson.present
|
||||
? data.payloadJson.value
|
||||
: this.payloadJson,
|
||||
baselineUpdatedAt: data.baselineUpdatedAt.present
|
||||
? data.baselineUpdatedAt.value
|
||||
: this.baselineUpdatedAt,
|
||||
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
|
||||
tries: data.tries.present ? data.tries.value : this.tries,
|
||||
lastError: data.lastError.present ? data.lastError.value : this.lastError,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('PendingWrite(')
|
||||
..write('id: $id, ')
|
||||
..write('domain: $domain, ')
|
||||
..write('verb: $verb, ')
|
||||
..write('targetId: $targetId, ')
|
||||
..write('tempId: $tempId, ')
|
||||
..write('payloadJson: $payloadJson, ')
|
||||
..write('baselineUpdatedAt: $baselineUpdatedAt, ')
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('tries: $tries, ')
|
||||
..write('lastError: $lastError')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
id,
|
||||
domain,
|
||||
verb,
|
||||
targetId,
|
||||
tempId,
|
||||
payloadJson,
|
||||
baselineUpdatedAt,
|
||||
createdAt,
|
||||
tries,
|
||||
lastError,
|
||||
);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is PendingWrite &&
|
||||
other.id == this.id &&
|
||||
other.domain == this.domain &&
|
||||
other.verb == this.verb &&
|
||||
other.targetId == this.targetId &&
|
||||
other.tempId == this.tempId &&
|
||||
other.payloadJson == this.payloadJson &&
|
||||
other.baselineUpdatedAt == this.baselineUpdatedAt &&
|
||||
other.createdAt == this.createdAt &&
|
||||
other.tries == this.tries &&
|
||||
other.lastError == this.lastError);
|
||||
}
|
||||
|
||||
class PendingWritesCompanion extends UpdateCompanion<PendingWrite> {
|
||||
final Value<int> id;
|
||||
final Value<String> domain;
|
||||
final Value<String> verb;
|
||||
final Value<int?> targetId;
|
||||
final Value<int?> tempId;
|
||||
final Value<String> payloadJson;
|
||||
final Value<DateTime?> baselineUpdatedAt;
|
||||
final Value<DateTime> createdAt;
|
||||
final Value<int> tries;
|
||||
final Value<String?> lastError;
|
||||
const PendingWritesCompanion({
|
||||
this.id = const Value.absent(),
|
||||
this.domain = const Value.absent(),
|
||||
this.verb = const Value.absent(),
|
||||
this.targetId = const Value.absent(),
|
||||
this.tempId = const Value.absent(),
|
||||
this.payloadJson = const Value.absent(),
|
||||
this.baselineUpdatedAt = const Value.absent(),
|
||||
this.createdAt = const Value.absent(),
|
||||
this.tries = const Value.absent(),
|
||||
this.lastError = const Value.absent(),
|
||||
});
|
||||
PendingWritesCompanion.insert({
|
||||
this.id = const Value.absent(),
|
||||
required String domain,
|
||||
required String verb,
|
||||
this.targetId = const Value.absent(),
|
||||
this.tempId = const Value.absent(),
|
||||
this.payloadJson = const Value.absent(),
|
||||
this.baselineUpdatedAt = const Value.absent(),
|
||||
this.createdAt = const Value.absent(),
|
||||
this.tries = const Value.absent(),
|
||||
this.lastError = const Value.absent(),
|
||||
}) : domain = Value(domain),
|
||||
verb = Value(verb);
|
||||
static Insertable<PendingWrite> custom({
|
||||
Expression<int>? id,
|
||||
Expression<String>? domain,
|
||||
Expression<String>? verb,
|
||||
Expression<int>? targetId,
|
||||
Expression<int>? tempId,
|
||||
Expression<String>? payloadJson,
|
||||
Expression<DateTime>? baselineUpdatedAt,
|
||||
Expression<DateTime>? createdAt,
|
||||
Expression<int>? tries,
|
||||
Expression<String>? lastError,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
if (id != null) 'id': id,
|
||||
if (domain != null) 'domain': domain,
|
||||
if (verb != null) 'verb': verb,
|
||||
if (targetId != null) 'target_id': targetId,
|
||||
if (tempId != null) 'temp_id': tempId,
|
||||
if (payloadJson != null) 'payload_json': payloadJson,
|
||||
if (baselineUpdatedAt != null) 'baseline_updated_at': baselineUpdatedAt,
|
||||
if (createdAt != null) 'created_at': createdAt,
|
||||
if (tries != null) 'tries': tries,
|
||||
if (lastError != null) 'last_error': lastError,
|
||||
});
|
||||
}
|
||||
|
||||
PendingWritesCompanion copyWith({
|
||||
Value<int>? id,
|
||||
Value<String>? domain,
|
||||
Value<String>? verb,
|
||||
Value<int?>? targetId,
|
||||
Value<int?>? tempId,
|
||||
Value<String>? payloadJson,
|
||||
Value<DateTime?>? baselineUpdatedAt,
|
||||
Value<DateTime>? createdAt,
|
||||
Value<int>? tries,
|
||||
Value<String?>? lastError,
|
||||
}) {
|
||||
return PendingWritesCompanion(
|
||||
id: id ?? this.id,
|
||||
domain: domain ?? this.domain,
|
||||
verb: verb ?? this.verb,
|
||||
targetId: targetId ?? this.targetId,
|
||||
tempId: tempId ?? this.tempId,
|
||||
payloadJson: payloadJson ?? this.payloadJson,
|
||||
baselineUpdatedAt: baselineUpdatedAt ?? this.baselineUpdatedAt,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
tries: tries ?? this.tries,
|
||||
lastError: lastError ?? this.lastError,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
if (id.present) {
|
||||
map['id'] = Variable<int>(id.value);
|
||||
}
|
||||
if (domain.present) {
|
||||
map['domain'] = Variable<String>(domain.value);
|
||||
}
|
||||
if (verb.present) {
|
||||
map['verb'] = Variable<String>(verb.value);
|
||||
}
|
||||
if (targetId.present) {
|
||||
map['target_id'] = Variable<int>(targetId.value);
|
||||
}
|
||||
if (tempId.present) {
|
||||
map['temp_id'] = Variable<int>(tempId.value);
|
||||
}
|
||||
if (payloadJson.present) {
|
||||
map['payload_json'] = Variable<String>(payloadJson.value);
|
||||
}
|
||||
if (baselineUpdatedAt.present) {
|
||||
map['baseline_updated_at'] = Variable<DateTime>(baselineUpdatedAt.value);
|
||||
}
|
||||
if (createdAt.present) {
|
||||
map['created_at'] = Variable<DateTime>(createdAt.value);
|
||||
}
|
||||
if (tries.present) {
|
||||
map['tries'] = Variable<int>(tries.value);
|
||||
}
|
||||
if (lastError.present) {
|
||||
map['last_error'] = Variable<String>(lastError.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('PendingWritesCompanion(')
|
||||
..write('id: $id, ')
|
||||
..write('domain: $domain, ')
|
||||
..write('verb: $verb, ')
|
||||
..write('targetId: $targetId, ')
|
||||
..write('tempId: $tempId, ')
|
||||
..write('payloadJson: $payloadJson, ')
|
||||
..write('baselineUpdatedAt: $baselineUpdatedAt, ')
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('tries: $tries, ')
|
||||
..write('lastError: $lastError')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _$FabledDatabase extends GeneratedDatabase {
|
||||
_$FabledDatabase(QueryExecutor e) : super(e);
|
||||
$FabledDatabaseManager get managers => $FabledDatabaseManager(this);
|
||||
@@ -3845,6 +4448,7 @@ abstract class _$FabledDatabase extends GeneratedDatabase {
|
||||
late final $CachedConversationsTable cachedConversations =
|
||||
$CachedConversationsTable(this);
|
||||
late final $SyncMetadataTable syncMetadata = $SyncMetadataTable(this);
|
||||
late final $PendingWritesTable pendingWrites = $PendingWritesTable(this);
|
||||
@override
|
||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
||||
@@ -3857,6 +4461,7 @@ abstract class _$FabledDatabase extends GeneratedDatabase {
|
||||
cachedCalendarEvents,
|
||||
cachedConversations,
|
||||
syncMetadata,
|
||||
pendingWrites,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -5830,6 +6435,301 @@ typedef $$SyncMetadataTableProcessedTableManager =
|
||||
SyncMetadataData,
|
||||
PrefetchHooks Function()
|
||||
>;
|
||||
typedef $$PendingWritesTableCreateCompanionBuilder =
|
||||
PendingWritesCompanion Function({
|
||||
Value<int> id,
|
||||
required String domain,
|
||||
required String verb,
|
||||
Value<int?> targetId,
|
||||
Value<int?> tempId,
|
||||
Value<String> payloadJson,
|
||||
Value<DateTime?> baselineUpdatedAt,
|
||||
Value<DateTime> createdAt,
|
||||
Value<int> tries,
|
||||
Value<String?> lastError,
|
||||
});
|
||||
typedef $$PendingWritesTableUpdateCompanionBuilder =
|
||||
PendingWritesCompanion Function({
|
||||
Value<int> id,
|
||||
Value<String> domain,
|
||||
Value<String> verb,
|
||||
Value<int?> targetId,
|
||||
Value<int?> tempId,
|
||||
Value<String> payloadJson,
|
||||
Value<DateTime?> baselineUpdatedAt,
|
||||
Value<DateTime> createdAt,
|
||||
Value<int> tries,
|
||||
Value<String?> lastError,
|
||||
});
|
||||
|
||||
class $$PendingWritesTableFilterComposer
|
||||
extends Composer<_$FabledDatabase, $PendingWritesTable> {
|
||||
$$PendingWritesTableFilterComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
ColumnFilters<int> get id => $composableBuilder(
|
||||
column: $table.id,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get domain => $composableBuilder(
|
||||
column: $table.domain,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get verb => $composableBuilder(
|
||||
column: $table.verb,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<int> get targetId => $composableBuilder(
|
||||
column: $table.targetId,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<int> get tempId => $composableBuilder(
|
||||
column: $table.tempId,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get payloadJson => $composableBuilder(
|
||||
column: $table.payloadJson,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<DateTime> get baselineUpdatedAt => $composableBuilder(
|
||||
column: $table.baselineUpdatedAt,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<DateTime> get createdAt => $composableBuilder(
|
||||
column: $table.createdAt,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<int> get tries => $composableBuilder(
|
||||
column: $table.tries,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get lastError => $composableBuilder(
|
||||
column: $table.lastError,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$PendingWritesTableOrderingComposer
|
||||
extends Composer<_$FabledDatabase, $PendingWritesTable> {
|
||||
$$PendingWritesTableOrderingComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
ColumnOrderings<int> get id => $composableBuilder(
|
||||
column: $table.id,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get domain => $composableBuilder(
|
||||
column: $table.domain,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get verb => $composableBuilder(
|
||||
column: $table.verb,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<int> get targetId => $composableBuilder(
|
||||
column: $table.targetId,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<int> get tempId => $composableBuilder(
|
||||
column: $table.tempId,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get payloadJson => $composableBuilder(
|
||||
column: $table.payloadJson,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<DateTime> get baselineUpdatedAt => $composableBuilder(
|
||||
column: $table.baselineUpdatedAt,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<DateTime> get createdAt => $composableBuilder(
|
||||
column: $table.createdAt,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<int> get tries => $composableBuilder(
|
||||
column: $table.tries,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get lastError => $composableBuilder(
|
||||
column: $table.lastError,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$PendingWritesTableAnnotationComposer
|
||||
extends Composer<_$FabledDatabase, $PendingWritesTable> {
|
||||
$$PendingWritesTableAnnotationComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
GeneratedColumn<int> get id =>
|
||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<String> get domain =>
|
||||
$composableBuilder(column: $table.domain, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<String> get verb =>
|
||||
$composableBuilder(column: $table.verb, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<int> get targetId =>
|
||||
$composableBuilder(column: $table.targetId, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<int> get tempId =>
|
||||
$composableBuilder(column: $table.tempId, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<String> get payloadJson => $composableBuilder(
|
||||
column: $table.payloadJson,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<DateTime> get baselineUpdatedAt => $composableBuilder(
|
||||
column: $table.baselineUpdatedAt,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<DateTime> get createdAt =>
|
||||
$composableBuilder(column: $table.createdAt, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<int> get tries =>
|
||||
$composableBuilder(column: $table.tries, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<String> get lastError =>
|
||||
$composableBuilder(column: $table.lastError, builder: (column) => column);
|
||||
}
|
||||
|
||||
class $$PendingWritesTableTableManager
|
||||
extends
|
||||
RootTableManager<
|
||||
_$FabledDatabase,
|
||||
$PendingWritesTable,
|
||||
PendingWrite,
|
||||
$$PendingWritesTableFilterComposer,
|
||||
$$PendingWritesTableOrderingComposer,
|
||||
$$PendingWritesTableAnnotationComposer,
|
||||
$$PendingWritesTableCreateCompanionBuilder,
|
||||
$$PendingWritesTableUpdateCompanionBuilder,
|
||||
(
|
||||
PendingWrite,
|
||||
BaseReferences<_$FabledDatabase, $PendingWritesTable, PendingWrite>,
|
||||
),
|
||||
PendingWrite,
|
||||
PrefetchHooks Function()
|
||||
> {
|
||||
$$PendingWritesTableTableManager(
|
||||
_$FabledDatabase db,
|
||||
$PendingWritesTable table,
|
||||
) : super(
|
||||
TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
$$PendingWritesTableFilterComposer($db: db, $table: table),
|
||||
createOrderingComposer: () =>
|
||||
$$PendingWritesTableOrderingComposer($db: db, $table: table),
|
||||
createComputedFieldComposer: () =>
|
||||
$$PendingWritesTableAnnotationComposer($db: db, $table: table),
|
||||
updateCompanionCallback:
|
||||
({
|
||||
Value<int> id = const Value.absent(),
|
||||
Value<String> domain = const Value.absent(),
|
||||
Value<String> verb = const Value.absent(),
|
||||
Value<int?> targetId = const Value.absent(),
|
||||
Value<int?> tempId = const Value.absent(),
|
||||
Value<String> payloadJson = const Value.absent(),
|
||||
Value<DateTime?> baselineUpdatedAt = const Value.absent(),
|
||||
Value<DateTime> createdAt = const Value.absent(),
|
||||
Value<int> tries = const Value.absent(),
|
||||
Value<String?> lastError = const Value.absent(),
|
||||
}) => PendingWritesCompanion(
|
||||
id: id,
|
||||
domain: domain,
|
||||
verb: verb,
|
||||
targetId: targetId,
|
||||
tempId: tempId,
|
||||
payloadJson: payloadJson,
|
||||
baselineUpdatedAt: baselineUpdatedAt,
|
||||
createdAt: createdAt,
|
||||
tries: tries,
|
||||
lastError: lastError,
|
||||
),
|
||||
createCompanionCallback:
|
||||
({
|
||||
Value<int> id = const Value.absent(),
|
||||
required String domain,
|
||||
required String verb,
|
||||
Value<int?> targetId = const Value.absent(),
|
||||
Value<int?> tempId = const Value.absent(),
|
||||
Value<String> payloadJson = const Value.absent(),
|
||||
Value<DateTime?> baselineUpdatedAt = const Value.absent(),
|
||||
Value<DateTime> createdAt = const Value.absent(),
|
||||
Value<int> tries = const Value.absent(),
|
||||
Value<String?> lastError = const Value.absent(),
|
||||
}) => PendingWritesCompanion.insert(
|
||||
id: id,
|
||||
domain: domain,
|
||||
verb: verb,
|
||||
targetId: targetId,
|
||||
tempId: tempId,
|
||||
payloadJson: payloadJson,
|
||||
baselineUpdatedAt: baselineUpdatedAt,
|
||||
createdAt: createdAt,
|
||||
tries: tries,
|
||||
lastError: lastError,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
|
||||
.toList(),
|
||||
prefetchHooksCallback: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
typedef $$PendingWritesTableProcessedTableManager =
|
||||
ProcessedTableManager<
|
||||
_$FabledDatabase,
|
||||
$PendingWritesTable,
|
||||
PendingWrite,
|
||||
$$PendingWritesTableFilterComposer,
|
||||
$$PendingWritesTableOrderingComposer,
|
||||
$$PendingWritesTableAnnotationComposer,
|
||||
$$PendingWritesTableCreateCompanionBuilder,
|
||||
$$PendingWritesTableUpdateCompanionBuilder,
|
||||
(
|
||||
PendingWrite,
|
||||
BaseReferences<_$FabledDatabase, $PendingWritesTable, PendingWrite>,
|
||||
),
|
||||
PendingWrite,
|
||||
PrefetchHooks Function()
|
||||
>;
|
||||
|
||||
class $FabledDatabaseManager {
|
||||
final _$FabledDatabase _db;
|
||||
@@ -5848,4 +6748,6 @@ class $FabledDatabaseManager {
|
||||
$$CachedConversationsTableTableManager(_db, _db.cachedConversations);
|
||||
$$SyncMetadataTableTableManager get syncMetadata =>
|
||||
$$SyncMetadataTableTableManager(_db, _db.syncMetadata);
|
||||
$$PendingWritesTableTableManager get pendingWrites =>
|
||||
$$PendingWritesTableTableManager(_db, _db.pendingWrites);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user