This commit is contained in:
otsmr 2026-01-22 23:26:23 +01:00
parent 4c06bcc751
commit b48df1baa5
31 changed files with 8313 additions and 132 deletions

File diff suppressed because one or more lines are too long

View file

@ -22,6 +22,8 @@ class Messages extends Table {
.nullable() .nullable()
.references(MediaFiles, #mediaId, onDelete: KeyAction.setNull)(); .references(MediaFiles, #mediaId, onDelete: KeyAction.setNull)();
BlobColumn get additionalMessageData => blob().nullable()();
BoolColumn get mediaStored => boolean().withDefault(const Constant(false))(); BoolColumn get mediaStored => boolean().withDefault(const Constant(false))();
BoolColumn get mediaReopened => BoolColumn get mediaReopened =>
boolean().withDefault(const Constant(false))(); boolean().withDefault(const Constant(false))();
@ -56,7 +58,7 @@ class MessageActions extends Table {
text().references(Messages, #messageId, onDelete: KeyAction.cascade)(); text().references(Messages, #messageId, onDelete: KeyAction.cascade)();
IntColumn get contactId => IntColumn get contactId =>
integer().references(Contacts, #contactId, onDelete: KeyAction.cascade)(); integer().references(Contacts, #userId, onDelete: KeyAction.cascade)();
TextColumn get type => textEnum<MessageActionType>()(); TextColumn get type => textEnum<MessageActionType>()();
@ -75,7 +77,7 @@ class MessageHistories extends Table {
IntColumn get contactId => integer() IntColumn get contactId => integer()
.nullable() .nullable()
.references(Contacts, #contactId, onDelete: KeyAction.cascade)(); .references(Contacts, #userId, onDelete: KeyAction.cascade)();
TextColumn get content => text().nullable()(); TextColumn get content => text().nullable()();

View file

@ -68,7 +68,7 @@ class TwonlyDB extends _$TwonlyDB {
TwonlyDB.forTesting(DatabaseConnection super.connection); TwonlyDB.forTesting(DatabaseConnection super.connection);
@override @override
int get schemaVersion => 6; int get schemaVersion => 7;
static QueryExecutor _openConnection() { static QueryExecutor _openConnection() {
return driftDatabase( return driftDatabase(
@ -85,7 +85,10 @@ class TwonlyDB extends _$TwonlyDB {
beforeOpen: (details) async { beforeOpen: (details) async {
await customStatement('PRAGMA foreign_keys = ON'); await customStatement('PRAGMA foreign_keys = ON');
}, },
onUpgrade: stepByStep( onUpgrade: (m, from, to) async {
// disable foreign_keys before migrations
await customStatement('PRAGMA foreign_keys = OFF');
return stepByStep(
from1To2: (m, schema) async { from1To2: (m, schema) async {
await m.addColumn(schema.messages, schema.messages.mediaReopened); await m.addColumn(schema.messages, schema.messages.mediaReopened);
await m.dropColumn(schema.mediaFiles, 'reopen_by_contact'); await m.dropColumn(schema.mediaFiles, 'reopen_by_contact');
@ -117,7 +120,14 @@ class TwonlyDB extends _$TwonlyDB {
schema.receipts.markForRetryAfterAccepted, schema.receipts.markForRetryAfterAccepted,
); );
}, },
), from6To7: (m, schema) async {
await m.addColumn(
schema.messages,
schema.messages.additionalMessageData,
);
},
)(m, from, to);
},
); );
} }

View file

@ -2796,6 +2796,12 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
requiredDuringInsert: false, requiredDuringInsert: false,
defaultConstraints: GeneratedColumn.constraintIsAlways( defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES media_files (media_id) ON DELETE SET NULL')); 'REFERENCES media_files (media_id) ON DELETE SET NULL'));
static const VerificationMeta _additionalMessageDataMeta =
const VerificationMeta('additionalMessageData');
@override
late final GeneratedColumn<Uint8List> additionalMessageData =
GeneratedColumn<Uint8List>('additional_message_data', aliasedName, true,
type: DriftSqlType.blob, requiredDuringInsert: false);
static const VerificationMeta _mediaStoredMeta = static const VerificationMeta _mediaStoredMeta =
const VerificationMeta('mediaStored'); const VerificationMeta('mediaStored');
@override @override
@ -2884,6 +2890,7 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
type, type,
content, content,
mediaId, mediaId,
additionalMessageData,
mediaStored, mediaStored,
mediaReopened, mediaReopened,
downloadToken, downloadToken,
@ -2930,6 +2937,12 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
context.handle(_mediaIdMeta, context.handle(_mediaIdMeta,
mediaId.isAcceptableOrUnknown(data['media_id']!, _mediaIdMeta)); mediaId.isAcceptableOrUnknown(data['media_id']!, _mediaIdMeta));
} }
if (data.containsKey('additional_message_data')) {
context.handle(
_additionalMessageDataMeta,
additionalMessageData.isAcceptableOrUnknown(
data['additional_message_data']!, _additionalMessageDataMeta));
}
if (data.containsKey('media_stored')) { if (data.containsKey('media_stored')) {
context.handle( context.handle(
_mediaStoredMeta, _mediaStoredMeta,
@ -3013,6 +3026,8 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
.read(DriftSqlType.string, data['${effectivePrefix}content']), .read(DriftSqlType.string, data['${effectivePrefix}content']),
mediaId: attachedDatabase.typeMapping mediaId: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}media_id']), .read(DriftSqlType.string, data['${effectivePrefix}media_id']),
additionalMessageData: attachedDatabase.typeMapping.read(
DriftSqlType.blob, data['${effectivePrefix}additional_message_data']),
mediaStored: attachedDatabase.typeMapping mediaStored: attachedDatabase.typeMapping
.read(DriftSqlType.bool, data['${effectivePrefix}media_stored'])!, .read(DriftSqlType.bool, data['${effectivePrefix}media_stored'])!,
mediaReopened: attachedDatabase.typeMapping mediaReopened: attachedDatabase.typeMapping
@ -3054,6 +3069,7 @@ class Message extends DataClass implements Insertable<Message> {
final MessageType type; final MessageType type;
final String? content; final String? content;
final String? mediaId; final String? mediaId;
final Uint8List? additionalMessageData;
final bool mediaStored; final bool mediaStored;
final bool mediaReopened; final bool mediaReopened;
final Uint8List? downloadToken; final Uint8List? downloadToken;
@ -3072,6 +3088,7 @@ class Message extends DataClass implements Insertable<Message> {
required this.type, required this.type,
this.content, this.content,
this.mediaId, this.mediaId,
this.additionalMessageData,
required this.mediaStored, required this.mediaStored,
required this.mediaReopened, required this.mediaReopened,
this.downloadToken, this.downloadToken,
@ -3100,6 +3117,10 @@ class Message extends DataClass implements Insertable<Message> {
if (!nullToAbsent || mediaId != null) { if (!nullToAbsent || mediaId != null) {
map['media_id'] = Variable<String>(mediaId); map['media_id'] = Variable<String>(mediaId);
} }
if (!nullToAbsent || additionalMessageData != null) {
map['additional_message_data'] =
Variable<Uint8List>(additionalMessageData);
}
map['media_stored'] = Variable<bool>(mediaStored); map['media_stored'] = Variable<bool>(mediaStored);
map['media_reopened'] = Variable<bool>(mediaReopened); map['media_reopened'] = Variable<bool>(mediaReopened);
if (!nullToAbsent || downloadToken != null) { if (!nullToAbsent || downloadToken != null) {
@ -3142,6 +3163,9 @@ class Message extends DataClass implements Insertable<Message> {
mediaId: mediaId == null && nullToAbsent mediaId: mediaId == null && nullToAbsent
? const Value.absent() ? const Value.absent()
: Value(mediaId), : Value(mediaId),
additionalMessageData: additionalMessageData == null && nullToAbsent
? const Value.absent()
: Value(additionalMessageData),
mediaStored: Value(mediaStored), mediaStored: Value(mediaStored),
mediaReopened: Value(mediaReopened), mediaReopened: Value(mediaReopened),
downloadToken: downloadToken == null && nullToAbsent downloadToken: downloadToken == null && nullToAbsent
@ -3181,6 +3205,8 @@ class Message extends DataClass implements Insertable<Message> {
.fromJson(serializer.fromJson<String>(json['type'])), .fromJson(serializer.fromJson<String>(json['type'])),
content: serializer.fromJson<String?>(json['content']), content: serializer.fromJson<String?>(json['content']),
mediaId: serializer.fromJson<String?>(json['mediaId']), mediaId: serializer.fromJson<String?>(json['mediaId']),
additionalMessageData:
serializer.fromJson<Uint8List?>(json['additionalMessageData']),
mediaStored: serializer.fromJson<bool>(json['mediaStored']), mediaStored: serializer.fromJson<bool>(json['mediaStored']),
mediaReopened: serializer.fromJson<bool>(json['mediaReopened']), mediaReopened: serializer.fromJson<bool>(json['mediaReopened']),
downloadToken: serializer.fromJson<Uint8List?>(json['downloadToken']), downloadToken: serializer.fromJson<Uint8List?>(json['downloadToken']),
@ -3206,6 +3232,8 @@ class Message extends DataClass implements Insertable<Message> {
serializer.toJson<String>($MessagesTable.$convertertype.toJson(type)), serializer.toJson<String>($MessagesTable.$convertertype.toJson(type)),
'content': serializer.toJson<String?>(content), 'content': serializer.toJson<String?>(content),
'mediaId': serializer.toJson<String?>(mediaId), 'mediaId': serializer.toJson<String?>(mediaId),
'additionalMessageData':
serializer.toJson<Uint8List?>(additionalMessageData),
'mediaStored': serializer.toJson<bool>(mediaStored), 'mediaStored': serializer.toJson<bool>(mediaStored),
'mediaReopened': serializer.toJson<bool>(mediaReopened), 'mediaReopened': serializer.toJson<bool>(mediaReopened),
'downloadToken': serializer.toJson<Uint8List?>(downloadToken), 'downloadToken': serializer.toJson<Uint8List?>(downloadToken),
@ -3227,6 +3255,7 @@ class Message extends DataClass implements Insertable<Message> {
MessageType? type, MessageType? type,
Value<String?> content = const Value.absent(), Value<String?> content = const Value.absent(),
Value<String?> mediaId = const Value.absent(), Value<String?> mediaId = const Value.absent(),
Value<Uint8List?> additionalMessageData = const Value.absent(),
bool? mediaStored, bool? mediaStored,
bool? mediaReopened, bool? mediaReopened,
Value<Uint8List?> downloadToken = const Value.absent(), Value<Uint8List?> downloadToken = const Value.absent(),
@ -3245,6 +3274,9 @@ class Message extends DataClass implements Insertable<Message> {
type: type ?? this.type, type: type ?? this.type,
content: content.present ? content.value : this.content, content: content.present ? content.value : this.content,
mediaId: mediaId.present ? mediaId.value : this.mediaId, mediaId: mediaId.present ? mediaId.value : this.mediaId,
additionalMessageData: additionalMessageData.present
? additionalMessageData.value
: this.additionalMessageData,
mediaStored: mediaStored ?? this.mediaStored, mediaStored: mediaStored ?? this.mediaStored,
mediaReopened: mediaReopened ?? this.mediaReopened, mediaReopened: mediaReopened ?? this.mediaReopened,
downloadToken: downloadToken:
@ -3268,6 +3300,9 @@ class Message extends DataClass implements Insertable<Message> {
type: data.type.present ? data.type.value : this.type, type: data.type.present ? data.type.value : this.type,
content: data.content.present ? data.content.value : this.content, content: data.content.present ? data.content.value : this.content,
mediaId: data.mediaId.present ? data.mediaId.value : this.mediaId, mediaId: data.mediaId.present ? data.mediaId.value : this.mediaId,
additionalMessageData: data.additionalMessageData.present
? data.additionalMessageData.value
: this.additionalMessageData,
mediaStored: mediaStored:
data.mediaStored.present ? data.mediaStored.value : this.mediaStored, data.mediaStored.present ? data.mediaStored.value : this.mediaStored,
mediaReopened: data.mediaReopened.present mediaReopened: data.mediaReopened.present
@ -3303,6 +3338,7 @@ class Message extends DataClass implements Insertable<Message> {
..write('type: $type, ') ..write('type: $type, ')
..write('content: $content, ') ..write('content: $content, ')
..write('mediaId: $mediaId, ') ..write('mediaId: $mediaId, ')
..write('additionalMessageData: $additionalMessageData, ')
..write('mediaStored: $mediaStored, ') ..write('mediaStored: $mediaStored, ')
..write('mediaReopened: $mediaReopened, ') ..write('mediaReopened: $mediaReopened, ')
..write('downloadToken: $downloadToken, ') ..write('downloadToken: $downloadToken, ')
@ -3326,6 +3362,7 @@ class Message extends DataClass implements Insertable<Message> {
type, type,
content, content,
mediaId, mediaId,
$driftBlobEquality.hash(additionalMessageData),
mediaStored, mediaStored,
mediaReopened, mediaReopened,
$driftBlobEquality.hash(downloadToken), $driftBlobEquality.hash(downloadToken),
@ -3347,6 +3384,8 @@ class Message extends DataClass implements Insertable<Message> {
other.type == this.type && other.type == this.type &&
other.content == this.content && other.content == this.content &&
other.mediaId == this.mediaId && other.mediaId == this.mediaId &&
$driftBlobEquality.equals(
other.additionalMessageData, this.additionalMessageData) &&
other.mediaStored == this.mediaStored && other.mediaStored == this.mediaStored &&
other.mediaReopened == this.mediaReopened && other.mediaReopened == this.mediaReopened &&
$driftBlobEquality.equals(other.downloadToken, this.downloadToken) && $driftBlobEquality.equals(other.downloadToken, this.downloadToken) &&
@ -3367,6 +3406,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
final Value<MessageType> type; final Value<MessageType> type;
final Value<String?> content; final Value<String?> content;
final Value<String?> mediaId; final Value<String?> mediaId;
final Value<Uint8List?> additionalMessageData;
final Value<bool> mediaStored; final Value<bool> mediaStored;
final Value<bool> mediaReopened; final Value<bool> mediaReopened;
final Value<Uint8List?> downloadToken; final Value<Uint8List?> downloadToken;
@ -3386,6 +3426,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
this.type = const Value.absent(), this.type = const Value.absent(),
this.content = const Value.absent(), this.content = const Value.absent(),
this.mediaId = const Value.absent(), this.mediaId = const Value.absent(),
this.additionalMessageData = const Value.absent(),
this.mediaStored = const Value.absent(), this.mediaStored = const Value.absent(),
this.mediaReopened = const Value.absent(), this.mediaReopened = const Value.absent(),
this.downloadToken = const Value.absent(), this.downloadToken = const Value.absent(),
@ -3406,6 +3447,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
required MessageType type, required MessageType type,
this.content = const Value.absent(), this.content = const Value.absent(),
this.mediaId = const Value.absent(), this.mediaId = const Value.absent(),
this.additionalMessageData = const Value.absent(),
this.mediaStored = const Value.absent(), this.mediaStored = const Value.absent(),
this.mediaReopened = const Value.absent(), this.mediaReopened = const Value.absent(),
this.downloadToken = const Value.absent(), this.downloadToken = const Value.absent(),
@ -3428,6 +3470,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
Expression<String>? type, Expression<String>? type,
Expression<String>? content, Expression<String>? content,
Expression<String>? mediaId, Expression<String>? mediaId,
Expression<Uint8List>? additionalMessageData,
Expression<bool>? mediaStored, Expression<bool>? mediaStored,
Expression<bool>? mediaReopened, Expression<bool>? mediaReopened,
Expression<Uint8List>? downloadToken, Expression<Uint8List>? downloadToken,
@ -3448,6 +3491,8 @@ class MessagesCompanion extends UpdateCompanion<Message> {
if (type != null) 'type': type, if (type != null) 'type': type,
if (content != null) 'content': content, if (content != null) 'content': content,
if (mediaId != null) 'media_id': mediaId, if (mediaId != null) 'media_id': mediaId,
if (additionalMessageData != null)
'additional_message_data': additionalMessageData,
if (mediaStored != null) 'media_stored': mediaStored, if (mediaStored != null) 'media_stored': mediaStored,
if (mediaReopened != null) 'media_reopened': mediaReopened, if (mediaReopened != null) 'media_reopened': mediaReopened,
if (downloadToken != null) 'download_token': downloadToken, if (downloadToken != null) 'download_token': downloadToken,
@ -3471,6 +3516,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
Value<MessageType>? type, Value<MessageType>? type,
Value<String?>? content, Value<String?>? content,
Value<String?>? mediaId, Value<String?>? mediaId,
Value<Uint8List?>? additionalMessageData,
Value<bool>? mediaStored, Value<bool>? mediaStored,
Value<bool>? mediaReopened, Value<bool>? mediaReopened,
Value<Uint8List?>? downloadToken, Value<Uint8List?>? downloadToken,
@ -3490,6 +3536,8 @@ class MessagesCompanion extends UpdateCompanion<Message> {
type: type ?? this.type, type: type ?? this.type,
content: content ?? this.content, content: content ?? this.content,
mediaId: mediaId ?? this.mediaId, mediaId: mediaId ?? this.mediaId,
additionalMessageData:
additionalMessageData ?? this.additionalMessageData,
mediaStored: mediaStored ?? this.mediaStored, mediaStored: mediaStored ?? this.mediaStored,
mediaReopened: mediaReopened ?? this.mediaReopened, mediaReopened: mediaReopened ?? this.mediaReopened,
downloadToken: downloadToken ?? this.downloadToken, downloadToken: downloadToken ?? this.downloadToken,
@ -3527,6 +3575,10 @@ class MessagesCompanion extends UpdateCompanion<Message> {
if (mediaId.present) { if (mediaId.present) {
map['media_id'] = Variable<String>(mediaId.value); map['media_id'] = Variable<String>(mediaId.value);
} }
if (additionalMessageData.present) {
map['additional_message_data'] =
Variable<Uint8List>(additionalMessageData.value);
}
if (mediaStored.present) { if (mediaStored.present) {
map['media_stored'] = Variable<bool>(mediaStored.value); map['media_stored'] = Variable<bool>(mediaStored.value);
} }
@ -3575,6 +3627,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
..write('type: $type, ') ..write('type: $type, ')
..write('content: $content, ') ..write('content: $content, ')
..write('mediaId: $mediaId, ') ..write('mediaId: $mediaId, ')
..write('additionalMessageData: $additionalMessageData, ')
..write('mediaStored: $mediaStored, ') ..write('mediaStored: $mediaStored, ')
..write('mediaReopened: $mediaReopened, ') ..write('mediaReopened: $mediaReopened, ')
..write('downloadToken: $downloadToken, ') ..write('downloadToken: $downloadToken, ')
@ -3621,7 +3674,10 @@ class $MessageHistoriesTable extends MessageHistories
@override @override
late final GeneratedColumn<int> contactId = GeneratedColumn<int>( late final GeneratedColumn<int> contactId = GeneratedColumn<int>(
'contact_id', aliasedName, true, 'contact_id', aliasedName, true,
type: DriftSqlType.int, requiredDuringInsert: false); type: DriftSqlType.int,
requiredDuringInsert: false,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES contacts (user_id) ON DELETE CASCADE'));
static const VerificationMeta _contentMeta = static const VerificationMeta _contentMeta =
const VerificationMeta('content'); const VerificationMeta('content');
@override @override
@ -6964,7 +7020,10 @@ class $MessageActionsTable extends MessageActions
@override @override
late final GeneratedColumn<int> contactId = GeneratedColumn<int>( late final GeneratedColumn<int> contactId = GeneratedColumn<int>(
'contact_id', aliasedName, false, 'contact_id', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true); type: DriftSqlType.int,
requiredDuringInsert: true,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES contacts (user_id) ON DELETE CASCADE'));
@override @override
late final GeneratedColumnWithTypeConverter<MessageActionType, String> type = late final GeneratedColumnWithTypeConverter<MessageActionType, String> type =
GeneratedColumn<String>('type', aliasedName, false, GeneratedColumn<String>('type', aliasedName, false,
@ -7837,6 +7896,13 @@ abstract class _$TwonlyDB extends GeneratedDatabase {
TableUpdate('message_histories', kind: UpdateKind.delete), TableUpdate('message_histories', kind: UpdateKind.delete),
], ],
), ),
WritePropagation(
on: TableUpdateQuery.onTableName('contacts',
limitUpdateKind: UpdateKind.delete),
result: [
TableUpdate('message_histories', kind: UpdateKind.delete),
],
),
WritePropagation( WritePropagation(
on: TableUpdateQuery.onTableName('messages', on: TableUpdateQuery.onTableName('messages',
limitUpdateKind: UpdateKind.delete), limitUpdateKind: UpdateKind.delete),
@ -7894,6 +7960,13 @@ abstract class _$TwonlyDB extends GeneratedDatabase {
TableUpdate('message_actions', kind: UpdateKind.delete), TableUpdate('message_actions', kind: UpdateKind.delete),
], ],
), ),
WritePropagation(
on: TableUpdateQuery.onTableName('contacts',
limitUpdateKind: UpdateKind.delete),
result: [
TableUpdate('message_actions', kind: UpdateKind.delete),
],
),
WritePropagation( WritePropagation(
on: TableUpdateQuery.onTableName('groups', on: TableUpdateQuery.onTableName('groups',
limitUpdateKind: UpdateKind.delete), limitUpdateKind: UpdateKind.delete),
@ -7955,6 +8028,23 @@ final class $$ContactsTableReferences
manager.$state.copyWith(prefetchedData: cache)); manager.$state.copyWith(prefetchedData: cache));
} }
static MultiTypedResultKey<$MessageHistoriesTable, List<MessageHistory>>
_messageHistoriesRefsTable(_$TwonlyDB db) =>
MultiTypedResultKey.fromTable(db.messageHistories,
aliasName: $_aliasNameGenerator(
db.contacts.userId, db.messageHistories.contactId));
$$MessageHistoriesTableProcessedTableManager get messageHistoriesRefs {
final manager =
$$MessageHistoriesTableTableManager($_db, $_db.messageHistories).filter(
(f) => f.contactId.userId.sqlEquals($_itemColumn<int>('user_id')!));
final cache =
$_typedResult.readTableOrNull(_messageHistoriesRefsTable($_db));
return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: cache));
}
static MultiTypedResultKey<$ReactionsTable, List<Reaction>> static MultiTypedResultKey<$ReactionsTable, List<Reaction>>
_reactionsRefsTable(_$TwonlyDB db) => MultiTypedResultKey.fromTable( _reactionsRefsTable(_$TwonlyDB db) => MultiTypedResultKey.fromTable(
db.reactions, db.reactions,
@ -8041,6 +8131,22 @@ final class $$ContactsTableReferences
manager.$state.copyWith(prefetchedData: cache)); manager.$state.copyWith(prefetchedData: cache));
} }
static MultiTypedResultKey<$MessageActionsTable, List<MessageAction>>
_messageActionsRefsTable(_$TwonlyDB db) =>
MultiTypedResultKey.fromTable(db.messageActions,
aliasName: $_aliasNameGenerator(
db.contacts.userId, db.messageActions.contactId));
$$MessageActionsTableProcessedTableManager get messageActionsRefs {
final manager = $$MessageActionsTableTableManager($_db, $_db.messageActions)
.filter(
(f) => f.contactId.userId.sqlEquals($_itemColumn<int>('user_id')!));
final cache = $_typedResult.readTableOrNull(_messageActionsRefsTable($_db));
return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: cache));
}
static MultiTypedResultKey<$GroupHistoriesTable, List<GroupHistory>> static MultiTypedResultKey<$GroupHistoriesTable, List<GroupHistory>>
_groupHistoriesRefsTable(_$TwonlyDB db) => _groupHistoriesRefsTable(_$TwonlyDB db) =>
MultiTypedResultKey.fromTable(db.groupHistories, MultiTypedResultKey.fromTable(db.groupHistories,
@ -8130,6 +8236,27 @@ class $$ContactsTableFilterComposer
return f(composer); return f(composer);
} }
Expression<bool> messageHistoriesRefs(
Expression<bool> Function($$MessageHistoriesTableFilterComposer f) f) {
final $$MessageHistoriesTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.userId,
referencedTable: $db.messageHistories,
getReferencedColumn: (t) => t.contactId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$MessageHistoriesTableFilterComposer(
$db: $db,
$table: $db.messageHistories,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return f(composer);
}
Expression<bool> reactionsRefs( Expression<bool> reactionsRefs(
Expression<bool> Function($$ReactionsTableFilterComposer f) f) { Expression<bool> Function($$ReactionsTableFilterComposer f) f) {
final $$ReactionsTableFilterComposer composer = $composerBuilder( final $$ReactionsTableFilterComposer composer = $composerBuilder(
@ -8239,6 +8366,27 @@ class $$ContactsTableFilterComposer
return f(composer); return f(composer);
} }
Expression<bool> messageActionsRefs(
Expression<bool> Function($$MessageActionsTableFilterComposer f) f) {
final $$MessageActionsTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.userId,
referencedTable: $db.messageActions,
getReferencedColumn: (t) => t.contactId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$MessageActionsTableFilterComposer(
$db: $db,
$table: $db.messageActions,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return f(composer);
}
Expression<bool> groupHistoriesRefs( Expression<bool> groupHistoriesRefs(
Expression<bool> Function($$GroupHistoriesTableFilterComposer f) f) { Expression<bool> Function($$GroupHistoriesTableFilterComposer f) f) {
final $$GroupHistoriesTableFilterComposer composer = $composerBuilder( final $$GroupHistoriesTableFilterComposer composer = $composerBuilder(
@ -8383,6 +8531,27 @@ class $$ContactsTableAnnotationComposer
return f(composer); return f(composer);
} }
Expression<T> messageHistoriesRefs<T extends Object>(
Expression<T> Function($$MessageHistoriesTableAnnotationComposer a) f) {
final $$MessageHistoriesTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.userId,
referencedTable: $db.messageHistories,
getReferencedColumn: (t) => t.contactId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$MessageHistoriesTableAnnotationComposer(
$db: $db,
$table: $db.messageHistories,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return f(composer);
}
Expression<T> reactionsRefs<T extends Object>( Expression<T> reactionsRefs<T extends Object>(
Expression<T> Function($$ReactionsTableAnnotationComposer a) f) { Expression<T> Function($$ReactionsTableAnnotationComposer a) f) {
final $$ReactionsTableAnnotationComposer composer = $composerBuilder( final $$ReactionsTableAnnotationComposer composer = $composerBuilder(
@ -8493,6 +8662,27 @@ class $$ContactsTableAnnotationComposer
return f(composer); return f(composer);
} }
Expression<T> messageActionsRefs<T extends Object>(
Expression<T> Function($$MessageActionsTableAnnotationComposer a) f) {
final $$MessageActionsTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.userId,
referencedTable: $db.messageActions,
getReferencedColumn: (t) => t.contactId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$MessageActionsTableAnnotationComposer(
$db: $db,
$table: $db.messageActions,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return f(composer);
}
Expression<T> groupHistoriesRefs<T extends Object>( Expression<T> groupHistoriesRefs<T extends Object>(
Expression<T> Function($$GroupHistoriesTableAnnotationComposer a) f) { Expression<T> Function($$GroupHistoriesTableAnnotationComposer a) f) {
final $$GroupHistoriesTableAnnotationComposer composer = $composerBuilder( final $$GroupHistoriesTableAnnotationComposer composer = $composerBuilder(
@ -8528,11 +8718,13 @@ class $$ContactsTableTableManager extends RootTableManager<
Contact, Contact,
PrefetchHooks Function( PrefetchHooks Function(
{bool messagesRefs, {bool messagesRefs,
bool messageHistoriesRefs,
bool reactionsRefs, bool reactionsRefs,
bool groupMembersRefs, bool groupMembersRefs,
bool receiptsRefs, bool receiptsRefs,
bool signalContactPreKeysRefs, bool signalContactPreKeysRefs,
bool signalContactSignedPreKeysRefs, bool signalContactSignedPreKeysRefs,
bool messageActionsRefs,
bool groupHistoriesRefs})> { bool groupHistoriesRefs})> {
$$ContactsTableTableManager(_$TwonlyDB db, $ContactsTable table) $$ContactsTableTableManager(_$TwonlyDB db, $ContactsTable table)
: super(TableManagerState( : super(TableManagerState(
@ -8610,22 +8802,26 @@ class $$ContactsTableTableManager extends RootTableManager<
.toList(), .toList(),
prefetchHooksCallback: ( prefetchHooksCallback: (
{messagesRefs = false, {messagesRefs = false,
messageHistoriesRefs = false,
reactionsRefs = false, reactionsRefs = false,
groupMembersRefs = false, groupMembersRefs = false,
receiptsRefs = false, receiptsRefs = false,
signalContactPreKeysRefs = false, signalContactPreKeysRefs = false,
signalContactSignedPreKeysRefs = false, signalContactSignedPreKeysRefs = false,
messageActionsRefs = false,
groupHistoriesRefs = false}) { groupHistoriesRefs = false}) {
return PrefetchHooks( return PrefetchHooks(
db: db, db: db,
explicitlyWatchedTables: [ explicitlyWatchedTables: [
if (messagesRefs) db.messages, if (messagesRefs) db.messages,
if (messageHistoriesRefs) db.messageHistories,
if (reactionsRefs) db.reactions, if (reactionsRefs) db.reactions,
if (groupMembersRefs) db.groupMembers, if (groupMembersRefs) db.groupMembers,
if (receiptsRefs) db.receipts, if (receiptsRefs) db.receipts,
if (signalContactPreKeysRefs) db.signalContactPreKeys, if (signalContactPreKeysRefs) db.signalContactPreKeys,
if (signalContactSignedPreKeysRefs) if (signalContactSignedPreKeysRefs)
db.signalContactSignedPreKeys, db.signalContactSignedPreKeys,
if (messageActionsRefs) db.messageActions,
if (groupHistoriesRefs) db.groupHistories if (groupHistoriesRefs) db.groupHistories
], ],
addJoins: null, addJoins: null,
@ -8643,6 +8839,19 @@ class $$ContactsTableTableManager extends RootTableManager<
(item, referencedItems) => referencedItems (item, referencedItems) => referencedItems
.where((e) => e.senderId == item.userId), .where((e) => e.senderId == item.userId),
typedResults: items), typedResults: items),
if (messageHistoriesRefs)
await $_getPrefetchedData<Contact, $ContactsTable,
MessageHistory>(
currentTable: table,
referencedTable: $$ContactsTableReferences
._messageHistoriesRefsTable(db),
managerFromTypedResult: (p0) =>
$$ContactsTableReferences(db, table, p0)
.messageHistoriesRefs,
referencedItemsForCurrentItem:
(item, referencedItems) => referencedItems
.where((e) => e.contactId == item.userId),
typedResults: items),
if (reactionsRefs) if (reactionsRefs)
await $_getPrefetchedData<Contact, $ContactsTable, await $_getPrefetchedData<Contact, $ContactsTable,
Reaction>( Reaction>(
@ -8707,6 +8916,19 @@ class $$ContactsTableTableManager extends RootTableManager<
(item, referencedItems) => referencedItems (item, referencedItems) => referencedItems
.where((e) => e.contactId == item.userId), .where((e) => e.contactId == item.userId),
typedResults: items), typedResults: items),
if (messageActionsRefs)
await $_getPrefetchedData<Contact, $ContactsTable,
MessageAction>(
currentTable: table,
referencedTable: $$ContactsTableReferences
._messageActionsRefsTable(db),
managerFromTypedResult: (p0) =>
$$ContactsTableReferences(db, table, p0)
.messageActionsRefs,
referencedItemsForCurrentItem:
(item, referencedItems) => referencedItems
.where((e) => e.contactId == item.userId),
typedResults: items),
if (groupHistoriesRefs) if (groupHistoriesRefs)
await $_getPrefetchedData<Contact, $ContactsTable, await $_getPrefetchedData<Contact, $ContactsTable,
GroupHistory>( GroupHistory>(
@ -8740,11 +8962,13 @@ typedef $$ContactsTableProcessedTableManager = ProcessedTableManager<
Contact, Contact,
PrefetchHooks Function( PrefetchHooks Function(
{bool messagesRefs, {bool messagesRefs,
bool messageHistoriesRefs,
bool reactionsRefs, bool reactionsRefs,
bool groupMembersRefs, bool groupMembersRefs,
bool receiptsRefs, bool receiptsRefs,
bool signalContactPreKeysRefs, bool signalContactPreKeysRefs,
bool signalContactSignedPreKeysRefs, bool signalContactSignedPreKeysRefs,
bool messageActionsRefs,
bool groupHistoriesRefs})>; bool groupHistoriesRefs})>;
typedef $$GroupsTableCreateCompanionBuilder = GroupsCompanion Function({ typedef $$GroupsTableCreateCompanionBuilder = GroupsCompanion Function({
required String groupId, required String groupId,
@ -9927,6 +10151,7 @@ typedef $$MessagesTableCreateCompanionBuilder = MessagesCompanion Function({
required MessageType type, required MessageType type,
Value<String?> content, Value<String?> content,
Value<String?> mediaId, Value<String?> mediaId,
Value<Uint8List?> additionalMessageData,
Value<bool> mediaStored, Value<bool> mediaStored,
Value<bool> mediaReopened, Value<bool> mediaReopened,
Value<Uint8List?> downloadToken, Value<Uint8List?> downloadToken,
@ -9947,6 +10172,7 @@ typedef $$MessagesTableUpdateCompanionBuilder = MessagesCompanion Function({
Value<MessageType> type, Value<MessageType> type,
Value<String?> content, Value<String?> content,
Value<String?> mediaId, Value<String?> mediaId,
Value<Uint8List?> additionalMessageData,
Value<bool> mediaStored, Value<bool> mediaStored,
Value<bool> mediaReopened, Value<bool> mediaReopened,
Value<Uint8List?> downloadToken, Value<Uint8List?> downloadToken,
@ -10096,6 +10322,10 @@ class $$MessagesTableFilterComposer
ColumnFilters<String> get content => $composableBuilder( ColumnFilters<String> get content => $composableBuilder(
column: $table.content, builder: (column) => ColumnFilters(column)); column: $table.content, builder: (column) => ColumnFilters(column));
ColumnFilters<Uint8List> get additionalMessageData => $composableBuilder(
column: $table.additionalMessageData,
builder: (column) => ColumnFilters(column));
ColumnFilters<bool> get mediaStored => $composableBuilder( ColumnFilters<bool> get mediaStored => $composableBuilder(
column: $table.mediaStored, builder: (column) => ColumnFilters(column)); column: $table.mediaStored, builder: (column) => ColumnFilters(column));
@ -10294,6 +10524,10 @@ class $$MessagesTableOrderingComposer
ColumnOrderings<String> get content => $composableBuilder( ColumnOrderings<String> get content => $composableBuilder(
column: $table.content, builder: (column) => ColumnOrderings(column)); column: $table.content, builder: (column) => ColumnOrderings(column));
ColumnOrderings<Uint8List> get additionalMessageData => $composableBuilder(
column: $table.additionalMessageData,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<bool> get mediaStored => $composableBuilder( ColumnOrderings<bool> get mediaStored => $composableBuilder(
column: $table.mediaStored, builder: (column) => ColumnOrderings(column)); column: $table.mediaStored, builder: (column) => ColumnOrderings(column));
@ -10410,6 +10644,9 @@ class $$MessagesTableAnnotationComposer
GeneratedColumn<String> get content => GeneratedColumn<String> get content =>
$composableBuilder(column: $table.content, builder: (column) => column); $composableBuilder(column: $table.content, builder: (column) => column);
GeneratedColumn<Uint8List> get additionalMessageData => $composableBuilder(
column: $table.additionalMessageData, builder: (column) => column);
GeneratedColumn<bool> get mediaStored => $composableBuilder( GeneratedColumn<bool> get mediaStored => $composableBuilder(
column: $table.mediaStored, builder: (column) => column); column: $table.mediaStored, builder: (column) => column);
@ -10624,6 +10861,7 @@ class $$MessagesTableTableManager extends RootTableManager<
Value<MessageType> type = const Value.absent(), Value<MessageType> type = const Value.absent(),
Value<String?> content = const Value.absent(), Value<String?> content = const Value.absent(),
Value<String?> mediaId = const Value.absent(), Value<String?> mediaId = const Value.absent(),
Value<Uint8List?> additionalMessageData = const Value.absent(),
Value<bool> mediaStored = const Value.absent(), Value<bool> mediaStored = const Value.absent(),
Value<bool> mediaReopened = const Value.absent(), Value<bool> mediaReopened = const Value.absent(),
Value<Uint8List?> downloadToken = const Value.absent(), Value<Uint8List?> downloadToken = const Value.absent(),
@ -10644,6 +10882,7 @@ class $$MessagesTableTableManager extends RootTableManager<
type: type, type: type,
content: content, content: content,
mediaId: mediaId, mediaId: mediaId,
additionalMessageData: additionalMessageData,
mediaStored: mediaStored, mediaStored: mediaStored,
mediaReopened: mediaReopened, mediaReopened: mediaReopened,
downloadToken: downloadToken, downloadToken: downloadToken,
@ -10664,6 +10903,7 @@ class $$MessagesTableTableManager extends RootTableManager<
required MessageType type, required MessageType type,
Value<String?> content = const Value.absent(), Value<String?> content = const Value.absent(),
Value<String?> mediaId = const Value.absent(), Value<String?> mediaId = const Value.absent(),
Value<Uint8List?> additionalMessageData = const Value.absent(),
Value<bool> mediaStored = const Value.absent(), Value<bool> mediaStored = const Value.absent(),
Value<bool> mediaReopened = const Value.absent(), Value<bool> mediaReopened = const Value.absent(),
Value<Uint8List?> downloadToken = const Value.absent(), Value<Uint8List?> downloadToken = const Value.absent(),
@ -10684,6 +10924,7 @@ class $$MessagesTableTableManager extends RootTableManager<
type: type, type: type,
content: content, content: content,
mediaId: mediaId, mediaId: mediaId,
additionalMessageData: additionalMessageData,
mediaStored: mediaStored, mediaStored: mediaStored,
mediaReopened: mediaReopened, mediaReopened: mediaReopened,
downloadToken: downloadToken, downloadToken: downloadToken,
@ -10878,6 +11119,21 @@ final class $$MessageHistoriesTableReferences
return ProcessedTableManager( return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item])); manager.$state.copyWith(prefetchedData: [item]));
} }
static $ContactsTable _contactIdTable(_$TwonlyDB db) =>
db.contacts.createAlias($_aliasNameGenerator(
db.messageHistories.contactId, db.contacts.userId));
$$ContactsTableProcessedTableManager? get contactId {
final $_column = $_itemColumn<int>('contact_id');
if ($_column == null) return null;
final manager = $$ContactsTableTableManager($_db, $_db.contacts)
.filter((f) => f.userId.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_contactIdTable($_db));
if (item == null) return manager;
return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]));
}
} }
class $$MessageHistoriesTableFilterComposer class $$MessageHistoriesTableFilterComposer
@ -10892,9 +11148,6 @@ class $$MessageHistoriesTableFilterComposer
ColumnFilters<int> get id => $composableBuilder( ColumnFilters<int> get id => $composableBuilder(
column: $table.id, builder: (column) => ColumnFilters(column)); column: $table.id, builder: (column) => ColumnFilters(column));
ColumnFilters<int> get contactId => $composableBuilder(
column: $table.contactId, builder: (column) => ColumnFilters(column));
ColumnFilters<String> get content => $composableBuilder( ColumnFilters<String> get content => $composableBuilder(
column: $table.content, builder: (column) => ColumnFilters(column)); column: $table.content, builder: (column) => ColumnFilters(column));
@ -10920,6 +11173,26 @@ class $$MessageHistoriesTableFilterComposer
)); ));
return composer; return composer;
} }
$$ContactsTableFilterComposer get contactId {
final $$ContactsTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.contactId,
referencedTable: $db.contacts,
getReferencedColumn: (t) => t.userId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$ContactsTableFilterComposer(
$db: $db,
$table: $db.contacts,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
} }
class $$MessageHistoriesTableOrderingComposer class $$MessageHistoriesTableOrderingComposer
@ -10934,9 +11207,6 @@ class $$MessageHistoriesTableOrderingComposer
ColumnOrderings<int> get id => $composableBuilder( ColumnOrderings<int> get id => $composableBuilder(
column: $table.id, builder: (column) => ColumnOrderings(column)); column: $table.id, builder: (column) => ColumnOrderings(column));
ColumnOrderings<int> get contactId => $composableBuilder(
column: $table.contactId, builder: (column) => ColumnOrderings(column));
ColumnOrderings<String> get content => $composableBuilder( ColumnOrderings<String> get content => $composableBuilder(
column: $table.content, builder: (column) => ColumnOrderings(column)); column: $table.content, builder: (column) => ColumnOrderings(column));
@ -10962,6 +11232,26 @@ class $$MessageHistoriesTableOrderingComposer
)); ));
return composer; return composer;
} }
$$ContactsTableOrderingComposer get contactId {
final $$ContactsTableOrderingComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.contactId,
referencedTable: $db.contacts,
getReferencedColumn: (t) => t.userId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$ContactsTableOrderingComposer(
$db: $db,
$table: $db.contacts,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
} }
class $$MessageHistoriesTableAnnotationComposer class $$MessageHistoriesTableAnnotationComposer
@ -10976,9 +11266,6 @@ class $$MessageHistoriesTableAnnotationComposer
GeneratedColumn<int> get id => GeneratedColumn<int> get id =>
$composableBuilder(column: $table.id, builder: (column) => column); $composableBuilder(column: $table.id, builder: (column) => column);
GeneratedColumn<int> get contactId =>
$composableBuilder(column: $table.contactId, builder: (column) => column);
GeneratedColumn<String> get content => GeneratedColumn<String> get content =>
$composableBuilder(column: $table.content, builder: (column) => column); $composableBuilder(column: $table.content, builder: (column) => column);
@ -11004,6 +11291,26 @@ class $$MessageHistoriesTableAnnotationComposer
)); ));
return composer; return composer;
} }
$$ContactsTableAnnotationComposer get contactId {
final $$ContactsTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.contactId,
referencedTable: $db.contacts,
getReferencedColumn: (t) => t.userId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$ContactsTableAnnotationComposer(
$db: $db,
$table: $db.contacts,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
} }
class $$MessageHistoriesTableTableManager extends RootTableManager< class $$MessageHistoriesTableTableManager extends RootTableManager<
@ -11017,7 +11324,7 @@ class $$MessageHistoriesTableTableManager extends RootTableManager<
$$MessageHistoriesTableUpdateCompanionBuilder, $$MessageHistoriesTableUpdateCompanionBuilder,
(MessageHistory, $$MessageHistoriesTableReferences), (MessageHistory, $$MessageHistoriesTableReferences),
MessageHistory, MessageHistory,
PrefetchHooks Function({bool messageId})> { PrefetchHooks Function({bool messageId, bool contactId})> {
$$MessageHistoriesTableTableManager( $$MessageHistoriesTableTableManager(
_$TwonlyDB db, $MessageHistoriesTable table) _$TwonlyDB db, $MessageHistoriesTable table)
: super(TableManagerState( : super(TableManagerState(
@ -11063,7 +11370,7 @@ class $$MessageHistoriesTableTableManager extends RootTableManager<
$$MessageHistoriesTableReferences(db, table, e) $$MessageHistoriesTableReferences(db, table, e)
)) ))
.toList(), .toList(),
prefetchHooksCallback: ({messageId = false}) { prefetchHooksCallback: ({messageId = false, contactId = false}) {
return PrefetchHooks( return PrefetchHooks(
db: db, db: db,
explicitlyWatchedTables: [], explicitlyWatchedTables: [],
@ -11091,6 +11398,17 @@ class $$MessageHistoriesTableTableManager extends RootTableManager<
.messageId, .messageId,
) as T; ) as T;
} }
if (contactId) {
state = state.withJoin(
currentTable: table,
currentColumn: table.contactId,
referencedTable:
$$MessageHistoriesTableReferences._contactIdTable(db),
referencedColumn: $$MessageHistoriesTableReferences
._contactIdTable(db)
.userId,
) as T;
}
return state; return state;
}, },
@ -11113,7 +11431,7 @@ typedef $$MessageHistoriesTableProcessedTableManager = ProcessedTableManager<
$$MessageHistoriesTableUpdateCompanionBuilder, $$MessageHistoriesTableUpdateCompanionBuilder,
(MessageHistory, $$MessageHistoriesTableReferences), (MessageHistory, $$MessageHistoriesTableReferences),
MessageHistory, MessageHistory,
PrefetchHooks Function({bool messageId})>; PrefetchHooks Function({bool messageId, bool contactId})>;
typedef $$ReactionsTableCreateCompanionBuilder = ReactionsCompanion Function({ typedef $$ReactionsTableCreateCompanionBuilder = ReactionsCompanion Function({
required String messageId, required String messageId,
required String emoji, required String emoji,
@ -13581,6 +13899,21 @@ final class $$MessageActionsTableReferences
return ProcessedTableManager( return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item])); manager.$state.copyWith(prefetchedData: [item]));
} }
static $ContactsTable _contactIdTable(_$TwonlyDB db) =>
db.contacts.createAlias($_aliasNameGenerator(
db.messageActions.contactId, db.contacts.userId));
$$ContactsTableProcessedTableManager get contactId {
final $_column = $_itemColumn<int>('contact_id')!;
final manager = $$ContactsTableTableManager($_db, $_db.contacts)
.filter((f) => f.userId.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_contactIdTable($_db));
if (item == null) return manager;
return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]));
}
} }
class $$MessageActionsTableFilterComposer class $$MessageActionsTableFilterComposer
@ -13592,9 +13925,6 @@ class $$MessageActionsTableFilterComposer
super.$addJoinBuilderToRootComposer, super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer, super.$removeJoinBuilderFromRootComposer,
}); });
ColumnFilters<int> get contactId => $composableBuilder(
column: $table.contactId, builder: (column) => ColumnFilters(column));
ColumnWithTypeConverterFilters<MessageActionType, MessageActionType, String> ColumnWithTypeConverterFilters<MessageActionType, MessageActionType, String>
get type => $composableBuilder( get type => $composableBuilder(
column: $table.type, column: $table.type,
@ -13622,6 +13952,26 @@ class $$MessageActionsTableFilterComposer
)); ));
return composer; return composer;
} }
$$ContactsTableFilterComposer get contactId {
final $$ContactsTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.contactId,
referencedTable: $db.contacts,
getReferencedColumn: (t) => t.userId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$ContactsTableFilterComposer(
$db: $db,
$table: $db.contacts,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
} }
class $$MessageActionsTableOrderingComposer class $$MessageActionsTableOrderingComposer
@ -13633,9 +13983,6 @@ class $$MessageActionsTableOrderingComposer
super.$addJoinBuilderToRootComposer, super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer, super.$removeJoinBuilderFromRootComposer,
}); });
ColumnOrderings<int> get contactId => $composableBuilder(
column: $table.contactId, builder: (column) => ColumnOrderings(column));
ColumnOrderings<String> get type => $composableBuilder( ColumnOrderings<String> get type => $composableBuilder(
column: $table.type, builder: (column) => ColumnOrderings(column)); column: $table.type, builder: (column) => ColumnOrderings(column));
@ -13661,6 +14008,26 @@ class $$MessageActionsTableOrderingComposer
)); ));
return composer; return composer;
} }
$$ContactsTableOrderingComposer get contactId {
final $$ContactsTableOrderingComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.contactId,
referencedTable: $db.contacts,
getReferencedColumn: (t) => t.userId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$ContactsTableOrderingComposer(
$db: $db,
$table: $db.contacts,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
} }
class $$MessageActionsTableAnnotationComposer class $$MessageActionsTableAnnotationComposer
@ -13672,9 +14039,6 @@ class $$MessageActionsTableAnnotationComposer
super.$addJoinBuilderToRootComposer, super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer, super.$removeJoinBuilderFromRootComposer,
}); });
GeneratedColumn<int> get contactId =>
$composableBuilder(column: $table.contactId, builder: (column) => column);
GeneratedColumnWithTypeConverter<MessageActionType, String> get type => GeneratedColumnWithTypeConverter<MessageActionType, String> get type =>
$composableBuilder(column: $table.type, builder: (column) => column); $composableBuilder(column: $table.type, builder: (column) => column);
@ -13700,6 +14064,26 @@ class $$MessageActionsTableAnnotationComposer
)); ));
return composer; return composer;
} }
$$ContactsTableAnnotationComposer get contactId {
final $$ContactsTableAnnotationComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.contactId,
referencedTable: $db.contacts,
getReferencedColumn: (t) => t.userId,
builder: (joinBuilder,
{$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer}) =>
$$ContactsTableAnnotationComposer(
$db: $db,
$table: $db.contacts,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
));
return composer;
}
} }
class $$MessageActionsTableTableManager extends RootTableManager< class $$MessageActionsTableTableManager extends RootTableManager<
@ -13713,7 +14097,7 @@ class $$MessageActionsTableTableManager extends RootTableManager<
$$MessageActionsTableUpdateCompanionBuilder, $$MessageActionsTableUpdateCompanionBuilder,
(MessageAction, $$MessageActionsTableReferences), (MessageAction, $$MessageActionsTableReferences),
MessageAction, MessageAction,
PrefetchHooks Function({bool messageId})> { PrefetchHooks Function({bool messageId, bool contactId})> {
$$MessageActionsTableTableManager(_$TwonlyDB db, $MessageActionsTable table) $$MessageActionsTableTableManager(_$TwonlyDB db, $MessageActionsTable table)
: super(TableManagerState( : super(TableManagerState(
db: db, db: db,
@ -13758,7 +14142,7 @@ class $$MessageActionsTableTableManager extends RootTableManager<
$$MessageActionsTableReferences(db, table, e) $$MessageActionsTableReferences(db, table, e)
)) ))
.toList(), .toList(),
prefetchHooksCallback: ({messageId = false}) { prefetchHooksCallback: ({messageId = false, contactId = false}) {
return PrefetchHooks( return PrefetchHooks(
db: db, db: db,
explicitlyWatchedTables: [], explicitlyWatchedTables: [],
@ -13786,6 +14170,17 @@ class $$MessageActionsTableTableManager extends RootTableManager<
.messageId, .messageId,
) as T; ) as T;
} }
if (contactId) {
state = state.withJoin(
currentTable: table,
currentColumn: table.contactId,
referencedTable:
$$MessageActionsTableReferences._contactIdTable(db),
referencedColumn: $$MessageActionsTableReferences
._contactIdTable(db)
.userId,
) as T;
}
return state; return state;
}, },
@ -13808,7 +14203,7 @@ typedef $$MessageActionsTableProcessedTableManager = ProcessedTableManager<
$$MessageActionsTableUpdateCompanionBuilder, $$MessageActionsTableUpdateCompanionBuilder,
(MessageAction, $$MessageActionsTableReferences), (MessageAction, $$MessageActionsTableReferences),
MessageAction, MessageAction,
PrefetchHooks Function({bool messageId})>; PrefetchHooks Function({bool messageId, bool contactId})>;
typedef $$GroupHistoriesTableCreateCompanionBuilder = GroupHistoriesCompanion typedef $$GroupHistoriesTableCreateCompanionBuilder = GroupHistoriesCompanion
Function({ Function({
required String groupHistoryId, required String groupHistoryId,

View file

@ -2804,12 +2804,439 @@ i1.GeneratedColumn<DateTime> _column_104(String aliasedName) =>
i1.GeneratedColumn<DateTime>( i1.GeneratedColumn<DateTime>(
'mark_for_retry_after_accepted', aliasedName, true, 'mark_for_retry_after_accepted', aliasedName, true,
type: i1.DriftSqlType.dateTime); type: i1.DriftSqlType.dateTime);
final class Schema7 extends i0.VersionedSchema {
Schema7({required super.database}) : super(version: 7);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
contacts,
groups,
mediaFiles,
messages,
messageHistories,
reactions,
groupMembers,
receipts,
receivedReceipts,
signalIdentityKeyStores,
signalPreKeyStores,
signalSenderKeyStores,
signalSessionStores,
signalContactPreKeys,
signalContactSignedPreKeys,
messageActions,
groupHistories,
];
late final Shape0 contacts = Shape0(
source: i0.VersionedTable(
entityName: 'contacts',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(user_id)',
],
columns: [
_column_0,
_column_1,
_column_2,
_column_3,
_column_4,
_column_5,
_column_6,
_column_7,
_column_8,
_column_9,
_column_10,
_column_11,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape17 groups = Shape17(
source: i0.VersionedTable(
entityName: 'groups',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(group_id)',
],
columns: [
_column_13,
_column_14,
_column_15,
_column_16,
_column_17,
_column_18,
_column_19,
_column_20,
_column_21,
_column_22,
_column_23,
_column_24,
_column_100,
_column_25,
_column_26,
_column_27,
_column_12,
_column_28,
_column_29,
_column_30,
_column_31,
_column_32,
_column_33,
_column_34,
_column_35,
],
attachedDatabase: database,
),
alias: null);
late final Shape18 mediaFiles = Shape18(
source: i0.VersionedTable(
entityName: 'media_files',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(media_id)',
],
columns: [
_column_36,
_column_37,
_column_38,
_column_39,
_column_40,
_column_41,
_column_42,
_column_43,
_column_44,
_column_45,
_column_46,
_column_47,
_column_48,
_column_49,
_column_102,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape21 messages = Shape21(
source: i0.VersionedTable(
entityName: 'messages',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(message_id)',
],
columns: [
_column_50,
_column_51,
_column_52,
_column_37,
_column_53,
_column_54,
_column_105,
_column_55,
_column_56,
_column_46,
_column_57,
_column_58,
_column_59,
_column_60,
_column_12,
_column_61,
_column_62,
_column_63,
],
attachedDatabase: database,
),
alias: null);
late final Shape4 messageHistories = Shape4(
source: i0.VersionedTable(
entityName: 'message_histories',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(id)',
],
columns: [
_column_64,
_column_65,
_column_66,
_column_53,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape5 reactions = Shape5(
source: i0.VersionedTable(
entityName: 'reactions',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(message_id, sender_id, emoji)',
],
columns: [
_column_65,
_column_67,
_column_68,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape6 groupMembers = Shape6(
source: i0.VersionedTable(
entityName: 'group_members',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(group_id, contact_id)',
],
columns: [
_column_50,
_column_69,
_column_70,
_column_71,
_column_72,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape20 receipts = Shape20(
source: i0.VersionedTable(
entityName: 'receipts',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(receipt_id)',
],
columns: [
_column_73,
_column_74,
_column_75,
_column_76,
_column_77,
_column_103,
_column_104,
_column_78,
_column_79,
_column_80,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape8 receivedReceipts = Shape8(
source: i0.VersionedTable(
entityName: 'received_receipts',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(receipt_id)',
],
columns: [
_column_73,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape9 signalIdentityKeyStores = Shape9(
source: i0.VersionedTable(
entityName: 'signal_identity_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(device_id, name)',
],
columns: [
_column_81,
_column_82,
_column_83,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape10 signalPreKeyStores = Shape10(
source: i0.VersionedTable(
entityName: 'signal_pre_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(pre_key_id)',
],
columns: [
_column_84,
_column_85,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape11 signalSenderKeyStores = Shape11(
source: i0.VersionedTable(
entityName: 'signal_sender_key_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(sender_key_name)',
],
columns: [
_column_86,
_column_87,
],
attachedDatabase: database,
),
alias: null);
late final Shape12 signalSessionStores = Shape12(
source: i0.VersionedTable(
entityName: 'signal_session_stores',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(device_id, name)',
],
columns: [
_column_81,
_column_82,
_column_88,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape13 signalContactPreKeys = Shape13(
source: i0.VersionedTable(
entityName: 'signal_contact_pre_keys',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(contact_id, pre_key_id)',
],
columns: [
_column_74,
_column_84,
_column_85,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape14 signalContactSignedPreKeys = Shape14(
source: i0.VersionedTable(
entityName: 'signal_contact_signed_pre_keys',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(contact_id)',
],
columns: [
_column_74,
_column_89,
_column_90,
_column_91,
_column_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape15 messageActions = Shape15(
source: i0.VersionedTable(
entityName: 'message_actions',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(message_id, contact_id, type)',
],
columns: [
_column_65,
_column_92,
_column_37,
_column_93,
],
attachedDatabase: database,
),
alias: null);
late final Shape16 groupHistories = Shape16(
source: i0.VersionedTable(
entityName: 'group_histories',
withoutRowId: false,
isStrict: false,
tableConstraints: [
'PRIMARY KEY(group_history_id)',
],
columns: [
_column_94,
_column_50,
_column_95,
_column_101,
_column_97,
_column_98,
_column_99,
_column_37,
_column_93,
],
attachedDatabase: database,
),
alias: null);
}
class Shape21 extends i0.VersionedTable {
Shape21({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get groupId =>
columnsByName['group_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get messageId =>
columnsByName['message_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get senderId =>
columnsByName['sender_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get type =>
columnsByName['type']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get content =>
columnsByName['content']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get mediaId =>
columnsByName['media_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<i2.Uint8List> get additionalMessageData =>
columnsByName['additional_message_data']!
as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<bool> get mediaStored =>
columnsByName['media_stored']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get mediaReopened =>
columnsByName['media_reopened']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<i2.Uint8List> get downloadToken =>
columnsByName['download_token']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<String> get quotesMessageId =>
columnsByName['quotes_message_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<bool> get isDeletedFromSender =>
columnsByName['is_deleted_from_sender']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<DateTime> get openedAt =>
columnsByName['opened_at']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get openedByAll =>
columnsByName['opened_by_all']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get modifiedAt =>
columnsByName['modified_at']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get ackByUser =>
columnsByName['ack_by_user']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get ackByServer =>
columnsByName['ack_by_server']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<i2.Uint8List> _column_105(String aliasedName) =>
i1.GeneratedColumn<i2.Uint8List>(
'additional_message_data', aliasedName, true,
type: i1.DriftSqlType.blob);
i0.MigrationStepWithVersion migrationSteps({ i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2, required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3, required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4, required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5, required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6, required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6,
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
}) { }) {
return (currentVersion, database) async { return (currentVersion, database) async {
switch (currentVersion) { switch (currentVersion) {
@ -2838,6 +3265,11 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema); final migrator = i1.Migrator(database, schema);
await from5To6(migrator, schema); await from5To6(migrator, schema);
return 6; return 6;
case 6:
final schema = Schema7(database: database);
final migrator = i1.Migrator(database, schema);
await from6To7(migrator, schema);
return 7;
default: default:
throw ArgumentError.value('Unknown migration from $currentVersion'); throw ArgumentError.value('Unknown migration from $currentVersion');
} }
@ -2850,6 +3282,7 @@ i1.OnUpgrade stepByStep({
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4, required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5, required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6, required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6,
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
}) => }) =>
i0.VersionedSchema.stepByStepHelper( i0.VersionedSchema.stepByStepHelper(
step: migrationSteps( step: migrationSteps(
@ -2858,4 +3291,5 @@ i1.OnUpgrade stepByStep({
from3To4: from3To4, from3To4: from3To4,
from4To5: from4To5, from4To5: from4To5,
from5To6: from5To6, from5To6: from5To6,
from6To7: from6To7,
)); ));

View file

@ -0,0 +1,11 @@
syntax = "proto3";
message AdditionalMessageData {
enum Type {
LINK = 0;
}
Type type = 1;
optional string link = 2;
}

View file

@ -0,0 +1,99 @@
// This is a generated file - do not edit.
//
// Generated from data.proto.
// @dart = 3.3
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
// ignore_for_file: constant_identifier_names
// ignore_for_file: curly_braces_in_flow_control_structures
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
// ignore_for_file: non_constant_identifier_names
import 'dart:core' as $core;
import 'package:protobuf/protobuf.dart' as $pb;
import 'data.pbenum.dart';
export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions;
export 'data.pbenum.dart';
class AdditionalMessageData extends $pb.GeneratedMessage {
factory AdditionalMessageData({
AdditionalMessageData_Type? type,
$core.String? link,
}) {
final result = create();
if (type != null) result.type = type;
if (link != null) result.link = link;
return result;
}
AdditionalMessageData._();
factory AdditionalMessageData.fromBuffer($core.List<$core.int> data,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromBuffer(data, registry);
factory AdditionalMessageData.fromJson($core.String json,
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
create()..mergeFromJson(json, registry);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
_omitMessageNames ? '' : 'AdditionalMessageData',
createEmptyInstance: create)
..e<AdditionalMessageData_Type>(
1, _omitFieldNames ? '' : 'type', $pb.PbFieldType.OE,
defaultOrMaker: AdditionalMessageData_Type.LINK,
valueOf: AdditionalMessageData_Type.valueOf,
enumValues: AdditionalMessageData_Type.values)
..aOS(2, _omitFieldNames ? '' : 'link')
..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
AdditionalMessageData clone() =>
AdditionalMessageData()..mergeFromMessage(this);
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
AdditionalMessageData copyWith(
void Function(AdditionalMessageData) updates) =>
super.copyWith((message) => updates(message as AdditionalMessageData))
as AdditionalMessageData;
@$core.override
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static AdditionalMessageData create() => AdditionalMessageData._();
@$core.override
AdditionalMessageData createEmptyInstance() => create();
static $pb.PbList<AdditionalMessageData> createRepeated() =>
$pb.PbList<AdditionalMessageData>();
@$core.pragma('dart2js:noInline')
static AdditionalMessageData getDefault() => _defaultInstance ??=
$pb.GeneratedMessage.$_defaultFor<AdditionalMessageData>(create);
static AdditionalMessageData? _defaultInstance;
@$pb.TagNumber(1)
AdditionalMessageData_Type get type => $_getN(0);
@$pb.TagNumber(1)
set type(AdditionalMessageData_Type value) => $_setField(1, value);
@$pb.TagNumber(1)
$core.bool hasType() => $_has(0);
@$pb.TagNumber(1)
void clearType() => $_clearField(1);
@$pb.TagNumber(2)
$core.String get link => $_getSZ(1);
@$pb.TagNumber(2)
set link($core.String value) => $_setString(1, value);
@$pb.TagNumber(2)
$core.bool hasLink() => $_has(1);
@$pb.TagNumber(2)
void clearLink() => $_clearField(2);
}
const $core.bool _omitFieldNames =
$core.bool.fromEnvironment('protobuf.omit_field_names');
const $core.bool _omitMessageNames =
$core.bool.fromEnvironment('protobuf.omit_message_names');

View file

@ -0,0 +1,35 @@
// This is a generated file - do not edit.
//
// Generated from data.proto.
// @dart = 3.3
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
// ignore_for_file: constant_identifier_names
// ignore_for_file: curly_braces_in_flow_control_structures
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
// ignore_for_file: non_constant_identifier_names
import 'dart:core' as $core;
import 'package:protobuf/protobuf.dart' as $pb;
class AdditionalMessageData_Type extends $pb.ProtobufEnum {
static const AdditionalMessageData_Type LINK =
AdditionalMessageData_Type._(0, _omitEnumNames ? '' : 'LINK');
static const $core.List<AdditionalMessageData_Type> values =
<AdditionalMessageData_Type>[
LINK,
];
static final $core.List<AdditionalMessageData_Type?> _byValue =
$pb.ProtobufEnum.$_initByValueList(values, 0);
static AdditionalMessageData_Type? valueOf($core.int value) =>
value < 0 || value >= _byValue.length ? null : _byValue[value];
const AdditionalMessageData_Type._(super.value, super.name);
}
const $core.bool _omitEnumNames =
$core.bool.fromEnvironment('protobuf.omit_enum_names');

View file

@ -0,0 +1,49 @@
// This is a generated file - do not edit.
//
// Generated from data.proto.
// @dart = 3.3
// ignore_for_file: annotate_overrides, camel_case_types, comment_references
// ignore_for_file: constant_identifier_names
// ignore_for_file: curly_braces_in_flow_control_structures
// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
// ignore_for_file: non_constant_identifier_names, unused_import
import 'dart:convert' as $convert;
import 'dart:core' as $core;
import 'dart:typed_data' as $typed_data;
@$core.Deprecated('Use additionalMessageDataDescriptor instead')
const AdditionalMessageData$json = {
'1': 'AdditionalMessageData',
'2': [
{
'1': 'type',
'3': 1,
'4': 1,
'5': 14,
'6': '.AdditionalMessageData.Type',
'10': 'type'
},
{'1': 'link', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'link', '17': true},
],
'4': [AdditionalMessageData_Type$json],
'8': [
{'1': '_link'},
],
};
@$core.Deprecated('Use additionalMessageDataDescriptor instead')
const AdditionalMessageData_Type$json = {
'1': 'Type',
'2': [
{'1': 'LINK', '2': 0},
],
};
/// Descriptor for `AdditionalMessageData`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List additionalMessageDataDescriptor = $convert.base64Decode(
'ChVBZGRpdGlvbmFsTWVzc2FnZURhdGESLwoEdHlwZRgBIAEoDjIbLkFkZGl0aW9uYWxNZXNzYW'
'dlRGF0YS5UeXBlUgR0eXBlEhcKBGxpbmsYAiABKAlIAFIEbGlua4gBASIQCgRUeXBlEggKBExJ'
'TksQAEIHCgVfbGluaw==');

View file

@ -969,6 +969,7 @@ class EncryptedContent_Media extends $pb.GeneratedMessage {
$core.List<$core.int>? encryptionKey, $core.List<$core.int>? encryptionKey,
$core.List<$core.int>? encryptionMac, $core.List<$core.int>? encryptionMac,
$core.List<$core.int>? encryptionNonce, $core.List<$core.int>? encryptionNonce,
$core.List<$core.int>? additionalMessageData,
}) { }) {
final result = create(); final result = create();
if (senderMessageId != null) result.senderMessageId = senderMessageId; if (senderMessageId != null) result.senderMessageId = senderMessageId;
@ -983,6 +984,8 @@ class EncryptedContent_Media extends $pb.GeneratedMessage {
if (encryptionKey != null) result.encryptionKey = encryptionKey; if (encryptionKey != null) result.encryptionKey = encryptionKey;
if (encryptionMac != null) result.encryptionMac = encryptionMac; if (encryptionMac != null) result.encryptionMac = encryptionMac;
if (encryptionNonce != null) result.encryptionNonce = encryptionNonce; if (encryptionNonce != null) result.encryptionNonce = encryptionNonce;
if (additionalMessageData != null)
result.additionalMessageData = additionalMessageData;
return result; return result;
} }
@ -1024,6 +1027,8 @@ class EncryptedContent_Media extends $pb.GeneratedMessage {
..a<$core.List<$core.int>>( ..a<$core.List<$core.int>>(
10, _omitFieldNames ? '' : 'encryptionNonce', $pb.PbFieldType.OY, 10, _omitFieldNames ? '' : 'encryptionNonce', $pb.PbFieldType.OY,
protoName: 'encryptionNonce') protoName: 'encryptionNonce')
..a<$core.List<$core.int>>(
11, _omitFieldNames ? '' : 'additionalMessageData', $pb.PbFieldType.OY)
..hasRequiredFields = false; ..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@ -1138,6 +1143,16 @@ class EncryptedContent_Media extends $pb.GeneratedMessage {
$core.bool hasEncryptionNonce() => $_has(9); $core.bool hasEncryptionNonce() => $_has(9);
@$pb.TagNumber(10) @$pb.TagNumber(10)
void clearEncryptionNonce() => $_clearField(10); void clearEncryptionNonce() => $_clearField(10);
@$pb.TagNumber(11)
$core.List<$core.int> get additionalMessageData => $_getN(10);
@$pb.TagNumber(11)
set additionalMessageData($core.List<$core.int> value) =>
$_setBytes(10, value);
@$pb.TagNumber(11)
$core.bool hasAdditionalMessageData() => $_has(10);
@$pb.TagNumber(11)
void clearAdditionalMessageData() => $_clearField(11);
} }
class EncryptedContent_MediaUpdate extends $pb.GeneratedMessage { class EncryptedContent_MediaUpdate extends $pb.GeneratedMessage {

View file

@ -603,6 +603,15 @@ const EncryptedContent_Media$json = {
'10': 'encryptionNonce', '10': 'encryptionNonce',
'17': true '17': true
}, },
{
'1': 'additional_message_data',
'3': 11,
'4': 1,
'5': 12,
'9': 6,
'10': 'additionalMessageData',
'17': true
},
], ],
'4': [EncryptedContent_Media_Type$json], '4': [EncryptedContent_Media_Type$json],
'8': [ '8': [
@ -612,6 +621,7 @@ const EncryptedContent_Media$json = {
{'1': '_encryptionKey'}, {'1': '_encryptionKey'},
{'1': '_encryptionMac'}, {'1': '_encryptionMac'},
{'1': '_encryptionNonce'}, {'1': '_encryptionNonce'},
{'1': '_additional_message_data'},
], ],
}; };
@ -840,7 +850,7 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
'EjoKGG11bHRpcGxlVGFyZ2V0TWVzc2FnZUlkcxgDIAMoCVIYbXVsdGlwbGVUYXJnZXRNZXNzYW' 'EjoKGG11bHRpcGxlVGFyZ2V0TWVzc2FnZUlkcxgDIAMoCVIYbXVsdGlwbGVUYXJnZXRNZXNzYW'
'dlSWRzEhcKBHRleHQYBCABKAlIAVIEdGV4dIgBARIcCgl0aW1lc3RhbXAYBSABKANSCXRpbWVz' 'dlSWRzEhcKBHRleHQYBCABKAlIAVIEdGV4dIgBARIcCgl0aW1lc3RhbXAYBSABKANSCXRpbWVz'
'dGFtcCItCgRUeXBlEgoKBkRFTEVURRAAEg0KCUVESVRfVEVYVBABEgoKBk9QRU5FRBACQhIKEF' 'dGFtcCItCgRUeXBlEgoKBkRFTEVURRAAEg0KCUVESVRfVEVYVBABEgoKBk9QRU5FRBACQhIKEF'
'9zZW5kZXJNZXNzYWdlSWRCBwoFX3RleHQalwUKBU1lZGlhEigKD3NlbmRlck1lc3NhZ2VJZBgB' '9zZW5kZXJNZXNzYWdlSWRCBwoFX3RleHQa8AUKBU1lZGlhEigKD3NlbmRlck1lc3NhZ2VJZBgB'
'IAEoCVIPc2VuZGVyTWVzc2FnZUlkEjAKBHR5cGUYAiABKA4yHC5FbmNyeXB0ZWRDb250ZW50Lk' 'IAEoCVIPc2VuZGVyTWVzc2FnZUlkEjAKBHR5cGUYAiABKA4yHC5FbmNyeXB0ZWRDb250ZW50Lk'
'1lZGlhLlR5cGVSBHR5cGUSQwoaZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHMYAyABKANIAFIa' '1lZGlhLlR5cGVSBHR5cGUSQwoaZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHMYAyABKANIAFIa'
'ZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHOIAQESNgoWcmVxdWlyZXNBdXRoZW50aWNhdGlvbh' 'ZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHOIAQESNgoWcmVxdWlyZXNBdXRoZW50aWNhdGlvbh'
@ -849,29 +859,31 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
'dubG9hZFRva2VuGAcgASgMSAJSDWRvd25sb2FkVG9rZW6IAQESKQoNZW5jcnlwdGlvbktleRgI' 'dubG9hZFRva2VuGAcgASgMSAJSDWRvd25sb2FkVG9rZW6IAQESKQoNZW5jcnlwdGlvbktleRgI'
'IAEoDEgDUg1lbmNyeXB0aW9uS2V5iAEBEikKDWVuY3J5cHRpb25NYWMYCSABKAxIBFINZW5jcn' 'IAEoDEgDUg1lbmNyeXB0aW9uS2V5iAEBEikKDWVuY3J5cHRpb25NYWMYCSABKAxIBFINZW5jcn'
'lwdGlvbk1hY4gBARItCg9lbmNyeXB0aW9uTm9uY2UYCiABKAxIBVIPZW5jcnlwdGlvbk5vbmNl' 'lwdGlvbk1hY4gBARItCg9lbmNyeXB0aW9uTm9uY2UYCiABKAxIBVIPZW5jcnlwdGlvbk5vbmNl'
'iAEBIj4KBFR5cGUSDAoIUkVVUExPQUQQABIJCgVJTUFHRRABEgkKBVZJREVPEAISBwoDR0lGEA' 'iAEBEjsKF2FkZGl0aW9uYWxfbWVzc2FnZV9kYXRhGAsgASgMSAZSFWFkZGl0aW9uYWxNZXNzYW'
'MSCQoFQVVESU8QBEIdChtfZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHNCEQoPX3F1b3RlTWVz' 'dlRGF0YYgBASI+CgRUeXBlEgwKCFJFVVBMT0FEEAASCQoFSU1BR0UQARIJCgVWSURFTxACEgcK'
'c2FnZUlkQhAKDl9kb3dubG9hZFRva2VuQhAKDl9lbmNyeXB0aW9uS2V5QhAKDl9lbmNyeXB0aW' 'A0dJRhADEgkKBUFVRElPEARCHQobX2Rpc3BsYXlMaW1pdEluTWlsbGlzZWNvbmRzQhEKD19xdW'
'9uTWFjQhIKEF9lbmNyeXB0aW9uTm9uY2UapwEKC01lZGlhVXBkYXRlEjYKBHR5cGUYASABKA4y' '90ZU1lc3NhZ2VJZEIQCg5fZG93bmxvYWRUb2tlbkIQCg5fZW5jcnlwdGlvbktleUIQCg5fZW5j'
'Ii5FbmNyeXB0ZWRDb250ZW50Lk1lZGlhVXBkYXRlLlR5cGVSBHR5cGUSKAoPdGFyZ2V0TWVzc2' 'cnlwdGlvbk1hY0ISChBfZW5jcnlwdGlvbk5vbmNlQhoKGF9hZGRpdGlvbmFsX21lc3NhZ2VfZG'
'FnZUlkGAIgASgJUg90YXJnZXRNZXNzYWdlSWQiNgoEVHlwZRIMCghSRU9QRU5FRBAAEgoKBlNU' 'F0YRqnAQoLTWVkaWFVcGRhdGUSNgoEdHlwZRgBIAEoDjIiLkVuY3J5cHRlZENvbnRlbnQuTWVk'
'T1JFRBABEhQKEERFQ1JZUFRJT05fRVJST1IQAhp4Cg5Db250YWN0UmVxdWVzdBI5CgR0eXBlGA' 'aWFVcGRhdGUuVHlwZVIEdHlwZRIoCg90YXJnZXRNZXNzYWdlSWQYAiABKAlSD3RhcmdldE1lc3'
'EgASgOMiUuRW5jcnlwdGVkQ29udGVudC5Db250YWN0UmVxdWVzdC5UeXBlUgR0eXBlIisKBFR5' 'NhZ2VJZCI2CgRUeXBlEgwKCFJFT1BFTkVEEAASCgoGU1RPUkVEEAESFAoQREVDUllQVElPTl9F'
'cGUSCwoHUkVRVUVTVBAAEgoKBlJFSkVDVBABEgoKBkFDQ0VQVBACGp4CCg1Db250YWN0VXBkYX' 'UlJPUhACGngKDkNvbnRhY3RSZXF1ZXN0EjkKBHR5cGUYASABKA4yJS5FbmNyeXB0ZWRDb250ZW'
'RlEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RVcGRhdGUuVHlwZVIE' '50LkNvbnRhY3RSZXF1ZXN0LlR5cGVSBHR5cGUiKwoEVHlwZRILCgdSRVFVRVNUEAASCgoGUkVK'
'dHlwZRI1ChNhdmF0YXJTdmdDb21wcmVzc2VkGAIgASgMSABSE2F2YXRhclN2Z0NvbXByZXNzZW' 'RUNUEAESCgoGQUNDRVBUEAIangIKDUNvbnRhY3RVcGRhdGUSOAoEdHlwZRgBIAEoDjIkLkVuY3'
'SIAQESHwoIdXNlcm5hbWUYAyABKAlIAVIIdXNlcm5hbWWIAQESJQoLZGlzcGxheU5hbWUYBCAB' 'J5cHRlZENvbnRlbnQuQ29udGFjdFVwZGF0ZS5UeXBlUgR0eXBlEjUKE2F2YXRhclN2Z0NvbXBy'
'KAlIAlILZGlzcGxheU5hbWWIAQEiHwoEVHlwZRILCgdSRVFVRVNUEAASCgoGVVBEQVRFEAFCFg' 'ZXNzZWQYAiABKAxIAFITYXZhdGFyU3ZnQ29tcHJlc3NlZIgBARIfCgh1c2VybmFtZRgDIAEoCU'
'oUX2F2YXRhclN2Z0NvbXByZXNzZWRCCwoJX3VzZXJuYW1lQg4KDF9kaXNwbGF5TmFtZRrVAQoI' 'gBUgh1c2VybmFtZYgBARIlCgtkaXNwbGF5TmFtZRgEIAEoCUgCUgtkaXNwbGF5TmFtZYgBASIf'
'UHVzaEtleXMSMwoEdHlwZRgBIAEoDjIfLkVuY3J5cHRlZENvbnRlbnQuUHVzaEtleXMuVHlwZV' 'CgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIWChRfYXZhdGFyU3ZnQ29tcHJlc3NlZE'
'IEdHlwZRIZCgVrZXlJZBgCIAEoA0gAUgVrZXlJZIgBARIVCgNrZXkYAyABKAxIAVIDa2V5iAEB' 'ILCglfdXNlcm5hbWVCDgoMX2Rpc3BsYXlOYW1lGtUBCghQdXNoS2V5cxIzCgR0eXBlGAEgASgO'
'EiEKCWNyZWF0ZWRBdBgEIAEoA0gCUgljcmVhdGVkQXSIAQEiHwoEVHlwZRILCgdSRVFVRVNUEA' 'Mh8uRW5jcnlwdGVkQ29udGVudC5QdXNoS2V5cy5UeXBlUgR0eXBlEhkKBWtleUlkGAIgASgDSA'
'ASCgoGVVBEQVRFEAFCCAoGX2tleUlkQgYKBF9rZXlCDAoKX2NyZWF0ZWRBdBqpAQoJRmxhbWVT' 'BSBWtleUlkiAEBEhUKA2tleRgDIAEoDEgBUgNrZXmIAQESIQoJY3JlYXRlZEF0GAQgASgDSAJS'
'eW5jEiIKDGZsYW1lQ291bnRlchgBIAEoA1IMZmxhbWVDb3VudGVyEjYKFmxhc3RGbGFtZUNvdW' 'CWNyZWF0ZWRBdIgBASIfCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIICgZfa2V5SW'
'50ZXJDaGFuZ2UYAiABKANSFmxhc3RGbGFtZUNvdW50ZXJDaGFuZ2USHgoKYmVzdEZyaWVuZBgD' 'RCBgoEX2tleUIMCgpfY3JlYXRlZEF0GqkBCglGbGFtZVN5bmMSIgoMZmxhbWVDb3VudGVyGAEg'
'IAEoCFIKYmVzdEZyaWVuZBIgCgtmb3JjZVVwZGF0ZRgEIAEoCFILZm9yY2VVcGRhdGVCCgoIX2' 'ASgDUgxmbGFtZUNvdW50ZXISNgoWbGFzdEZsYW1lQ291bnRlckNoYW5nZRgCIAEoA1IWbGFzdE'
'dyb3VwSWRCDwoNX2lzRGlyZWN0Q2hhdEIXChVfc2VuZGVyUHJvZmlsZUNvdW50ZXJCEAoOX21l' 'ZsYW1lQ291bnRlckNoYW5nZRIeCgpiZXN0RnJpZW5kGAMgASgIUgpiZXN0RnJpZW5kEiAKC2Zv'
'c3NhZ2VVcGRhdGVCCAoGX21lZGlhQg4KDF9tZWRpYVVwZGF0ZUIQCg5fY29udGFjdFVwZGF0ZU' 'cmNlVXBkYXRlGAQgASgIUgtmb3JjZVVwZGF0ZUIKCghfZ3JvdXBJZEIPCg1faXNEaXJlY3RDaG'
'IRCg9fY29udGFjdFJlcXVlc3RCDAoKX2ZsYW1lU3luY0ILCglfcHVzaEtleXNCCwoJX3JlYWN0' 'F0QhcKFV9zZW5kZXJQcm9maWxlQ291bnRlckIQCg5fbWVzc2FnZVVwZGF0ZUIICgZfbWVkaWFC'
'aW9uQg4KDF90ZXh0TWVzc2FnZUIOCgxfZ3JvdXBDcmVhdGVCDAoKX2dyb3VwSm9pbkIOCgxfZ3' 'DgoMX21lZGlhVXBkYXRlQhAKDl9jb250YWN0VXBkYXRlQhEKD19jb250YWN0UmVxdWVzdEIMCg'
'JvdXBVcGRhdGVCFwoVX3Jlc2VuZEdyb3VwUHVibGljS2V5QhEKD19lcnJvcl9tZXNzYWdlcw=='); 'pfZmxhbWVTeW5jQgsKCV9wdXNoS2V5c0ILCglfcmVhY3Rpb25CDgoMX3RleHRNZXNzYWdlQg4K'
'DF9ncm91cENyZWF0ZUIMCgpfZ3JvdXBKb2luQg4KDF9ncm91cFVwZGF0ZUIXChVfcmVzZW5kR3'
'JvdXBQdWJsaWNLZXlCEQoPX2Vycm9yX21lc3NhZ2Vz');

View file

@ -132,6 +132,8 @@ message EncryptedContent {
optional bytes encryptionKey = 8; optional bytes encryptionKey = 8;
optional bytes encryptionMac = 9; optional bytes encryptionMac = 9;
optional bytes encryptionNonce = 10; optional bytes encryptionNonce = 10;
optional bytes additional_message_data = 11;
} }
message MediaUpdate { message MediaUpdate {

View file

@ -105,6 +105,11 @@ Future<void> handleMedia(
groupId: Value(groupId), groupId: Value(groupId),
mediaId: Value(mediaFile.mediaId), mediaId: Value(mediaFile.mediaId),
type: const Value(MessageType.media), type: const Value(MessageType.media),
additionalMessageData: Value.absentIfNull(
media.hasAdditionalMessageData()
? Uint8List.fromList(media.additionalMessageData)
: null,
),
quotesMessageId: Value( quotesMessageId: Value(
media.hasQuoteMessageId() ? media.quoteMessageId : null, media.hasQuoteMessageId() ? media.quoteMessageId : null,
), ),

View file

@ -16,6 +16,7 @@ import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/tables/messages.table.dart'; import 'package:twonly/src/database/tables/messages.table.dart';
import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/protobuf/api/http/http_requests.pb.dart'; import 'package:twonly/src/model/protobuf/api/http/http_requests.pb.dart';
import 'package:twonly/src/model/protobuf/client/generated/data.pb.dart';
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'; import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
import 'package:twonly/src/services/api/mediafiles/media_background.service.dart'; import 'package:twonly/src/services/api/mediafiles/media_background.service.dart';
import 'package:twonly/src/services/api/messages.dart'; import 'package:twonly/src/services/api/messages.dart';
@ -88,8 +89,9 @@ Future<MediaFileService?> initializeMediaUpload(
Future<void> insertMediaFileInMessagesTable( Future<void> insertMediaFileInMessagesTable(
MediaFileService mediaService, MediaFileService mediaService,
List<String> groupIds, List<String> groupIds, {
) async { AdditionalMessageData? additionalData,
}) async {
await twonlyDB.mediaFilesDao.updateAllMediaFiles( await twonlyDB.mediaFilesDao.updateAllMediaFiles(
const MediaFilesCompanion( const MediaFilesCompanion(
isDraftMedia: Value(false), isDraftMedia: Value(false),
@ -101,6 +103,8 @@ Future<void> insertMediaFileInMessagesTable(
groupId: Value(groupId), groupId: Value(groupId),
mediaId: Value(mediaService.mediaFile.mediaId), mediaId: Value(mediaService.mediaFile.mediaId),
type: const Value(MessageType.media), type: const Value(MessageType.media),
additionalMessageData:
Value.absentIfNull(additionalData?.writeToBuffer()),
), ),
); );
await twonlyDB.groupsDao.increaseLastMessageExchange(groupId, clock.now()); await twonlyDB.groupsDao.increaseLastMessageExchange(groupId, clock.now());
@ -245,6 +249,7 @@ Future<void> _createUploadRequest(MediaFileService media) async {
encryptionKey: media.mediaFile.encryptionKey, encryptionKey: media.mediaFile.encryptionKey,
encryptionNonce: media.mediaFile.encryptionNonce, encryptionNonce: media.mediaFile.encryptionNonce,
encryptionMac: media.mediaFile.encryptionMac, encryptionMac: media.mediaFile.encryptionMac,
additionalMessageData: message.additionalMessageData,
), ),
); );

View file

@ -61,6 +61,7 @@ Future<(Uint8List, Uint8List?)?> tryToSendCompleteMessage({
String? receiptId, String? receiptId,
Receipt? receipt, Receipt? receipt,
bool onlyReturnEncryptedData = false, bool onlyReturnEncryptedData = false,
bool blocking = true,
}) async { }) async {
try { try {
if (receiptId == null && receipt == null) return null; if (receiptId == null && receipt == null) return null;
@ -238,12 +239,11 @@ Future<void> sendCipherTextToGroup(
encryptedContent.groupId = groupId; encryptedContent.groupId = groupId;
for (final groupMember in groupMembers) { for (final groupMember in groupMembers) {
unawaited( await sendCipherText(
sendCipherText(
groupMember.contactId, groupMember.contactId,
encryptedContent, encryptedContent,
messageId: messageId, messageId: messageId,
), blocking: false,
); );
} }
} }
@ -252,6 +252,7 @@ Future<(Uint8List, Uint8List?)?> sendCipherText(
int contactId, int contactId,
pb.EncryptedContent encryptedContent, { pb.EncryptedContent encryptedContent, {
bool onlyReturnEncryptedData = false, bool onlyReturnEncryptedData = false,
bool blocking = true,
String? messageId, String? messageId,
}) async { }) async {
encryptedContent.senderProfileCounter = Int64(gUser.avatarCounter); encryptedContent.senderProfileCounter = Int64(gUser.avatarCounter);
@ -270,10 +271,15 @@ Future<(Uint8List, Uint8List?)?> sendCipherText(
); );
if (receipt != null) { if (receipt != null) {
return tryToSendCompleteMessage( final tmp = tryToSendCompleteMessage(
receipt: receipt, receipt: receipt,
onlyReturnEncryptedData: onlyReturnEncryptedData, onlyReturnEncryptedData: onlyReturnEncryptedData,
blocking: blocking,
); );
if (!blocking) {
return null;
}
return tmp;
} }
return null; return null;
} }
@ -302,6 +308,7 @@ Future<void> notifyContactAboutOpeningMessage(
timestamp: Int64(actionAt.millisecondsSinceEpoch), timestamp: Int64(actionAt.millisecondsSinceEpoch),
), ),
), ),
blocking: false,
); );
for (final messageId in messageOtherIds) { for (final messageId in messageOtherIds) {
await twonlyDB.messagesDao.updateMessageId( await twonlyDB.messagesDao.updateMessageId(

View file

@ -4,6 +4,7 @@ import 'dart:io';
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
import 'package:clock/clock.dart'; import 'package:clock/clock.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_android_volume_keydown/flutter_android_volume_keydown.dart'; import 'package:flutter_android_volume_keydown/flutter_android_volume_keydown.dart';
@ -352,7 +353,12 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
sendToGroup: widget.sendToGroup, sendToGroup: widget.sendToGroup,
mediaFileService: mediaFileService, mediaFileService: mediaFileService,
mainCameraController: mc, mainCameraController: mc,
previewLink: mc.sharedLinkForPreview, // previewLink: mc.sharedLinkForPreview,
previewLink: kDebugMode
? Uri.parse(
'https://mastodon.social/@islieb/115883317936171927',
)
: mc.sharedLinkForPreview,
), ),
transitionsBuilder: (context, animation, secondaryAnimation, child) { transitionsBuilder: (context, animation, secondaryAnimation, child) {
return child; return child;

View file

@ -9,6 +9,7 @@ import 'package:twonly/globals.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/protobuf/client/generated/data.pb.dart';
import 'package:twonly/src/services/api/mediafiles/upload.service.dart'; import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
import 'package:twonly/src/services/flame.service.dart'; import 'package:twonly/src/services/flame.service.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
@ -24,12 +25,14 @@ class ShareImageView extends StatefulWidget {
required this.updateSelectedGroupIds, required this.updateSelectedGroupIds,
required this.mediaStoreFuture, required this.mediaStoreFuture,
required this.mediaFileService, required this.mediaFileService,
required this.additionalData,
super.key, super.key,
}); });
final HashSet<String> selectedGroupIds; final HashSet<String> selectedGroupIds;
final void Function(String, bool) updateSelectedGroupIds; final void Function(String, bool) updateSelectedGroupIds;
final Future<Uint8List?>? mediaStoreFuture; final Future<Uint8List?>? mediaStoreFuture;
final MediaFileService mediaFileService; final MediaFileService mediaFileService;
final AdditionalMessageData? additionalData;
@override @override
State<ShareImageView> createState() => _ShareImageView(); State<ShareImageView> createState() => _ShareImageView();
@ -286,6 +289,7 @@ class _ShareImageView extends State<ShareImageView> {
await insertMediaFileInMessagesTable( await insertMediaFileInMessagesTable(
widget.mediaFileService, widget.mediaFileService,
widget.selectedGroupIds.toList(), widget.selectedGroupIds.toList(),
additionalData: widget.additionalData,
); );
if (context.mounted) { if (context.mounted) {

View file

@ -11,6 +11,7 @@ import 'package:twonly/globals.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart'; import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/protobuf/client/generated/data.pb.dart';
import 'package:twonly/src/services/api/mediafiles/upload.service.dart'; import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
@ -420,6 +421,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
updateSelectedGroupIds: updateSelectedGroupIds, updateSelectedGroupIds: updateSelectedGroupIds,
mediaStoreFuture: mediaStoreFuture, mediaStoreFuture: mediaStoreFuture,
mediaFileService: mediaService, mediaFileService: mediaService,
additionalData: getAdditionalData(),
), ),
), ),
) as bool?; ) as bool?;
@ -545,6 +547,18 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
}); });
} }
AdditionalMessageData? getAdditionalData() {
AdditionalMessageData? additionalData;
if (widget.previewLink != null) {
additionalData = AdditionalMessageData(
type: AdditionalMessageData_Type.LINK,
link: widget.previewLink.toString(),
);
}
return additionalData;
}
Future<void> sendImageToSinglePerson() async { Future<void> sendImageToSinglePerson() async {
if (sendingOrLoadingImage) return; if (sendingOrLoadingImage) return;
setState(() { setState(() {
@ -560,6 +574,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
await insertMediaFileInMessagesTable( await insertMediaFileInMessagesTable(
mediaService, mediaService,
[widget.sendToGroup!.groupId], [widget.sendToGroup!.groupId],
additionalData: getAdditionalData(),
); );
if (mounted) { if (mounted) {

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hand_signature/signature.dart'; import 'package:hand_signature/signature.dart';
import 'package:twonly/src/views/camera/share_image_editor/image_item.dart'; import 'package:twonly/src/views/camera/share_image_editor/image_item.dart';
import 'package:twonly/src/views/camera/share_image_editor/layers/link_preview/parser/base.dart';
/// Layer class with some common properties /// Layer class with some common properties
class Layer { class Layer {
@ -41,6 +42,8 @@ class LinkPreviewLayerData extends Layer {
required this.link, required this.link,
}); });
Uri link; Uri link;
Metadata? metadata;
bool error = false;
} }
class FilterLayerData extends Layer { class FilterLayerData extends Layer {

View file

@ -1,5 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/views/camera/share_image_editor/layer_data.dart'; import 'package:twonly/src/views/camera/share_image_editor/layer_data.dart';
import 'package:twonly/src/views/camera/share_image_editor/layers/link_preview/cards/custom.card.dart';
import 'package:twonly/src/views/camera/share_image_editor/layers/link_preview/cards/mastodon.card.dart';
import 'package:twonly/src/views/camera/share_image_editor/layers/link_preview/cards/twitter.card.dart';
import 'package:twonly/src/views/camera/share_image_editor/layers/link_preview/cards/youtube.card.dart';
import 'package:twonly/src/views/camera/share_image_editor/layers/link_preview/parse_link.dart';
import 'package:twonly/src/views/camera/share_image_editor/layers/link_preview/parser/base.dart';
import 'package:twonly/src/views/components/loader.dart';
class LinkPreviewLayer extends StatefulWidget { class LinkPreviewLayer extends StatefulWidget {
const LinkPreviewLayer({ const LinkPreviewLayer({
@ -15,11 +22,48 @@ class LinkPreviewLayer extends StatefulWidget {
} }
class _LinkPreviewLayerState extends State<LinkPreviewLayer> { class _LinkPreviewLayerState extends State<LinkPreviewLayer> {
Metadata? metadata;
@override
void initState() {
initAsync();
super.initState();
}
Future<void> initAsync() async {
if (widget.layerData.metadata == null) {
widget.layerData.metadata =
await getMetadata(widget.layerData.link.toString());
if (widget.layerData.metadata == null) {
widget.layerData.error = true;
}
if (mounted) setState(() {});
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( if (widget.layerData.error) {
padding: EdgeInsets.zero, return Container();
child: Text(widget.layerData.link.toString()), }
final meta = widget.layerData.metadata;
late Widget child;
if (meta == null) {
child = const ThreeRotatingDots(size: 30);
} else if (meta.title == null) {
return Container();
} else if (meta.vendor == Vendor.mastodonSocialMediaPosting) {
child = MastodonPostCard(info: meta);
} else if (meta.vendor == Vendor.twitterPosting) {
child = TwitterPostCard(info: meta);
} else if (meta.vendor == Vendor.youtubeVideo) {
child = YouTubePostCard(info: meta);
} else {
child = CustomLinkCard(info: meta);
}
return Center(
child: child,
); );
} }
} }

View file

@ -0,0 +1,87 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/camera/share_image_editor/layers/link_preview/parser/base.dart';
class CustomLinkCard extends StatelessWidget {
const CustomLinkCard({required this.info, super.key});
final Metadata info;
@override
Widget build(BuildContext context) {
return FractionallySizedBox(
widthFactor: 0.8,
child: Container(
decoration: BoxDecoration(
color: const Color(0xFF1E1E1E),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.white10),
),
clipBehavior: Clip.antiAlias,
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
Uri.parse(info.url).host.toUpperCase(),
style: TextStyle(
color: context.color.primary,
fontSize: 10,
fontWeight: FontWeight.bold,
letterSpacing: 1.2,
),
),
const SizedBox(height: 4),
Text(
substringBy(info.title ?? 'Link Preview', 35),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
if (info.desc != null && info.desc != 'null') ...[
const SizedBox(height: 6),
Text(
substringBy(info.desc!, 500),
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Color(0xFFB0B0B0),
fontSize: 13,
height: 1.4,
),
),
],
if (info.image != null && info.image != 'null')
Padding(
padding: const EdgeInsets.only(top: 12),
child: ClipRRect(
borderRadius: BorderRadius.circular(4),
child: CachedNetworkImage(
imageUrl: info.image!,
fit: BoxFit.cover,
width: double.infinity,
),
),
),
],
),
),
),
],
),
),
),
);
}
}

View file

@ -0,0 +1,110 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/views/camera/share_image_editor/layers/link_preview/parser/base.dart';
import 'package:twonly/src/views/components/loader.dart';
class MastodonPostCard extends StatelessWidget {
const MastodonPostCard({required this.info, super.key});
final Metadata info;
@override
Widget build(BuildContext context) {
const backgroundColor = Color(0xFF282C37);
const secondaryTextColor = Color(0xFF9BA3AF);
const accentColor = Color(0xFF6364FF);
return FractionallySizedBox(
widthFactor: 0.8,
child: Container(
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
const FaIcon(
FontAwesomeIcons.mastodon,
color: accentColor,
size: 20,
),
const SizedBox(width: 10),
Text(
substringBy(info.title ?? 'Mastodon User', 37),
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
],
),
const SizedBox(height: 4),
if (info.desc != null && info.desc != 'null')
Text(
substringBy(info.desc!, 1000),
style: const TextStyle(color: Colors.white, fontSize: 14),
),
if (info.image != null && info.image != 'null')
Padding(
padding: const EdgeInsets.only(top: 8),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 250),
child: CachedNetworkImage(
imageUrl: info.image!,
fit: BoxFit.contain,
width: double.infinity,
placeholder: (context, url) => Container(
height: 150,
color: Colors.black12,
child: const Center(
child: ThreeRotatingDots(size: 20),
),
),
),
),
),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildAction(
Icons.repeat,
'${info.shareAction ?? 0}',
secondaryTextColor,
),
const SizedBox(width: 20),
_buildAction(
Icons.star_border,
'${info.likeAction ?? 0}',
secondaryTextColor,
),
],
),
],
),
),
);
}
Widget _buildAction(IconData icon, String count, Color color) {
return Row(
children: [
Icon(icon, size: 18, color: color),
if (count.isNotEmpty && count != '0') ...[
const SizedBox(width: 5),
Text(count, style: TextStyle(color: color, fontSize: 13)),
],
],
);
}
}

View file

@ -0,0 +1,101 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
// Assuming the same Metadata import structure
import 'package:twonly/src/views/camera/share_image_editor/layers/link_preview/parser/base.dart';
class TwitterPostCard extends StatelessWidget {
const TwitterPostCard({required this.info, super.key});
final Metadata info;
@override
Widget build(BuildContext context) {
// Classic Twitter Brand Colors
const twitterBlue = Color(0xFF1DA1F2);
const backgroundWhite = Colors.white;
const primaryText = Color(0xFF14171A);
const borderColor = Color(0xFFE1E8ED);
return FractionallySizedBox(
widthFactor: 0.9, // Twitter cards often feel a bit wider
child: Container(
decoration: BoxDecoration(
color: backgroundWhite,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: borderColor),
),
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
const FaIcon(
FontAwesomeIcons.twitter,
color: twitterBlue,
size: 22,
),
const SizedBox(width: 12),
Expanded(
child: Text(
substringBy(info.title ?? 'Twitter User', 37),
style: const TextStyle(
color: primaryText,
fontWeight: FontWeight.w800,
fontSize: 16,
letterSpacing: -0.5,
),
overflow: TextOverflow.ellipsis,
),
),
],
),
const SizedBox(height: 8),
if (info.desc != null && info.desc != 'null')
Text(
substringBy(info.desc!, 1000),
style: const TextStyle(
color: primaryText,
fontSize: 15,
height: 1.3,
),
),
if (info.image != null && info.image != 'null')
Padding(
padding: const EdgeInsets.only(top: 12),
child: ClipRRect(
borderRadius: BorderRadius.circular(14),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: borderColor),
borderRadius: BorderRadius.circular(14),
),
child: ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 300),
child: CachedNetworkImage(
imageUrl: info.image!,
fit: BoxFit.cover,
width: double.infinity,
placeholder: (context, url) => Container(
height: 150,
color: const Color(0xFFF5F8FA),
child: const Center(
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation(twitterBlue),
),
),
),
),
),
),
),
),
],
),
),
);
}
}

