feat(chat): usable 1:1 chat — bottom-anchored, Drift-backed, dated
- Anchor the message list to the bottom (`reverse: true`) so new messages stay in view instead of landing below the fold. - Move chat history from the OS keystore (O(n²) JSON blob, silent 200-msg cap) to a separate encrypted Drift/SQLCipher DB (`ChatDatabase`): indexed append, uncapped history, dedup as a unique-key invariant. It's an ephemeral per-device cache, isolated from the inventory schema, its migrations, and its sync. No data migration (pre-release). - Add day separators (Today/Yesterday/locale date) and a per-bubble time, all via ICU (12/24h per locale; Localizations locale maps Asturian → Spanish for intl date symbols). - Add peer avatars (deterministic colour from the pubkey + name initial), surface send failures that were previously silent, and make bubble text selectable (addresses, links). - New i18n keys in en/es/pt/ast; tests for grouping, formatting, avatars, scroll anchoring, storage and send errors. Docs: docs/design/chat-storage.md + open-decisions.md.
This commit is contained in:
parent
171daabce3
commit
6cff0d0b11
27 changed files with 1793 additions and 264 deletions
54
apps/app_seeds/lib/db/chat_database.dart
Normal file
54
apps/app_seeds/lib/db/chat_database.dart
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import 'package:drift/drift.dart';
|
||||
|
||||
part 'chat_database.g.dart';
|
||||
|
||||
/// A 1:1 chat message, held locally so a conversation survives leaving the
|
||||
/// screen and going offline.
|
||||
///
|
||||
/// This is deliberately NOT a [SyncColumns] inventory row: chat history is an
|
||||
/// ephemeral, per-device cache of what the relays delivered — not the user's
|
||||
/// CRDT-synced inventory. It never enters an inventory snapshot, backup, or the
|
||||
/// device-to-device sync, so it needs no HLC / tombstone / author metadata. It
|
||||
/// lives in its own encrypted database ([ChatDatabase]) to keep it fully
|
||||
/// isolated from the inventory schema, its migrations, and its sync stream.
|
||||
///
|
||||
/// [accountScope] namespaces rows per social identity (empty = the original
|
||||
/// identity), replacing the old per-key keystore prefix. The unique key makes
|
||||
/// de-duplication a DB invariant: a relay re-delivers the same gift wrap on
|
||||
/// every (re)subscribe, and an identical (scope, peer, sender, timestamp, text)
|
||||
/// row is simply not inserted twice.
|
||||
@TableIndex(
|
||||
name: 'messages_by_conversation',
|
||||
columns: {#accountScope, #peerPubkey, #sentAt},
|
||||
)
|
||||
class Messages extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
TextColumn get accountScope => text()();
|
||||
TextColumn get peerPubkey => text()();
|
||||
TextColumn get fromPubkey => text()();
|
||||
TextColumn get body => text()();
|
||||
|
||||
/// Milliseconds since epoch — the message's own timestamp (NIP-17), used for
|
||||
/// ordering and for the read/unread mark.
|
||||
IntColumn get sentAt => integer()();
|
||||
|
||||
@override
|
||||
List<Set<Column>> get uniqueKeys => [
|
||||
{accountScope, peerPubkey, fromPubkey, sentAt, body},
|
||||
];
|
||||
}
|
||||
|
||||
/// The encrypted local chat cache (SQLCipher via an injected executor, exactly
|
||||
/// like [AppDatabase]). Separate database on purpose — see [Messages]. There is
|
||||
/// no historical schema to migrate: version 1, created fresh.
|
||||
@DriftDatabase(tables: [Messages])
|
||||
class ChatDatabase extends _$ChatDatabase {
|
||||
ChatDatabase(super.e);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration =>
|
||||
MigrationStrategy(onCreate: (m) async => m.createAll());
|
||||
}
|
||||
654
apps/app_seeds/lib/db/chat_database.g.dart
Normal file
654
apps/app_seeds/lib/db/chat_database.g.dart
Normal file
|
|
@ -0,0 +1,654 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'chat_database.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
||||
@override
|
||||
final GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
$MessagesTable(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 _accountScopeMeta = const VerificationMeta(
|
||||
'accountScope',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<String> accountScope = GeneratedColumn<String>(
|
||||
'account_scope',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
static const VerificationMeta _peerPubkeyMeta = const VerificationMeta(
|
||||
'peerPubkey',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<String> peerPubkey = GeneratedColumn<String>(
|
||||
'peer_pubkey',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
static const VerificationMeta _fromPubkeyMeta = const VerificationMeta(
|
||||
'fromPubkey',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<String> fromPubkey = GeneratedColumn<String>(
|
||||
'from_pubkey',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
static const VerificationMeta _bodyMeta = const VerificationMeta('body');
|
||||
@override
|
||||
late final GeneratedColumn<String> body = GeneratedColumn<String>(
|
||||
'body',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
static const VerificationMeta _sentAtMeta = const VerificationMeta('sentAt');
|
||||
@override
|
||||
late final GeneratedColumn<int> sentAt = GeneratedColumn<int>(
|
||||
'sent_at',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [
|
||||
id,
|
||||
accountScope,
|
||||
peerPubkey,
|
||||
fromPubkey,
|
||||
body,
|
||||
sentAt,
|
||||
];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'messages';
|
||||
@override
|
||||
VerificationContext validateIntegrity(
|
||||
Insertable<Message> 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('account_scope')) {
|
||||
context.handle(
|
||||
_accountScopeMeta,
|
||||
accountScope.isAcceptableOrUnknown(
|
||||
data['account_scope']!,
|
||||
_accountScopeMeta,
|
||||
),
|
||||
);
|
||||
} else if (isInserting) {
|
||||
context.missing(_accountScopeMeta);
|
||||
}
|
||||
if (data.containsKey('peer_pubkey')) {
|
||||
context.handle(
|
||||
_peerPubkeyMeta,
|
||||
peerPubkey.isAcceptableOrUnknown(data['peer_pubkey']!, _peerPubkeyMeta),
|
||||
);
|
||||
} else if (isInserting) {
|
||||
context.missing(_peerPubkeyMeta);
|
||||
}
|
||||
if (data.containsKey('from_pubkey')) {
|
||||
context.handle(
|
||||
_fromPubkeyMeta,
|
||||
fromPubkey.isAcceptableOrUnknown(data['from_pubkey']!, _fromPubkeyMeta),
|
||||
);
|
||||
} else if (isInserting) {
|
||||
context.missing(_fromPubkeyMeta);
|
||||
}
|
||||
if (data.containsKey('body')) {
|
||||
context.handle(
|
||||
_bodyMeta,
|
||||
body.isAcceptableOrUnknown(data['body']!, _bodyMeta),
|
||||
);
|
||||
} else if (isInserting) {
|
||||
context.missing(_bodyMeta);
|
||||
}
|
||||
if (data.containsKey('sent_at')) {
|
||||
context.handle(
|
||||
_sentAtMeta,
|
||||
sentAt.isAcceptableOrUnknown(data['sent_at']!, _sentAtMeta),
|
||||
);
|
||||
} else if (isInserting) {
|
||||
context.missing(_sentAtMeta);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<GeneratedColumn> get $primaryKey => {id};
|
||||
@override
|
||||
List<Set<GeneratedColumn>> get uniqueKeys => [
|
||||
{accountScope, peerPubkey, fromPubkey, sentAt, body},
|
||||
];
|
||||
@override
|
||||
Message map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return Message(
|
||||
id: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}id'],
|
||||
)!,
|
||||
accountScope: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}account_scope'],
|
||||
)!,
|
||||
peerPubkey: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}peer_pubkey'],
|
||||
)!,
|
||||
fromPubkey: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}from_pubkey'],
|
||||
)!,
|
||||
body: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}body'],
|
||||
)!,
|
||||
sentAt: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}sent_at'],
|
||||
)!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
$MessagesTable createAlias(String alias) {
|
||||
return $MessagesTable(attachedDatabase, alias);
|
||||
}
|
||||
}
|
||||
|
||||
class Message extends DataClass implements Insertable<Message> {
|
||||
final int id;
|
||||
final String accountScope;
|
||||
final String peerPubkey;
|
||||
final String fromPubkey;
|
||||
final String body;
|
||||
|
||||
/// Milliseconds since epoch — the message's own timestamp (NIP-17), used for
|
||||
/// ordering and for the read/unread mark.
|
||||
final int sentAt;
|
||||
const Message({
|
||||
required this.id,
|
||||
required this.accountScope,
|
||||
required this.peerPubkey,
|
||||
required this.fromPubkey,
|
||||
required this.body,
|
||||
required this.sentAt,
|
||||
});
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
map['id'] = Variable<int>(id);
|
||||
map['account_scope'] = Variable<String>(accountScope);
|
||||
map['peer_pubkey'] = Variable<String>(peerPubkey);
|
||||
map['from_pubkey'] = Variable<String>(fromPubkey);
|
||||
map['body'] = Variable<String>(body);
|
||||
map['sent_at'] = Variable<int>(sentAt);
|
||||
return map;
|
||||
}
|
||||
|
||||
MessagesCompanion toCompanion(bool nullToAbsent) {
|
||||
return MessagesCompanion(
|
||||
id: Value(id),
|
||||
accountScope: Value(accountScope),
|
||||
peerPubkey: Value(peerPubkey),
|
||||
fromPubkey: Value(fromPubkey),
|
||||
body: Value(body),
|
||||
sentAt: Value(sentAt),
|
||||
);
|
||||
}
|
||||
|
||||
factory Message.fromJson(
|
||||
Map<String, dynamic> json, {
|
||||
ValueSerializer? serializer,
|
||||
}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return Message(
|
||||
id: serializer.fromJson<int>(json['id']),
|
||||
accountScope: serializer.fromJson<String>(json['accountScope']),
|
||||
peerPubkey: serializer.fromJson<String>(json['peerPubkey']),
|
||||
fromPubkey: serializer.fromJson<String>(json['fromPubkey']),
|
||||
body: serializer.fromJson<String>(json['body']),
|
||||
sentAt: serializer.fromJson<int>(json['sentAt']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'id': serializer.toJson<int>(id),
|
||||
'accountScope': serializer.toJson<String>(accountScope),
|
||||
'peerPubkey': serializer.toJson<String>(peerPubkey),
|
||||
'fromPubkey': serializer.toJson<String>(fromPubkey),
|
||||
'body': serializer.toJson<String>(body),
|
||||
'sentAt': serializer.toJson<int>(sentAt),
|
||||
};
|
||||
}
|
||||
|
||||
Message copyWith({
|
||||
int? id,
|
||||
String? accountScope,
|
||||
String? peerPubkey,
|
||||
String? fromPubkey,
|
||||
String? body,
|
||||
int? sentAt,
|
||||
}) => Message(
|
||||
id: id ?? this.id,
|
||||
accountScope: accountScope ?? this.accountScope,
|
||||
peerPubkey: peerPubkey ?? this.peerPubkey,
|
||||
fromPubkey: fromPubkey ?? this.fromPubkey,
|
||||
body: body ?? this.body,
|
||||
sentAt: sentAt ?? this.sentAt,
|
||||
);
|
||||
Message copyWithCompanion(MessagesCompanion data) {
|
||||
return Message(
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
accountScope: data.accountScope.present
|
||||
? data.accountScope.value
|
||||
: this.accountScope,
|
||||
peerPubkey: data.peerPubkey.present
|
||||
? data.peerPubkey.value
|
||||
: this.peerPubkey,
|
||||
fromPubkey: data.fromPubkey.present
|
||||
? data.fromPubkey.value
|
||||
: this.fromPubkey,
|
||||
body: data.body.present ? data.body.value : this.body,
|
||||
sentAt: data.sentAt.present ? data.sentAt.value : this.sentAt,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('Message(')
|
||||
..write('id: $id, ')
|
||||
..write('accountScope: $accountScope, ')
|
||||
..write('peerPubkey: $peerPubkey, ')
|
||||
..write('fromPubkey: $fromPubkey, ')
|
||||
..write('body: $body, ')
|
||||
..write('sentAt: $sentAt')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(id, accountScope, peerPubkey, fromPubkey, body, sentAt);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is Message &&
|
||||
other.id == this.id &&
|
||||
other.accountScope == this.accountScope &&
|
||||
other.peerPubkey == this.peerPubkey &&
|
||||
other.fromPubkey == this.fromPubkey &&
|
||||
other.body == this.body &&
|
||||
other.sentAt == this.sentAt);
|
||||
}
|
||||
|
||||
class MessagesCompanion extends UpdateCompanion<Message> {
|
||||
final Value<int> id;
|
||||
final Value<String> accountScope;
|
||||
final Value<String> peerPubkey;
|
||||
final Value<String> fromPubkey;
|
||||
final Value<String> body;
|
||||
final Value<int> sentAt;
|
||||
const MessagesCompanion({
|
||||
this.id = const Value.absent(),
|
||||
this.accountScope = const Value.absent(),
|
||||
this.peerPubkey = const Value.absent(),
|
||||
this.fromPubkey = const Value.absent(),
|
||||
this.body = const Value.absent(),
|
||||
this.sentAt = const Value.absent(),
|
||||
});
|
||||
MessagesCompanion.insert({
|
||||
this.id = const Value.absent(),
|
||||
required String accountScope,
|
||||
required String peerPubkey,
|
||||
required String fromPubkey,
|
||||
required String body,
|
||||
required int sentAt,
|
||||
}) : accountScope = Value(accountScope),
|
||||
peerPubkey = Value(peerPubkey),
|
||||
fromPubkey = Value(fromPubkey),
|
||||
body = Value(body),
|
||||
sentAt = Value(sentAt);
|
||||
static Insertable<Message> custom({
|
||||
Expression<int>? id,
|
||||
Expression<String>? accountScope,
|
||||
Expression<String>? peerPubkey,
|
||||
Expression<String>? fromPubkey,
|
||||
Expression<String>? body,
|
||||
Expression<int>? sentAt,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
if (id != null) 'id': id,
|
||||
if (accountScope != null) 'account_scope': accountScope,
|
||||
if (peerPubkey != null) 'peer_pubkey': peerPubkey,
|
||||
if (fromPubkey != null) 'from_pubkey': fromPubkey,
|
||||
if (body != null) 'body': body,
|
||||
if (sentAt != null) 'sent_at': sentAt,
|
||||
});
|
||||
}
|
||||
|
||||
MessagesCompanion copyWith({
|
||||
Value<int>? id,
|
||||
Value<String>? accountScope,
|
||||
Value<String>? peerPubkey,
|
||||
Value<String>? fromPubkey,
|
||||
Value<String>? body,
|
||||
Value<int>? sentAt,
|
||||
}) {
|
||||
return MessagesCompanion(
|
||||
id: id ?? this.id,
|
||||
accountScope: accountScope ?? this.accountScope,
|
||||
peerPubkey: peerPubkey ?? this.peerPubkey,
|
||||
fromPubkey: fromPubkey ?? this.fromPubkey,
|
||||
body: body ?? this.body,
|
||||
sentAt: sentAt ?? this.sentAt,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
if (id.present) {
|
||||
map['id'] = Variable<int>(id.value);
|
||||
}
|
||||
if (accountScope.present) {
|
||||
map['account_scope'] = Variable<String>(accountScope.value);
|
||||
}
|
||||
if (peerPubkey.present) {
|
||||
map['peer_pubkey'] = Variable<String>(peerPubkey.value);
|
||||
}
|
||||
if (fromPubkey.present) {
|
||||
map['from_pubkey'] = Variable<String>(fromPubkey.value);
|
||||
}
|
||||
if (body.present) {
|
||||
map['body'] = Variable<String>(body.value);
|
||||
}
|
||||
if (sentAt.present) {
|
||||
map['sent_at'] = Variable<int>(sentAt.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('MessagesCompanion(')
|
||||
..write('id: $id, ')
|
||||
..write('accountScope: $accountScope, ')
|
||||
..write('peerPubkey: $peerPubkey, ')
|
||||
..write('fromPubkey: $fromPubkey, ')
|
||||
..write('body: $body, ')
|
||||
..write('sentAt: $sentAt')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _$ChatDatabase extends GeneratedDatabase {
|
||||
_$ChatDatabase(QueryExecutor e) : super(e);
|
||||
$ChatDatabaseManager get managers => $ChatDatabaseManager(this);
|
||||
late final $MessagesTable messages = $MessagesTable(this);
|
||||
late final Index messagesByConversation = Index(
|
||||
'messages_by_conversation',
|
||||
'CREATE INDEX messages_by_conversation ON messages (account_scope, peer_pubkey, sent_at)',
|
||||
);
|
||||
@override
|
||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
||||
@override
|
||||
List<DatabaseSchemaEntity> get allSchemaEntities => [
|
||||
messages,
|
||||
messagesByConversation,
|
||||
];
|
||||
}
|
||||
|
||||
typedef $$MessagesTableCreateCompanionBuilder =
|
||||
MessagesCompanion Function({
|
||||
Value<int> id,
|
||||
required String accountScope,
|
||||
required String peerPubkey,
|
||||
required String fromPubkey,
|
||||
required String body,
|
||||
required int sentAt,
|
||||
});
|
||||
typedef $$MessagesTableUpdateCompanionBuilder =
|
||||
MessagesCompanion Function({
|
||||
Value<int> id,
|
||||
Value<String> accountScope,
|
||||
Value<String> peerPubkey,
|
||||
Value<String> fromPubkey,
|
||||
Value<String> body,
|
||||
Value<int> sentAt,
|
||||
});
|
||||
|
||||
class $$MessagesTableFilterComposer
|
||||
extends Composer<_$ChatDatabase, $MessagesTable> {
|
||||
$$MessagesTableFilterComposer({
|
||||
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 accountScope => $composableBuilder(
|
||||
column: $table.accountScope,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get peerPubkey => $composableBuilder(
|
||||
column: $table.peerPubkey,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get fromPubkey => $composableBuilder(
|
||||
column: $table.fromPubkey,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get body => $composableBuilder(
|
||||
column: $table.body,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<int> get sentAt => $composableBuilder(
|
||||
column: $table.sentAt,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$MessagesTableOrderingComposer
|
||||
extends Composer<_$ChatDatabase, $MessagesTable> {
|
||||
$$MessagesTableOrderingComposer({
|
||||
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 accountScope => $composableBuilder(
|
||||
column: $table.accountScope,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get peerPubkey => $composableBuilder(
|
||||
column: $table.peerPubkey,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get fromPubkey => $composableBuilder(
|
||||
column: $table.fromPubkey,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get body => $composableBuilder(
|
||||
column: $table.body,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<int> get sentAt => $composableBuilder(
|
||||
column: $table.sentAt,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$MessagesTableAnnotationComposer
|
||||
extends Composer<_$ChatDatabase, $MessagesTable> {
|
||||
$$MessagesTableAnnotationComposer({
|
||||
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 accountScope => $composableBuilder(
|
||||
column: $table.accountScope,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<String> get peerPubkey => $composableBuilder(
|
||||
column: $table.peerPubkey,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<String> get fromPubkey => $composableBuilder(
|
||||
column: $table.fromPubkey,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<String> get body =>
|
||||
$composableBuilder(column: $table.body, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<int> get sentAt =>
|
||||
$composableBuilder(column: $table.sentAt, builder: (column) => column);
|
||||
}
|
||||
|
||||
class $$MessagesTableTableManager
|
||||
extends
|
||||
RootTableManager<
|
||||
_$ChatDatabase,
|
||||
$MessagesTable,
|
||||
Message,
|
||||
$$MessagesTableFilterComposer,
|
||||
$$MessagesTableOrderingComposer,
|
||||
$$MessagesTableAnnotationComposer,
|
||||
$$MessagesTableCreateCompanionBuilder,
|
||||
$$MessagesTableUpdateCompanionBuilder,
|
||||
(Message, BaseReferences<_$ChatDatabase, $MessagesTable, Message>),
|
||||
Message,
|
||||
PrefetchHooks Function()
|
||||
> {
|
||||
$$MessagesTableTableManager(_$ChatDatabase db, $MessagesTable table)
|
||||
: super(
|
||||
TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
$$MessagesTableFilterComposer($db: db, $table: table),
|
||||
createOrderingComposer: () =>
|
||||
$$MessagesTableOrderingComposer($db: db, $table: table),
|
||||
createComputedFieldComposer: () =>
|
||||
$$MessagesTableAnnotationComposer($db: db, $table: table),
|
||||
updateCompanionCallback:
|
||||
({
|
||||
Value<int> id = const Value.absent(),
|
||||
Value<String> accountScope = const Value.absent(),
|
||||
Value<String> peerPubkey = const Value.absent(),
|
||||
Value<String> fromPubkey = const Value.absent(),
|
||||
Value<String> body = const Value.absent(),
|
||||
Value<int> sentAt = const Value.absent(),
|
||||
}) => MessagesCompanion(
|
||||
id: id,
|
||||
accountScope: accountScope,
|
||||
peerPubkey: peerPubkey,
|
||||
fromPubkey: fromPubkey,
|
||||
body: body,
|
||||
sentAt: sentAt,
|
||||
),
|
||||
createCompanionCallback:
|
||||
({
|
||||
Value<int> id = const Value.absent(),
|
||||
required String accountScope,
|
||||
required String peerPubkey,
|
||||
required String fromPubkey,
|
||||
required String body,
|
||||
required int sentAt,
|
||||
}) => MessagesCompanion.insert(
|
||||
id: id,
|
||||
accountScope: accountScope,
|
||||
peerPubkey: peerPubkey,
|
||||
fromPubkey: fromPubkey,
|
||||
body: body,
|
||||
sentAt: sentAt,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
|
||||
.toList(),
|
||||
prefetchHooksCallback: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
typedef $$MessagesTableProcessedTableManager =
|
||||
ProcessedTableManager<
|
||||
_$ChatDatabase,
|
||||
$MessagesTable,
|
||||
Message,
|
||||
$$MessagesTableFilterComposer,
|
||||
$$MessagesTableOrderingComposer,
|
||||
$$MessagesTableAnnotationComposer,
|
||||
$$MessagesTableCreateCompanionBuilder,
|
||||
$$MessagesTableUpdateCompanionBuilder,
|
||||
(Message, BaseReferences<_$ChatDatabase, $MessagesTable, Message>),
|
||||
Message,
|
||||
PrefetchHooks Function()
|
||||
>;
|
||||
|
||||
class $ChatDatabaseManager {
|
||||
final _$ChatDatabase _db;
|
||||
$ChatDatabaseManager(this._db);
|
||||
$$MessagesTableTableManager get messages =>
|
||||
$$MessagesTableTableManager(_db, _db.messages);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue