This commit is contained in:
otsmr 2025-11-13 22:46:23 +01:00
parent 58b286526d
commit fa85dbd6e2
18 changed files with 7042 additions and 43 deletions

File diff suppressed because one or more lines are too long

View file

@ -23,6 +23,7 @@ class Groups extends Table {
BlobColumn get myGroupPrivateKey => blob().nullable()();
TextColumn get groupName => text()();
TextColumn get draftMessage => text().nullable()();
IntColumn get totalMediaCounter => integer().withDefault(const Constant(0))();

View file

@ -67,7 +67,7 @@ class TwonlyDB extends _$TwonlyDB {
TwonlyDB.forTesting(DatabaseConnection super.connection);
@override
int get schemaVersion => 2;
int get schemaVersion => 3;
static QueryExecutor _openConnection() {
return driftDatabase(
@ -89,6 +89,9 @@ class TwonlyDB extends _$TwonlyDB {
await m.addColumn(schema.messages, schema.messages.mediaReopened);
await m.dropColumn(schema.mediaFiles, 'reopen_by_contact');
},
from2To3: (m, schema) async {
await m.addColumn(schema.groups, schema.groups.draftMessage);
},
),
);
}

View file

@ -760,6 +760,12 @@ class $GroupsTable extends Groups with TableInfo<$GroupsTable, Group> {
late final GeneratedColumn<String> groupName = GeneratedColumn<String>(
'group_name', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
static const VerificationMeta _draftMessageMeta =
const VerificationMeta('draftMessage');
@override
late final GeneratedColumn<String> draftMessage = GeneratedColumn<String>(
'draft_message', aliasedName, true,
type: DriftSqlType.string, requiredDuringInsert: false);
static const VerificationMeta _totalMediaCounterMeta =
const VerificationMeta('totalMediaCounter');
@override
@ -863,6 +869,7 @@ class $GroupsTable extends Groups with TableInfo<$GroupsTable, Group> {
stateEncryptionKey,
myGroupPrivateKey,
groupName,
draftMessage,
totalMediaCounter,
alsoBestFriend,
deleteMessagesAfterMilliseconds,
@ -952,6 +959,12 @@ class $GroupsTable extends Groups with TableInfo<$GroupsTable, Group> {
} else if (isInserting) {
context.missing(_groupNameMeta);
}
if (data.containsKey('draft_message')) {
context.handle(
_draftMessageMeta,
draftMessage.isAcceptableOrUnknown(
data['draft_message']!, _draftMessageMeta));
}
if (data.containsKey('total_media_counter')) {
context.handle(
_totalMediaCounterMeta,
@ -1056,6 +1069,8 @@ class $GroupsTable extends Groups with TableInfo<$GroupsTable, Group> {
DriftSqlType.blob, data['${effectivePrefix}my_group_private_key']),
groupName: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}group_name'])!,
draftMessage: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}draft_message']),
totalMediaCounter: attachedDatabase.typeMapping.read(
DriftSqlType.int, data['${effectivePrefix}total_media_counter'])!,
alsoBestFriend: attachedDatabase.typeMapping
@ -1107,6 +1122,7 @@ class Group extends DataClass implements Insertable<Group> {
final Uint8List? stateEncryptionKey;
final Uint8List? myGroupPrivateKey;
final String groupName;
final String? draftMessage;
final int totalMediaCounter;
final bool alsoBestFriend;
final int deleteMessagesAfterMilliseconds;
@ -1132,6 +1148,7 @@ class Group extends DataClass implements Insertable<Group> {
this.stateEncryptionKey,
this.myGroupPrivateKey,
required this.groupName,
this.draftMessage,
required this.totalMediaCounter,
required this.alsoBestFriend,
required this.deleteMessagesAfterMilliseconds,
@ -1163,6 +1180,9 @@ class Group extends DataClass implements Insertable<Group> {
map['my_group_private_key'] = Variable<Uint8List>(myGroupPrivateKey);
}
map['group_name'] = Variable<String>(groupName);
if (!nullToAbsent || draftMessage != null) {
map['draft_message'] = Variable<String>(draftMessage);
}
map['total_media_counter'] = Variable<int>(totalMediaCounter);
map['also_best_friend'] = Variable<bool>(alsoBestFriend);
map['delete_messages_after_milliseconds'] =
@ -1208,6 +1228,9 @@ class Group extends DataClass implements Insertable<Group> {
? const Value.absent()
: Value(myGroupPrivateKey),
groupName: Value(groupName),
draftMessage: draftMessage == null && nullToAbsent
? const Value.absent()
: Value(draftMessage),
totalMediaCounter: Value(totalMediaCounter),
alsoBestFriend: Value(alsoBestFriend),
deleteMessagesAfterMilliseconds: Value(deleteMessagesAfterMilliseconds),
@ -1251,6 +1274,7 @@ class Group extends DataClass implements Insertable<Group> {
myGroupPrivateKey:
serializer.fromJson<Uint8List?>(json['myGroupPrivateKey']),
groupName: serializer.fromJson<String>(json['groupName']),
draftMessage: serializer.fromJson<String?>(json['draftMessage']),
totalMediaCounter: serializer.fromJson<int>(json['totalMediaCounter']),
alsoBestFriend: serializer.fromJson<bool>(json['alsoBestFriend']),
deleteMessagesAfterMilliseconds:
@ -1286,6 +1310,7 @@ class Group extends DataClass implements Insertable<Group> {
'stateEncryptionKey': serializer.toJson<Uint8List?>(stateEncryptionKey),
'myGroupPrivateKey': serializer.toJson<Uint8List?>(myGroupPrivateKey),
'groupName': serializer.toJson<String>(groupName),
'draftMessage': serializer.toJson<String?>(draftMessage),
'totalMediaCounter': serializer.toJson<int>(totalMediaCounter),
'alsoBestFriend': serializer.toJson<bool>(alsoBestFriend),
'deleteMessagesAfterMilliseconds':
@ -1316,6 +1341,7 @@ class Group extends DataClass implements Insertable<Group> {
Value<Uint8List?> stateEncryptionKey = const Value.absent(),
Value<Uint8List?> myGroupPrivateKey = const Value.absent(),
String? groupName,
Value<String?> draftMessage = const Value.absent(),
int? totalMediaCounter,
bool? alsoBestFriend,
int? deleteMessagesAfterMilliseconds,
@ -1345,6 +1371,8 @@ class Group extends DataClass implements Insertable<Group> {
? myGroupPrivateKey.value
: this.myGroupPrivateKey,
groupName: groupName ?? this.groupName,
draftMessage:
draftMessage.present ? draftMessage.value : this.draftMessage,
totalMediaCounter: totalMediaCounter ?? this.totalMediaCounter,
alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend,
deleteMessagesAfterMilliseconds: deleteMessagesAfterMilliseconds ??
@ -1395,6 +1423,9 @@ class Group extends DataClass implements Insertable<Group> {
? data.myGroupPrivateKey.value
: this.myGroupPrivateKey,
groupName: data.groupName.present ? data.groupName.value : this.groupName,
draftMessage: data.draftMessage.present
? data.draftMessage.value
: this.draftMessage,
totalMediaCounter: data.totalMediaCounter.present
? data.totalMediaCounter.value
: this.totalMediaCounter,
@ -1448,6 +1479,7 @@ class Group extends DataClass implements Insertable<Group> {
..write('stateEncryptionKey: $stateEncryptionKey, ')
..write('myGroupPrivateKey: $myGroupPrivateKey, ')
..write('groupName: $groupName, ')
..write('draftMessage: $draftMessage, ')
..write('totalMediaCounter: $totalMediaCounter, ')
..write('alsoBestFriend: $alsoBestFriend, ')
..write(
@ -1479,6 +1511,7 @@ class Group extends DataClass implements Insertable<Group> {
$driftBlobEquality.hash(stateEncryptionKey),
$driftBlobEquality.hash(myGroupPrivateKey),
groupName,
draftMessage,
totalMediaCounter,
alsoBestFriend,
deleteMessagesAfterMilliseconds,
@ -1510,6 +1543,7 @@ class Group extends DataClass implements Insertable<Group> {
$driftBlobEquality.equals(
other.myGroupPrivateKey, this.myGroupPrivateKey) &&
other.groupName == this.groupName &&
other.draftMessage == this.draftMessage &&
other.totalMediaCounter == this.totalMediaCounter &&
other.alsoBestFriend == this.alsoBestFriend &&
other.deleteMessagesAfterMilliseconds ==
@ -1538,6 +1572,7 @@ class GroupsCompanion extends UpdateCompanion<Group> {
final Value<Uint8List?> stateEncryptionKey;
final Value<Uint8List?> myGroupPrivateKey;
final Value<String> groupName;
final Value<String?> draftMessage;
final Value<int> totalMediaCounter;
final Value<bool> alsoBestFriend;
final Value<int> deleteMessagesAfterMilliseconds;
@ -1564,6 +1599,7 @@ class GroupsCompanion extends UpdateCompanion<Group> {
this.stateEncryptionKey = const Value.absent(),
this.myGroupPrivateKey = const Value.absent(),
this.groupName = const Value.absent(),
this.draftMessage = const Value.absent(),
this.totalMediaCounter = const Value.absent(),
this.alsoBestFriend = const Value.absent(),
this.deleteMessagesAfterMilliseconds = const Value.absent(),
@ -1591,6 +1627,7 @@ class GroupsCompanion extends UpdateCompanion<Group> {
this.stateEncryptionKey = const Value.absent(),
this.myGroupPrivateKey = const Value.absent(),
required String groupName,
this.draftMessage = const Value.absent(),
this.totalMediaCounter = const Value.absent(),
this.alsoBestFriend = const Value.absent(),
this.deleteMessagesAfterMilliseconds = const Value.absent(),
@ -1619,6 +1656,7 @@ class GroupsCompanion extends UpdateCompanion<Group> {
Expression<Uint8List>? stateEncryptionKey,
Expression<Uint8List>? myGroupPrivateKey,
Expression<String>? groupName,
Expression<String>? draftMessage,
Expression<int>? totalMediaCounter,
Expression<bool>? alsoBestFriend,
Expression<int>? deleteMessagesAfterMilliseconds,
@ -1647,6 +1685,7 @@ class GroupsCompanion extends UpdateCompanion<Group> {
'state_encryption_key': stateEncryptionKey,
if (myGroupPrivateKey != null) 'my_group_private_key': myGroupPrivateKey,
if (groupName != null) 'group_name': groupName,
if (draftMessage != null) 'draft_message': draftMessage,
if (totalMediaCounter != null) 'total_media_counter': totalMediaCounter,
if (alsoBestFriend != null) 'also_best_friend': alsoBestFriend,
if (deleteMessagesAfterMilliseconds != null)
@ -1681,6 +1720,7 @@ class GroupsCompanion extends UpdateCompanion<Group> {
Value<Uint8List?>? stateEncryptionKey,
Value<Uint8List?>? myGroupPrivateKey,
Value<String>? groupName,
Value<String?>? draftMessage,
Value<int>? totalMediaCounter,
Value<bool>? alsoBestFriend,
Value<int>? deleteMessagesAfterMilliseconds,
@ -1707,6 +1747,7 @@ class GroupsCompanion extends UpdateCompanion<Group> {
stateEncryptionKey: stateEncryptionKey ?? this.stateEncryptionKey,
myGroupPrivateKey: myGroupPrivateKey ?? this.myGroupPrivateKey,
groupName: groupName ?? this.groupName,
draftMessage: draftMessage ?? this.draftMessage,
totalMediaCounter: totalMediaCounter ?? this.totalMediaCounter,
alsoBestFriend: alsoBestFriend ?? this.alsoBestFriend,
deleteMessagesAfterMilliseconds: deleteMessagesAfterMilliseconds ??
@ -1766,6 +1807,9 @@ class GroupsCompanion extends UpdateCompanion<Group> {
if (groupName.present) {
map['group_name'] = Variable<String>(groupName.value);
}
if (draftMessage.present) {
map['draft_message'] = Variable<String>(draftMessage.value);
}
if (totalMediaCounter.present) {
map['total_media_counter'] = Variable<int>(totalMediaCounter.value);
}
@ -1828,6 +1872,7 @@ class GroupsCompanion extends UpdateCompanion<Group> {
..write('stateEncryptionKey: $stateEncryptionKey, ')
..write('myGroupPrivateKey: $myGroupPrivateKey, ')
..write('groupName: $groupName, ')
..write('draftMessage: $draftMessage, ')
..write('totalMediaCounter: $totalMediaCounter, ')
..write('alsoBestFriend: $alsoBestFriend, ')
..write(
@ -8495,6 +8540,7 @@ typedef $$GroupsTableCreateCompanionBuilder = GroupsCompanion Function({
Value<Uint8List?> stateEncryptionKey,
Value<Uint8List?> myGroupPrivateKey,
required String groupName,
Value<String?> draftMessage,
Value<int> totalMediaCounter,
Value<bool> alsoBestFriend,
Value<int> deleteMessagesAfterMilliseconds,
@ -8522,6 +8568,7 @@ typedef $$GroupsTableUpdateCompanionBuilder = GroupsCompanion Function({
Value<Uint8List?> stateEncryptionKey,
Value<Uint8List?> myGroupPrivateKey,
Value<String> groupName,
Value<String?> draftMessage,
Value<int> totalMediaCounter,
Value<bool> alsoBestFriend,
Value<int> deleteMessagesAfterMilliseconds,
@ -8637,6 +8684,9 @@ class $$GroupsTableFilterComposer extends Composer<_$TwonlyDB, $GroupsTable> {
ColumnFilters<String> get groupName => $composableBuilder(
column: $table.groupName, builder: (column) => ColumnFilters(column));
ColumnFilters<String> get draftMessage => $composableBuilder(
column: $table.draftMessage, builder: (column) => ColumnFilters(column));
ColumnFilters<int> get totalMediaCounter => $composableBuilder(
column: $table.totalMediaCounter,
builder: (column) => ColumnFilters(column));
@ -8796,6 +8846,10 @@ class $$GroupsTableOrderingComposer extends Composer<_$TwonlyDB, $GroupsTable> {
ColumnOrderings<String> get groupName => $composableBuilder(
column: $table.groupName, builder: (column) => ColumnOrderings(column));
ColumnOrderings<String> get draftMessage => $composableBuilder(
column: $table.draftMessage,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<int> get totalMediaCounter => $composableBuilder(
column: $table.totalMediaCounter,
builder: (column) => ColumnOrderings(column));
@ -8890,6 +8944,9 @@ class $$GroupsTableAnnotationComposer
GeneratedColumn<String> get groupName =>
$composableBuilder(column: $table.groupName, builder: (column) => column);
GeneratedColumn<String> get draftMessage => $composableBuilder(
column: $table.draftMessage, builder: (column) => column);
GeneratedColumn<int> get totalMediaCounter => $composableBuilder(
column: $table.totalMediaCounter, builder: (column) => column);
@ -9028,6 +9085,7 @@ class $$GroupsTableTableManager extends RootTableManager<
Value<Uint8List?> stateEncryptionKey = const Value.absent(),
Value<Uint8List?> myGroupPrivateKey = const Value.absent(),
Value<String> groupName = const Value.absent(),
Value<String?> draftMessage = const Value.absent(),
Value<int> totalMediaCounter = const Value.absent(),
Value<bool> alsoBestFriend = const Value.absent(),
Value<int> deleteMessagesAfterMilliseconds = const Value.absent(),
@ -9055,6 +9113,7 @@ class $$GroupsTableTableManager extends RootTableManager<
stateEncryptionKey: stateEncryptionKey,
myGroupPrivateKey: myGroupPrivateKey,
groupName: groupName,
draftMessage: draftMessage,
totalMediaCounter: totalMediaCounter,
alsoBestFriend: alsoBestFriend,
deleteMessagesAfterMilliseconds: deleteMessagesAfterMilliseconds,
@ -9082,6 +9141,7 @@ class $$GroupsTableTableManager extends RootTableManager<
Value<Uint8List?> stateEncryptionKey = const Value.absent(),
Value<Uint8List?> myGroupPrivateKey = const Value.absent(),
required String groupName,
Value<String?> draftMessage = const Value.absent(),
Value<int> totalMediaCounter = const Value.absent(),
Value<bool> alsoBestFriend = const Value.absent(),
Value<int> deleteMessagesAfterMilliseconds = const Value.absent(),
@ -9109,6 +9169,7 @@ class $$GroupsTableTableManager extends RootTableManager<
stateEncryptionKey: stateEncryptionKey,
myGroupPrivateKey: myGroupPrivateKey,
groupName: groupName,
draftMessage: draftMessage,
totalMediaCounter: totalMediaCounter,
alsoBestFriend: alsoBestFriend,
deleteMessagesAfterMilliseconds: deleteMessagesAfterMilliseconds,

View file

@ -1128,8 +1128,447 @@ i1.GeneratedColumn<int> _column_99(String aliasedName) =>
i1.GeneratedColumn<int>(
'new_delete_messages_after_milliseconds', aliasedName, true,
type: i1.DriftSqlType.int);
final class Schema3 extends i0.VersionedSchema {
Schema3({required super.database}) : super(version: 3);
@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 Shape2 mediaFiles = Shape2(
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_12,
],
attachedDatabase: database,
),
alias: null);
late final Shape3 messages = Shape3(
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_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 Shape7 receipts = Shape7(
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_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_96,
_column_97,
_column_98,
_column_99,
_column_37,
_column_93,
],
attachedDatabase: database,
),
alias: null);
}
class Shape17 extends i0.VersionedTable {
Shape17({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get groupId =>
columnsByName['group_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<bool> get isGroupAdmin =>
columnsByName['is_group_admin']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get isDirectChat =>
columnsByName['is_direct_chat']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get pinned =>
columnsByName['pinned']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get archived =>
columnsByName['archived']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get joinedGroup =>
columnsByName['joined_group']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get leftGroup =>
columnsByName['left_group']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get deletedContent =>
columnsByName['deleted_content']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<int> get stateVersionId =>
columnsByName['state_version_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<i2.Uint8List> get stateEncryptionKey =>
columnsByName['state_encryption_key']!
as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<i2.Uint8List> get myGroupPrivateKey =>
columnsByName['my_group_private_key']!
as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<String> get groupName =>
columnsByName['group_name']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get draftMessage =>
columnsByName['draft_message']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get totalMediaCounter =>
columnsByName['total_media_counter']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<bool> get alsoBestFriend =>
columnsByName['also_best_friend']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<int> get deleteMessagesAfterMilliseconds =>
columnsByName['delete_messages_after_milliseconds']!
as i1.GeneratedColumn<int>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get lastMessageSend =>
columnsByName['last_message_send']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get lastMessageReceived =>
columnsByName['last_message_received']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get lastFlameCounterChange =>
columnsByName['last_flame_counter_change']!
as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get lastFlameSync =>
columnsByName['last_flame_sync']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<int> get flameCounter =>
columnsByName['flame_counter']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<int> get maxFlameCounter =>
columnsByName['max_flame_counter']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<DateTime> get maxFlameCounterFrom =>
columnsByName['max_flame_counter_from']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get lastMessageExchange =>
columnsByName['last_message_exchange']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<String> _column_100(String aliasedName) =>
i1.GeneratedColumn<String>('draft_message', aliasedName, true,
type: i1.DriftSqlType.string);
i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
}) {
return (currentVersion, database) async {
switch (currentVersion) {
@ -1138,6 +1577,11 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema);
await from1To2(migrator, schema);
return 2;
case 2:
final schema = Schema3(database: database);
final migrator = i1.Migrator(database, schema);
await from2To3(migrator, schema);
return 3;
default:
throw ArgumentError.value('Unknown migration from $currentVersion');
}
@ -1146,8 +1590,10 @@ i0.MigrationStepWithVersion migrationSteps({
i1.OnUpgrade stepByStep({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
}) =>
i0.VersionedSchema.stepByStepHelper(
step: migrationSteps(
from1To2: from1To2,
from2To3: from2To3,
));

View file

@ -825,5 +825,7 @@
"allowErrorTrackingSubtitle": "Wenn twonly abstürzt oder Fehler auftreten, werden diese automatisch an unsere selbst gehostete Glitchtip-Instanz gemeldet. Persönliche Daten wie Nachrichten oder Bilder werden niemals hochgeladen.",
"avatarSaveChanges": "Möchtest du die Änderungen speichern?",
"avatarSaveChangesStore": "Speichern",
"avatarSaveChangesDiscard": "Verwerfen"
"avatarSaveChangesDiscard": "Verwerfen",
"inProcess": "Wird verarbeitet",
"draftMessage": "Entwurf"
}

View file

@ -603,5 +603,7 @@
"allowErrorTrackingSubtitle": "If twonly crashes or errors occur, these are automatically reported to our self-hosted Glitchtip instance. Personal data such as messages or images are never uploaded.",
"avatarSaveChanges": "Would you like to save the changes?",
"avatarSaveChangesStore": "Save",
"avatarSaveChangesDiscard": "Discard"
"avatarSaveChangesDiscard": "Discard",
"inProcess": "In process",
"draftMessage": "Draft"
}

View file

@ -2725,6 +2725,18 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Discard'**
String get avatarSaveChangesDiscard;
/// No description provided for @inProcess.
///
/// In en, this message translates to:
/// **'In process'**
String get inProcess;
/// No description provided for @draftMessage.
///
/// In en, this message translates to:
/// **'Draft'**
String get draftMessage;
}
class _AppLocalizationsDelegate

View file

@ -1504,4 +1504,10 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get avatarSaveChangesDiscard => 'Verwerfen';
@override
String get inProcess => 'Wird verarbeitet';
@override
String get draftMessage => 'Entwurf';
}

View file

@ -1494,4 +1494,10 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get avatarSaveChangesDiscard => 'Discard';
@override
String get inProcess => 'In process';
@override
String get draftMessage => 'Draft';
}

View file

@ -162,6 +162,12 @@ Future<void> insertAndSendTextMessage(
String textMessage,
String? quotesMessageId,
) async {
await twonlyDB.groupsDao.updateGroup(
groupId,
const GroupsCompanion(
draftMessage: Value(null),
),
);
final message = await twonlyDB.messagesDao.insertMessage(
MessagesCompanion(
groupId: Value(groupId),

View file

@ -12,7 +12,7 @@ class EmojiPickerBottom extends StatelessWidget {
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.zero,
height: 450,
height: 480,
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(32),
@ -30,7 +30,7 @@ class EmojiPickerBottom extends StatelessWidget {
child: Column(
children: [
Container(
margin: const EdgeInsets.all(30),
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32),
color: Colors.grey,
@ -52,11 +52,6 @@ class EmojiPickerBottom extends StatelessWidget {
config: Config(
height: 400,
locale: Localizations.localeOf(context),
viewOrderConfig: const ViewOrderConfig(
top: EmojiPickerItem.searchBar,
// middle: EmojiPickerItem.emojiView,
bottom: EmojiPickerItem.categoryBar,
),
emojiTextStyle:
TextStyle(fontSize: 24 * (Platform.isIOS ? 1.2 : 1)),
emojiViewConfig: EmojiViewConfig(
@ -68,7 +63,7 @@ class EmojiPickerBottom extends StatelessWidget {
),
categoryViewConfig: CategoryViewConfig(
backgroundColor: context.color.surfaceContainer,
dividerColor: Colors.white,
dividerColor: context.color.surfaceContainerHigh,
indicatorColor: context.color.primary,
iconColorSelected: context.color.primary,
iconColor: context.color.secondary,

View file

@ -235,6 +235,7 @@ class _UserListItem extends State<GroupListItem> {
_previewMessages,
_previewMediaFiles,
lastReaction: _lastReaction,
group: widget.group,
),
const Text(''),
const SizedBox(width: 5),

View file

@ -1,12 +1,14 @@
import 'dart:async';
import 'dart:io';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:drift/drift.dart' show Value;
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
@ -57,10 +59,13 @@ class _MessageInputState extends State<MessageInput> {
@override
void initState() {
super.initState();
_textFieldController = TextEditingController();
if (widget.group.draftMessage != null) {
_textFieldController.text = widget.group.draftMessage!;
}
widget.textFieldFocus.addListener(_handleTextFocusChange);
_initializeControllers();
super.initState();
}
@override
@ -196,8 +201,15 @@ class _MessageInputState extends State<MessageInput> {
keyboardType: TextInputType.multiline,
maxLines: 4,
minLines: 1,
onChanged: (value) {
onChanged: (value) async {
setState(() {});
await twonlyDB.groupsDao.updateGroup(
widget.group.groupId,
GroupsCompanion(
draftMessage:
Value(_textFieldController.text),
),
);
},
onSubmitted: (_) {
_sendMessage();

View file

@ -2,6 +2,7 @@ import 'dart:collection';
import 'package:collection/collection.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/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/tables/messages.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
@ -45,6 +46,7 @@ class MessageSendStateIcon extends StatefulWidget {
this.mediaFiles, {
super.key,
this.canBeReopened = false,
this.group,
this.lastReaction,
this.mainAxisAlignment = MainAxisAlignment.end,
});
@ -53,6 +55,7 @@ class MessageSendStateIcon extends StatefulWidget {
final Reaction? lastReaction;
final MainAxisAlignment mainAxisAlignment;
final bool canBeReopened;
final Group? group;
@override
State<MessageSendStateIcon> createState() => _MessageSendStateIconState();
@ -172,7 +175,7 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
};
}
if (mediaFile.uploadState == UploadState.preprocessing) {
text = 'Wird verarbeitet';
text = context.lang.inProcess;
}
}
@ -221,38 +224,48 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
}
}
if (widget.lastReaction != null &&
!widget.messages.any((t) => t.openedAt == null)) {
/// No messages are still open, so check if the reaction is the last message received.
if (!widget.messages
.any((m) => m.createdAt.isAfter(widget.lastReaction!.createdAt))) {
if (EmojiAnimation.animatedIcons
.containsKey(widget.lastReaction!.emoji)) {
icons = [
SizedBox(
height: 18,
child: EmojiAnimation(emoji: widget.lastReaction!.emoji),
),
];
} else {
icons = [
SizedBox(
height: 18,
child: Center(
child: Text(
widget.lastReaction!.emoji,
style: const TextStyle(fontSize: 15),
strutStyle: const StrutStyle(
forceStrutHeight: true,
height: 1.4,
if (!widget.messages.any((t) => t.openedAt == null)) {
if (widget.lastReaction != null) {
/// No messages are still open, so check if the reaction is the last message received.
if (!widget.messages
.any((m) => m.createdAt.isAfter(widget.lastReaction!.createdAt))) {
if (EmojiAnimation.animatedIcons
.containsKey(widget.lastReaction!.emoji)) {
icons = [
SizedBox(
height: 18,
child: EmojiAnimation(emoji: widget.lastReaction!.emoji),
),
];
} else {
icons = [
SizedBox(
height: 18,
child: Center(
child: Text(
widget.lastReaction!.emoji,
style: const TextStyle(fontSize: 15),
strutStyle: const StrutStyle(
forceStrutHeight: true,
height: 1.4,
),
),
),
),
),
];
];
}
// Log.info("DISPLAY REACTION");
}
// Log.info("DISPLAY REACTION");
}
if (widget.group != null &&
widget.group!.draftMessage != null &&
widget.group!.draftMessage != '') {
icons = [
const FaIcon(FontAwesomeIcons.pen, size: 12, color: Colors.grey),
];
textWidget = Text(
'${context.lang.draftMessage}: ${substringBy(widget.group!.draftMessage!, 10)}',
);
}
}

View file

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:collection';
import 'package:drift/drift.dart' show Value;
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:lottie/lottie.dart';
@ -642,6 +643,14 @@ class _MediaViewerViewState extends State<MediaViewerView> {
child: TextField(
autofocus: true,
controller: textMessageController,
onChanged: (value) async {
await twonlyDB.groupsDao.updateGroup(
widget.group.groupId,
GroupsCompanion(
draftMessage: Value(textMessageController.text),
),
);
},
onEditingComplete: () {
setState(() {
showSendTextMessageInput = false;

View file

@ -5,6 +5,7 @@ import 'package:drift/drift.dart';
import 'package:drift/internal/migrations.dart';
import 'schema_v1.dart' as v1;
import 'schema_v2.dart' as v2;
import 'schema_v3.dart' as v3;
class GeneratedHelper implements SchemaInstantiationHelper {
@override
@ -14,10 +15,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v1.DatabaseAtV1(db);
case 2:
return v2.DatabaseAtV2(db);
case 3:
return v3.DatabaseAtV3(db);
default:
throw MissingSchemaException(version, versions);
}
}
static const versions = const [1, 2];
static const versions = const [1, 2, 3];
}

File diff suppressed because it is too large Load diff