View file

@ -0,0 +1,90 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/views/camera/share_image_editor/layers/link_preview/parser/base.dart';
class YouTubePostCard extends StatelessWidget {
const YouTubePostCard({required this.info, super.key});
final Metadata info;
@override
Widget build(BuildContext context) {
const ytBlack = Color(0xFF0F0F0F);
const ytWhite = Colors.white;
const ytRed = Color.fromARGB(255, 255, 1, 51);
return FractionallySizedBox(
widthFactor: 0.8,
child: Container(
decoration: BoxDecoration(
color: ytBlack,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Stack(
alignment: Alignment.bottomRight,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: AspectRatio(
aspectRatio: 16 / 9,
child: CachedNetworkImage(
imageUrl: info.image ?? '',
fit: BoxFit.cover,
placeholder: (context, url) =>
Container(color: Colors.white10),
errorWidget: (context, url, error) => const ColoredBox(
color: Colors.white10,
child: Icon(
Icons.play_circle_outline,
color: ytWhite,
size: 50,
),
),
),
),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const FaIcon(
FontAwesomeIcons.youtube,
color: ytRed,
size: 20,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
substringBy(info.title ?? 'Video Title', 600),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: ytWhite,
fontSize: 16,
fontWeight: FontWeight.w500,
height: 1.2,
),
),
],
),
),
],
),
),
],
),
),
);
}
}

