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()(); BlobColumn get myGroupPrivateKey => blob().nullable()();
TextColumn get groupName => text()(); TextColumn get groupName => text()();
TextColumn get draftMessage => text().nullable()();
IntColumn get totalMediaCounter => integer().withDefault(const Constant(0))(); IntColumn get totalMediaCounter => integer().withDefault(const Constant(0))();

View file

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

View file

@ -1128,8 +1128,447 @@ i1.GeneratedColumn<int> _column_99(String aliasedName) =>
i1.GeneratedColumn<int>( i1.GeneratedColumn<int>(
'new_delete_messages_after_milliseconds', aliasedName, true, 'new_delete_messages_after_milliseconds', aliasedName, true,
type: i1.DriftSqlType.int); 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({ 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,
}) { }) {
return (currentVersion, database) async { return (currentVersion, database) async {
switch (currentVersion) { switch (currentVersion) {
@ -1138,6 +1577,11 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema); final migrator = i1.Migrator(database, schema);
await from1To2(migrator, schema); await from1To2(migrator, schema);
return 2; return 2;
case 2:
final schema = Schema3(database: database);
final migrator = i1.Migrator(database, schema);
await from2To3(migrator, schema);
return 3;
default: default:
throw ArgumentError.value('Unknown migration from $currentVersion'); throw ArgumentError.value('Unknown migration from $currentVersion');
} }
@ -1146,8 +1590,10 @@ i0.MigrationStepWithVersion migrationSteps({
i1.OnUpgrade stepByStep({ i1.OnUpgrade stepByStep({
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,
}) => }) =>
i0.VersionedSchema.stepByStepHelper( i0.VersionedSchema.stepByStepHelper(
step: migrationSteps( step: migrationSteps(
from1To2: from1To2, 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.", "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?", "avatarSaveChanges": "Möchtest du die Änderungen speichern?",
"avatarSaveChangesStore": "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.", "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?", "avatarSaveChanges": "Would you like to save the changes?",
"avatarSaveChangesStore": "Save", "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: /// In en, this message translates to:
/// **'Discard'** /// **'Discard'**
String get avatarSaveChangesDiscard; 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 class _AppLocalizationsDelegate

View file

@ -1504,4 +1504,10 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get avatarSaveChangesDiscard => 'Verwerfen'; 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 @override
String get avatarSaveChangesDiscard => 'Discard'; 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 textMessage,
String? quotesMessageId, String? quotesMessageId,
) async { ) async {
await twonlyDB.groupsDao.updateGroup(
groupId,
const GroupsCompanion(
draftMessage: Value(null),
),
);
final message = await twonlyDB.messagesDao.insertMessage( final message = await twonlyDB.messagesDao.insertMessage(
MessagesCompanion( MessagesCompanion(
groupId: Value(groupId), groupId: Value(groupId),

View file

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

View file

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

View file

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

View file

@ -2,6 +2,7 @@ import 'dart:collection';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.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/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';
@ -45,6 +46,7 @@ class MessageSendStateIcon extends StatefulWidget {
this.mediaFiles, { this.mediaFiles, {
super.key, super.key,
this.canBeReopened = false, this.canBeReopened = false,
this.group,
this.lastReaction, this.lastReaction,
this.mainAxisAlignment = MainAxisAlignment.end, this.mainAxisAlignment = MainAxisAlignment.end,
}); });
@ -53,6 +55,7 @@ class MessageSendStateIcon extends StatefulWidget {
final Reaction? lastReaction; final Reaction? lastReaction;
final MainAxisAlignment mainAxisAlignment; final MainAxisAlignment mainAxisAlignment;
final bool canBeReopened; final bool canBeReopened;
final Group? group;
@override @override
State<MessageSendStateIcon> createState() => _MessageSendStateIconState(); State<MessageSendStateIcon> createState() => _MessageSendStateIconState();
@ -172,7 +175,7 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
}; };
} }
if (mediaFile.uploadState == UploadState.preprocessing) { if (mediaFile.uploadState == UploadState.preprocessing) {
text = 'Wird verarbeitet'; text = context.lang.inProcess;
} }
} }
@ -221,10 +224,9 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
} }
} }
if (widget.lastReaction != null && if (!widget.messages.any((t) => t.openedAt == null)) {
!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. /// No messages are still open, so check if the reaction is the last message received.
if (!widget.messages if (!widget.messages
.any((m) => m.createdAt.isAfter(widget.lastReaction!.createdAt))) { .any((m) => m.createdAt.isAfter(widget.lastReaction!.createdAt))) {
if (EmojiAnimation.animatedIcons if (EmojiAnimation.animatedIcons
@ -255,6 +257,17 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
// 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)}',
);
}
}
if (icons.isEmpty) return Container(); if (icons.isEmpty) return Container();

View file

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

View file

@ -5,6 +5,7 @@ import 'package:drift/drift.dart';
import 'package:drift/internal/migrations.dart'; import 'package:drift/internal/migrations.dart';
import 'schema_v1.dart' as v1; import 'schema_v1.dart' as v1;
import 'schema_v2.dart' as v2; import 'schema_v2.dart' as v2;
import 'schema_v3.dart' as v3;
class GeneratedHelper implements SchemaInstantiationHelper { class GeneratedHelper implements SchemaInstantiationHelper {
@override @override
@ -14,10 +15,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v1.DatabaseAtV1(db); return v1.DatabaseAtV1(db);
case 2: case 2:
return v2.DatabaseAtV2(db); return v2.DatabaseAtV2(db);
case 3:
return v3.DatabaseAtV3(db);
default: default:
throw MissingSchemaException(version, versions); 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