View file

@ -18,7 +18,7 @@ class FriendlyMessageTime extends StatelessWidget {
padding: const EdgeInsets.only(left: 6), padding: const EdgeInsets.only(left: 6),
child: Row( child: Row(
children: [ children: [
if (message.modifiedAt != null) if (message.modifiedAt != null && !message.isDeletedFromSender)
Padding( Padding(
padding: const EdgeInsets.only(right: 5), padding: const EdgeInsets.only(right: 5),
child: SizedBox( child: SizedBox(

View file

@ -21,6 +21,7 @@ import 'package:twonly/src/services/notifications/background.notifications.dart'
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/camera/camera_send_to.view.dart'; import 'package:twonly/src/views/camera/camera_send_to.view.dart';
import 'package:twonly/src/views/chats/media_viewer_components/additional_message_content.dart';
import 'package:twonly/src/views/chats/media_viewer_components/reaction_buttons.component.dart'; import 'package:twonly/src/views/chats/media_viewer_components/reaction_buttons.component.dart';
import 'package:twonly/src/views/components/animate_icon.dart'; import 'package:twonly/src/views/components/animate_icon.dart';
import 'package:twonly/src/views/components/loader.dart'; import 'package:twonly/src/views/components/loader.dart';
@ -493,15 +494,8 @@ class _MediaViewerViewState extends State<MediaViewerView> {
); );
} }
@override Widget _loader() {
Widget build(BuildContext context) { return Center(
return Scaffold(
body: SafeArea(
child: Stack(
fit: StackFit.expand,
children: [
if (_showDownloadingLoader)
Center(
child: SizedBox( child: SizedBox(
height: 60, height: 60,
width: 60, width: 60,
@ -510,7 +504,17 @@ class _MediaViewerViewState extends State<MediaViewerView> {
color: context.color.primary, color: context.color.primary,
), ),
), ),
), );
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
fit: StackFit.expand,
children: [
if (_showDownloadingLoader) _loader(),
if ((currentMedia != null || videoController != null) && if ((currentMedia != null || videoController != null) &&
(canBeSeenUntil == null || progress >= 0)) (canBeSeenUntil == null || progress >= 0))
GestureDetector( GestureDetector(
@ -549,9 +553,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
if (displayTwonlyPresent) if (displayTwonlyPresent)
Positioned.fill( Positioned.fill(
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () => loadCurrentMediaFile(showTwonly: true),
loadCurrentMediaFile(showTwonly: true);
},
child: Column( child: Column(
children: [ children: [
Expanded( Expanded(
@ -575,26 +577,14 @@ class _MediaViewerViewState extends State<MediaViewerView> {
IconButton( IconButton(
icon: const Icon(Icons.close, size: 30), icon: const Icon(Icons.close, size: 30),
color: Colors.white, color: Colors.white,
onPressed: () async { onPressed: () => Navigator.pop(context),
Navigator.pop(context);
},
), ),
], ],
), ),
), ),
if (currentMedia != null && if (currentMedia != null &&
currentMedia?.mediaFile.downloadState != DownloadState.ready) currentMedia?.mediaFile.downloadState != DownloadState.ready)
const Positioned.fill( Positioned.fill(child: _loader()),
child: Center(
child: SizedBox(
height: 60,
width: 60,
child: CircularProgressIndicator(
strokeWidth: 6,
),
),
),
),
if (canBeSeenUntil != null || progress >= 0) if (canBeSeenUntil != null || progress >= 0)
Positioned( Positioned(
right: 20, right: 20,
@ -718,6 +708,8 @@ class _MediaViewerViewState extends State<MediaViewerView> {
Positioned.fill( Positioned.fill(
child: EmojiFloatWidget(key: emojiKey), child: EmojiFloatWidget(key: emojiKey),
), ),
if (currentMessage != null)
AdditionalMessageContent(currentMessage!),
], ],
), ),
), ),

View file

@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/protobuf/client/generated/data.pb.dart';
import 'package:url_launcher/url_launcher_string.dart';
class AdditionalMessageContent extends StatelessWidget {
const AdditionalMessageContent(this.message, {super.key});
final Message message;
@override
Widget build(BuildContext context) {
if (message.additionalMessageData == null) return Container();
try {
final data =
AdditionalMessageData.fromBuffer(message.additionalMessageData!);
switch (data.type) {
case AdditionalMessageData_Type.LINK:
if (!data.link.startsWith('http://') &&
!data.link.startsWith('https://')) {
return Container();
}
return Positioned(
bottom: 150,
right: 0,
left: 0,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FilledButton.icon(
icon: const FaIcon(FontAwesomeIcons.shareFromSquare),
onPressed: () => launchUrlString(data.link),
label: Text(
substringBy(
data.link
.replaceAll('http://', '')
.replaceAll('https://', ''),
30,
),
),
),
],
),
);
default:
}
// ignore: empty_catches
} catch (e) {}
return Container();
}
}

View file

@ -15,6 +15,7 @@ protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "backup.proto"
protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "messages.proto" protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "messages.proto"
protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "groups.proto" protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "groups.proto"
protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "qr.proto" protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "qr.proto"
protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "data.proto"
protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "push_notification.proto" protoc --proto_path="$CLIENT_DIR" --dart_out="$GENERATED_DIR" "push_notification.proto"
protoc --proto_path="$CLIENT_DIR" --swift_out="./ios/NotificationService/" "push_notification.proto" protoc --proto_path="$CLIENT_DIR" --swift_out="./ios/NotificationService/" "push_notification.proto"

View file

@ -9,6 +9,7 @@ import 'schema_v3.dart' as v3;
import 'schema_v4.dart' as v4; import 'schema_v4.dart' as v4;
import 'schema_v5.dart' as v5; import 'schema_v5.dart' as v5;
import 'schema_v6.dart' as v6; import 'schema_v6.dart' as v6;
import 'schema_v7.dart' as v7;
class GeneratedHelper implements SchemaInstantiationHelper { class GeneratedHelper implements SchemaInstantiationHelper {
@override @override
@ -26,10 +27,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v5.DatabaseAtV5(db); return v5.DatabaseAtV5(db);
case 6: case 6:
return v6.DatabaseAtV6(db); return v6.DatabaseAtV6(db);
case 7:
return v7.DatabaseAtV7(db);
default: default:
throw MissingSchemaException(version, versions); throw MissingSchemaException(version, versions);
} }
} }
static const versions = const [1, 2, 3, 4, 5, 6]; static const versions = const [1, 2, 3, 4, 5, 6, 7];
} }

File diff suppressed because it is too large Load diff

View file

@ -96,6 +96,13 @@ void main() {
image: 'https://pbs.twimg.com/media/ECF8Z5KWwAIBZ6o.jpg:large', image: 'https://pbs.twimg.com/media/ECF8Z5KWwAIBZ6o.jpg:large',
vendor: Vendor.twitterPosting, vendor: Vendor.twitterPosting,
), ),
LinkParserTest(
title: 'twonly Public Launch',
desc:
'After about a year of development, twonly is finally ready for its public launch.',
url: 'https://twonly.eu/en/blog/2026-public-launch.html',
image: 'https://twonly.eu/assets/blog/2026-public-launch.webp',
),
]; ];
for (final testCase in testCases) { for (final testCase in testCases) {