Compare commits

..

6 commits

Author SHA1 Message Date
Tobi
1f8bdaa32d
Merge pull request #365 from twonlyapp/dev
Some checks failed
Publish on Github / build_and_publish (push) Has been cancelled
- Added an option in the settings to automatically save all sent images
- Hides duplicate images in the memory
- Fixes a bug where messages were not being received
- Several other minor improvements
2025-12-29 23:11:00 +01:00
otsmr
d067a3c931 bump version
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
2025-12-29 23:09:15 +01:00
otsmr
b3c25dd160 fix #364 2025-12-29 23:06:09 +01:00
otsmr
3899c8e6e4 fix #363
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
2025-12-29 22:27:44 +01:00
otsmr
87187843fa fix #360
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
2025-12-29 21:22:32 +01:00
otsmr
ebf53a5ab4 fix #362 2025-12-29 16:13:57 +01:00
30 changed files with 7410 additions and 143 deletions

View file

@ -1,5 +1,12 @@
# Changelog # Changelog
## 0.0.82
- Added an option in the settings to automatically save all sent images
- Hides duplicate images in the memory
- Fixes a bug where messages were not being received
- Several other minor improvements
## 0.0.81 ## 0.0.81
- Fixes the issue where black/blank images were sometimes received - Fixes the issue where black/blank images were sometimes received

View file

@ -100,6 +100,14 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
.get(); .get();
} }
Future<List<MediaFile>> getAllNonHashedStoredMediaFiles() async {
return (select(mediaFiles)
..where(
(t) => t.stored.equals(true) & t.storedFileHash.isNull(),
))
.get();
}
Future<List<MediaFile>> getAllMediaFilesPendingUpload() async { Future<List<MediaFile>> getAllMediaFilesPendingUpload() async {
return (select(mediaFiles) return (select(mediaFiles)
..where( ..where(
@ -111,7 +119,10 @@ class MediaFilesDao extends DatabaseAccessor<TwonlyDB>
} }
Stream<List<MediaFile>> watchAllStoredMediaFiles() { Stream<List<MediaFile>> watchAllStoredMediaFiles() {
return (select(mediaFiles)..where((t) => t.stored.equals(true))).watch(); final query = (select(mediaFiles)..where((t) => t.stored.equals(true)))
.join([])
..groupBy([mediaFiles.storedFileHash]);
return query.map((row) => row.readTable(mediaFiles)).watch();
} }
Stream<List<MediaFile>> watchNewestMediaFiles() { Stream<List<MediaFile>> watchNewestMediaFiles() {

View file

@ -80,10 +80,18 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
} }
} }
Future<List<Receipt>> getReceiptsNotAckByServer() async { Future<List<Receipt>> getReceiptsForRetransmission() async {
final markedRetriesTime = DateTime.now().subtract(
const Duration(
// give the server time to transmit all messages to the client
seconds: 20,
),
);
return (select(receipts) return (select(receipts)
..where( ..where(
(t) => t.ackByServerAt.isNull(), (t) =>
t.ackByServerAt.isNull() |
t.markForRetry.isSmallerThanValue(markedRetriesTime),
)) ))
.get(); .get();
} }
@ -100,6 +108,14 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
.write(updates); .write(updates);
} }
Future<void> markMessagesForRetry(int contactId) async {
await (update(receipts)..where((c) => c.contactId.equals(contactId))).write(
ReceiptsCompanion(
markForRetry: Value(DateTime.now()),
),
);
}
Future<bool> isDuplicated(String receiptId) async { Future<bool> isDuplicated(String receiptId) async {
return await (select(receivedReceipts) return await (select(receivedReceipts)
..where((t) => t.receiptId.equals(receiptId))) ..where((t) => t.receiptId.equals(receiptId)))

File diff suppressed because one or more lines are too long

View file

@ -59,6 +59,8 @@ class MediaFiles extends Table {
BlobColumn get encryptionMac => blob().nullable()(); BlobColumn get encryptionMac => blob().nullable()();
BlobColumn get encryptionNonce => blob().nullable()(); BlobColumn get encryptionNonce => blob().nullable()();
BlobColumn get storedFileHash => blob().nullable()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override @override

View file

@ -20,6 +20,8 @@ class Receipts extends Table {
BoolColumn get contactWillSendsReceipt => BoolColumn get contactWillSendsReceipt =>
boolean().withDefault(const Constant(true))(); boolean().withDefault(const Constant(true))();
DateTimeColumn get markForRetry => dateTime().nullable()();
DateTimeColumn get ackByServerAt => dateTime().nullable()(); DateTimeColumn get ackByServerAt => dateTime().nullable()();
IntColumn get retryCount => integer().withDefault(const Constant(0))(); IntColumn get retryCount => 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 => 4; int get schemaVersion => 5;
static QueryExecutor _openConnection() { static QueryExecutor _openConnection() {
return driftDatabase( return driftDatabase(
@ -103,6 +103,13 @@ class TwonlyDB extends _$TwonlyDB {
), ),
); );
}, },
from4To5: (m, schema) async {
await m.addColumn(schema.receipts, schema.receipts.markForRetry);
await m.addColumn(
schema.mediaFiles,
schema.mediaFiles.storedFileHash,
);
},
), ),
); );
} }

View file

@ -1996,6 +1996,12 @@ class $MediaFilesTable extends MediaFiles
late final GeneratedColumn<Uint8List> encryptionNonce = late final GeneratedColumn<Uint8List> encryptionNonce =
GeneratedColumn<Uint8List>('encryption_nonce', aliasedName, true, GeneratedColumn<Uint8List>('encryption_nonce', aliasedName, true,
type: DriftSqlType.blob, requiredDuringInsert: false); type: DriftSqlType.blob, requiredDuringInsert: false);
static const VerificationMeta _storedFileHashMeta =
const VerificationMeta('storedFileHash');
@override
late final GeneratedColumn<Uint8List> storedFileHash =
GeneratedColumn<Uint8List>('stored_file_hash', aliasedName, true,
type: DriftSqlType.blob, requiredDuringInsert: false);
static const VerificationMeta _createdAtMeta = static const VerificationMeta _createdAtMeta =
const VerificationMeta('createdAt'); const VerificationMeta('createdAt');
@override @override
@ -2020,6 +2026,7 @@ class $MediaFilesTable extends MediaFiles
encryptionKey, encryptionKey,
encryptionMac, encryptionMac,
encryptionNonce, encryptionNonce,
storedFileHash,
createdAt createdAt
]; ];
@override @override
@ -2091,6 +2098,12 @@ class $MediaFilesTable extends MediaFiles
encryptionNonce.isAcceptableOrUnknown( encryptionNonce.isAcceptableOrUnknown(
data['encryption_nonce']!, _encryptionNonceMeta)); data['encryption_nonce']!, _encryptionNonceMeta));
} }
if (data.containsKey('stored_file_hash')) {
context.handle(
_storedFileHashMeta,
storedFileHash.isAcceptableOrUnknown(
data['stored_file_hash']!, _storedFileHashMeta));
}
if (data.containsKey('created_at')) { if (data.containsKey('created_at')) {
context.handle(_createdAtMeta, context.handle(_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
@ -2137,6 +2150,8 @@ class $MediaFilesTable extends MediaFiles
.read(DriftSqlType.blob, data['${effectivePrefix}encryption_mac']), .read(DriftSqlType.blob, data['${effectivePrefix}encryption_mac']),
encryptionNonce: attachedDatabase.typeMapping encryptionNonce: attachedDatabase.typeMapping
.read(DriftSqlType.blob, data['${effectivePrefix}encryption_nonce']), .read(DriftSqlType.blob, data['${effectivePrefix}encryption_nonce']),
storedFileHash: attachedDatabase.typeMapping
.read(DriftSqlType.blob, data['${effectivePrefix}stored_file_hash']),
createdAt: attachedDatabase.typeMapping createdAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
); );
@ -2181,6 +2196,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
final Uint8List? encryptionKey; final Uint8List? encryptionKey;
final Uint8List? encryptionMac; final Uint8List? encryptionMac;
final Uint8List? encryptionNonce; final Uint8List? encryptionNonce;
final Uint8List? storedFileHash;
final DateTime createdAt; final DateTime createdAt;
const MediaFile( const MediaFile(
{required this.mediaId, {required this.mediaId,
@ -2197,6 +2213,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
this.encryptionKey, this.encryptionKey,
this.encryptionMac, this.encryptionMac,
this.encryptionNonce, this.encryptionNonce,
this.storedFileHash,
required this.createdAt}); required this.createdAt});
@override @override
Map<String, Expression> toColumns(bool nullToAbsent) { Map<String, Expression> toColumns(bool nullToAbsent) {
@ -2241,6 +2258,9 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
if (!nullToAbsent || encryptionNonce != null) { if (!nullToAbsent || encryptionNonce != null) {
map['encryption_nonce'] = Variable<Uint8List>(encryptionNonce); map['encryption_nonce'] = Variable<Uint8List>(encryptionNonce);
} }
if (!nullToAbsent || storedFileHash != null) {
map['stored_file_hash'] = Variable<Uint8List>(storedFileHash);
}
map['created_at'] = Variable<DateTime>(createdAt); map['created_at'] = Variable<DateTime>(createdAt);
return map; return map;
} }
@ -2280,6 +2300,9 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
encryptionNonce: encryptionNonce == null && nullToAbsent encryptionNonce: encryptionNonce == null && nullToAbsent
? const Value.absent() ? const Value.absent()
: Value(encryptionNonce), : Value(encryptionNonce),
storedFileHash: storedFileHash == null && nullToAbsent
? const Value.absent()
: Value(storedFileHash),
createdAt: Value(createdAt), createdAt: Value(createdAt),
); );
} }
@ -2308,6 +2331,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
encryptionKey: serializer.fromJson<Uint8List?>(json['encryptionKey']), encryptionKey: serializer.fromJson<Uint8List?>(json['encryptionKey']),
encryptionMac: serializer.fromJson<Uint8List?>(json['encryptionMac']), encryptionMac: serializer.fromJson<Uint8List?>(json['encryptionMac']),
encryptionNonce: serializer.fromJson<Uint8List?>(json['encryptionNonce']), encryptionNonce: serializer.fromJson<Uint8List?>(json['encryptionNonce']),
storedFileHash: serializer.fromJson<Uint8List?>(json['storedFileHash']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']), createdAt: serializer.fromJson<DateTime>(json['createdAt']),
); );
} }
@ -2333,6 +2357,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
'encryptionKey': serializer.toJson<Uint8List?>(encryptionKey), 'encryptionKey': serializer.toJson<Uint8List?>(encryptionKey),
'encryptionMac': serializer.toJson<Uint8List?>(encryptionMac), 'encryptionMac': serializer.toJson<Uint8List?>(encryptionMac),
'encryptionNonce': serializer.toJson<Uint8List?>(encryptionNonce), 'encryptionNonce': serializer.toJson<Uint8List?>(encryptionNonce),
'storedFileHash': serializer.toJson<Uint8List?>(storedFileHash),
'createdAt': serializer.toJson<DateTime>(createdAt), 'createdAt': serializer.toJson<DateTime>(createdAt),
}; };
} }
@ -2352,6 +2377,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
Value<Uint8List?> encryptionKey = const Value.absent(), Value<Uint8List?> encryptionKey = const Value.absent(),
Value<Uint8List?> encryptionMac = const Value.absent(), Value<Uint8List?> encryptionMac = const Value.absent(),
Value<Uint8List?> encryptionNonce = const Value.absent(), Value<Uint8List?> encryptionNonce = const Value.absent(),
Value<Uint8List?> storedFileHash = const Value.absent(),
DateTime? createdAt}) => DateTime? createdAt}) =>
MediaFile( MediaFile(
mediaId: mediaId ?? this.mediaId, mediaId: mediaId ?? this.mediaId,
@ -2379,6 +2405,8 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
encryptionNonce: encryptionNonce.present encryptionNonce: encryptionNonce.present
? encryptionNonce.value ? encryptionNonce.value
: this.encryptionNonce, : this.encryptionNonce,
storedFileHash:
storedFileHash.present ? storedFileHash.value : this.storedFileHash,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
); );
MediaFile copyWithCompanion(MediaFilesCompanion data) { MediaFile copyWithCompanion(MediaFilesCompanion data) {
@ -2417,6 +2445,9 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
encryptionNonce: data.encryptionNonce.present encryptionNonce: data.encryptionNonce.present
? data.encryptionNonce.value ? data.encryptionNonce.value
: this.encryptionNonce, : this.encryptionNonce,
storedFileHash: data.storedFileHash.present
? data.storedFileHash.value
: this.storedFileHash,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
); );
} }
@ -2438,6 +2469,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
..write('encryptionKey: $encryptionKey, ') ..write('encryptionKey: $encryptionKey, ')
..write('encryptionMac: $encryptionMac, ') ..write('encryptionMac: $encryptionMac, ')
..write('encryptionNonce: $encryptionNonce, ') ..write('encryptionNonce: $encryptionNonce, ')
..write('storedFileHash: $storedFileHash, ')
..write('createdAt: $createdAt') ..write('createdAt: $createdAt')
..write(')')) ..write(')'))
.toString(); .toString();
@ -2459,6 +2491,7 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
$driftBlobEquality.hash(encryptionKey), $driftBlobEquality.hash(encryptionKey),
$driftBlobEquality.hash(encryptionMac), $driftBlobEquality.hash(encryptionMac),
$driftBlobEquality.hash(encryptionNonce), $driftBlobEquality.hash(encryptionNonce),
$driftBlobEquality.hash(storedFileHash),
createdAt); createdAt);
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
@ -2479,6 +2512,8 @@ class MediaFile extends DataClass implements Insertable<MediaFile> {
$driftBlobEquality.equals(other.encryptionMac, this.encryptionMac) && $driftBlobEquality.equals(other.encryptionMac, this.encryptionMac) &&
$driftBlobEquality.equals( $driftBlobEquality.equals(
other.encryptionNonce, this.encryptionNonce) && other.encryptionNonce, this.encryptionNonce) &&
$driftBlobEquality.equals(
other.storedFileHash, this.storedFileHash) &&
other.createdAt == this.createdAt); other.createdAt == this.createdAt);
} }
@ -2497,6 +2532,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
final Value<Uint8List?> encryptionKey; final Value<Uint8List?> encryptionKey;
final Value<Uint8List?> encryptionMac; final Value<Uint8List?> encryptionMac;
final Value<Uint8List?> encryptionNonce; final Value<Uint8List?> encryptionNonce;
final Value<Uint8List?> storedFileHash;
final Value<DateTime> createdAt; final Value<DateTime> createdAt;
final Value<int> rowid; final Value<int> rowid;
const MediaFilesCompanion({ const MediaFilesCompanion({
@ -2514,6 +2550,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
this.encryptionKey = const Value.absent(), this.encryptionKey = const Value.absent(),
this.encryptionMac = const Value.absent(), this.encryptionMac = const Value.absent(),
this.encryptionNonce = const Value.absent(), this.encryptionNonce = const Value.absent(),
this.storedFileHash = const Value.absent(),
this.createdAt = const Value.absent(), this.createdAt = const Value.absent(),
this.rowid = const Value.absent(), this.rowid = const Value.absent(),
}); });
@ -2532,6 +2569,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
this.encryptionKey = const Value.absent(), this.encryptionKey = const Value.absent(),
this.encryptionMac = const Value.absent(), this.encryptionMac = const Value.absent(),
this.encryptionNonce = const Value.absent(), this.encryptionNonce = const Value.absent(),
this.storedFileHash = const Value.absent(),
this.createdAt = const Value.absent(), this.createdAt = const Value.absent(),
this.rowid = const Value.absent(), this.rowid = const Value.absent(),
}) : mediaId = Value(mediaId), }) : mediaId = Value(mediaId),
@ -2551,6 +2589,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
Expression<Uint8List>? encryptionKey, Expression<Uint8List>? encryptionKey,
Expression<Uint8List>? encryptionMac, Expression<Uint8List>? encryptionMac,
Expression<Uint8List>? encryptionNonce, Expression<Uint8List>? encryptionNonce,
Expression<Uint8List>? storedFileHash,
Expression<DateTime>? createdAt, Expression<DateTime>? createdAt,
Expression<int>? rowid, Expression<int>? rowid,
}) { }) {
@ -2572,6 +2611,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
if (encryptionKey != null) 'encryption_key': encryptionKey, if (encryptionKey != null) 'encryption_key': encryptionKey,
if (encryptionMac != null) 'encryption_mac': encryptionMac, if (encryptionMac != null) 'encryption_mac': encryptionMac,
if (encryptionNonce != null) 'encryption_nonce': encryptionNonce, if (encryptionNonce != null) 'encryption_nonce': encryptionNonce,
if (storedFileHash != null) 'stored_file_hash': storedFileHash,
if (createdAt != null) 'created_at': createdAt, if (createdAt != null) 'created_at': createdAt,
if (rowid != null) 'rowid': rowid, if (rowid != null) 'rowid': rowid,
}); });
@ -2592,6 +2632,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
Value<Uint8List?>? encryptionKey, Value<Uint8List?>? encryptionKey,
Value<Uint8List?>? encryptionMac, Value<Uint8List?>? encryptionMac,
Value<Uint8List?>? encryptionNonce, Value<Uint8List?>? encryptionNonce,
Value<Uint8List?>? storedFileHash,
Value<DateTime>? createdAt, Value<DateTime>? createdAt,
Value<int>? rowid}) { Value<int>? rowid}) {
return MediaFilesCompanion( return MediaFilesCompanion(
@ -2611,6 +2652,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
encryptionKey: encryptionKey ?? this.encryptionKey, encryptionKey: encryptionKey ?? this.encryptionKey,
encryptionMac: encryptionMac ?? this.encryptionMac, encryptionMac: encryptionMac ?? this.encryptionMac,
encryptionNonce: encryptionNonce ?? this.encryptionNonce, encryptionNonce: encryptionNonce ?? this.encryptionNonce,
storedFileHash: storedFileHash ?? this.storedFileHash,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
rowid: rowid ?? this.rowid, rowid: rowid ?? this.rowid,
); );
@ -2668,6 +2710,9 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
if (encryptionNonce.present) { if (encryptionNonce.present) {
map['encryption_nonce'] = Variable<Uint8List>(encryptionNonce.value); map['encryption_nonce'] = Variable<Uint8List>(encryptionNonce.value);
} }
if (storedFileHash.present) {
map['stored_file_hash'] = Variable<Uint8List>(storedFileHash.value);
}
if (createdAt.present) { if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value); map['created_at'] = Variable<DateTime>(createdAt.value);
} }
@ -2694,6 +2739,7 @@ class MediaFilesCompanion extends UpdateCompanion<MediaFile> {
..write('encryptionKey: $encryptionKey, ') ..write('encryptionKey: $encryptionKey, ')
..write('encryptionMac: $encryptionMac, ') ..write('encryptionMac: $encryptionMac, ')
..write('encryptionNonce: $encryptionNonce, ') ..write('encryptionNonce: $encryptionNonce, ')
..write('storedFileHash: $storedFileHash, ')
..write('createdAt: $createdAt, ') ..write('createdAt: $createdAt, ')
..write('rowid: $rowid') ..write('rowid: $rowid')
..write(')')) ..write(')'))
@ -4546,6 +4592,12 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
defaultConstraints: GeneratedColumn.constraintIsAlways( defaultConstraints: GeneratedColumn.constraintIsAlways(
'CHECK ("contact_will_sends_receipt" IN (0, 1))'), 'CHECK ("contact_will_sends_receipt" IN (0, 1))'),
defaultValue: const Constant(true)); defaultValue: const Constant(true));
static const VerificationMeta _markForRetryMeta =
const VerificationMeta('markForRetry');
@override
late final GeneratedColumn<DateTime> markForRetry = GeneratedColumn<DateTime>(
'mark_for_retry', aliasedName, true,
type: DriftSqlType.dateTime, requiredDuringInsert: false);
static const VerificationMeta _ackByServerAtMeta = static const VerificationMeta _ackByServerAtMeta =
const VerificationMeta('ackByServerAt'); const VerificationMeta('ackByServerAt');
@override @override
@ -4581,6 +4633,7 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
messageId, messageId,
message, message,
contactWillSendsReceipt, contactWillSendsReceipt,
markForRetry,
ackByServerAt, ackByServerAt,
retryCount, retryCount,
lastRetry, lastRetry,
@ -4625,6 +4678,12 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
data['contact_will_sends_receipt']!, data['contact_will_sends_receipt']!,
_contactWillSendsReceiptMeta)); _contactWillSendsReceiptMeta));
} }
if (data.containsKey('mark_for_retry')) {
context.handle(
_markForRetryMeta,
markForRetry.isAcceptableOrUnknown(
data['mark_for_retry']!, _markForRetryMeta));
}
if (data.containsKey('ack_by_server_at')) { if (data.containsKey('ack_by_server_at')) {
context.handle( context.handle(
_ackByServerAtMeta, _ackByServerAtMeta,
@ -4665,6 +4724,8 @@ class $ReceiptsTable extends Receipts with TableInfo<$ReceiptsTable, Receipt> {
contactWillSendsReceipt: attachedDatabase.typeMapping.read( contactWillSendsReceipt: attachedDatabase.typeMapping.read(
DriftSqlType.bool, DriftSqlType.bool,
data['${effectivePrefix}contact_will_sends_receipt'])!, data['${effectivePrefix}contact_will_sends_receipt'])!,
markForRetry: attachedDatabase.typeMapping.read(
DriftSqlType.dateTime, data['${effectivePrefix}mark_for_retry']),
ackByServerAt: attachedDatabase.typeMapping.read( ackByServerAt: attachedDatabase.typeMapping.read(
DriftSqlType.dateTime, data['${effectivePrefix}ack_by_server_at']), DriftSqlType.dateTime, data['${effectivePrefix}ack_by_server_at']),
retryCount: attachedDatabase.typeMapping retryCount: attachedDatabase.typeMapping
@ -4690,6 +4751,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
/// This is the protobuf 'Message' /// This is the protobuf 'Message'
final Uint8List message; final Uint8List message;
final bool contactWillSendsReceipt; final bool contactWillSendsReceipt;
final DateTime? markForRetry;
final DateTime? ackByServerAt; final DateTime? ackByServerAt;
final int retryCount; final int retryCount;
final DateTime? lastRetry; final DateTime? lastRetry;
@ -4700,6 +4762,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
this.messageId, this.messageId,
required this.message, required this.message,
required this.contactWillSendsReceipt, required this.contactWillSendsReceipt,
this.markForRetry,
this.ackByServerAt, this.ackByServerAt,
required this.retryCount, required this.retryCount,
this.lastRetry, this.lastRetry,
@ -4714,6 +4777,9 @@ class Receipt extends DataClass implements Insertable<Receipt> {
} }
map['message'] = Variable<Uint8List>(message); map['message'] = Variable<Uint8List>(message);
map['contact_will_sends_receipt'] = Variable<bool>(contactWillSendsReceipt); map['contact_will_sends_receipt'] = Variable<bool>(contactWillSendsReceipt);
if (!nullToAbsent || markForRetry != null) {
map['mark_for_retry'] = Variable<DateTime>(markForRetry);
}
if (!nullToAbsent || ackByServerAt != null) { if (!nullToAbsent || ackByServerAt != null) {
map['ack_by_server_at'] = Variable<DateTime>(ackByServerAt); map['ack_by_server_at'] = Variable<DateTime>(ackByServerAt);
} }
@ -4734,6 +4800,9 @@ class Receipt extends DataClass implements Insertable<Receipt> {
: Value(messageId), : Value(messageId),
message: Value(message), message: Value(message),
contactWillSendsReceipt: Value(contactWillSendsReceipt), contactWillSendsReceipt: Value(contactWillSendsReceipt),
markForRetry: markForRetry == null && nullToAbsent
? const Value.absent()
: Value(markForRetry),
ackByServerAt: ackByServerAt == null && nullToAbsent ackByServerAt: ackByServerAt == null && nullToAbsent
? const Value.absent() ? const Value.absent()
: Value(ackByServerAt), : Value(ackByServerAt),
@ -4755,6 +4824,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
message: serializer.fromJson<Uint8List>(json['message']), message: serializer.fromJson<Uint8List>(json['message']),
contactWillSendsReceipt: contactWillSendsReceipt:
serializer.fromJson<bool>(json['contactWillSendsReceipt']), serializer.fromJson<bool>(json['contactWillSendsReceipt']),
markForRetry: serializer.fromJson<DateTime?>(json['markForRetry']),
ackByServerAt: serializer.fromJson<DateTime?>(json['ackByServerAt']), ackByServerAt: serializer.fromJson<DateTime?>(json['ackByServerAt']),
retryCount: serializer.fromJson<int>(json['retryCount']), retryCount: serializer.fromJson<int>(json['retryCount']),
lastRetry: serializer.fromJson<DateTime?>(json['lastRetry']), lastRetry: serializer.fromJson<DateTime?>(json['lastRetry']),
@ -4771,6 +4841,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
'message': serializer.toJson<Uint8List>(message), 'message': serializer.toJson<Uint8List>(message),
'contactWillSendsReceipt': 'contactWillSendsReceipt':
serializer.toJson<bool>(contactWillSendsReceipt), serializer.toJson<bool>(contactWillSendsReceipt),
'markForRetry': serializer.toJson<DateTime?>(markForRetry),
'ackByServerAt': serializer.toJson<DateTime?>(ackByServerAt), 'ackByServerAt': serializer.toJson<DateTime?>(ackByServerAt),
'retryCount': serializer.toJson<int>(retryCount), 'retryCount': serializer.toJson<int>(retryCount),
'lastRetry': serializer.toJson<DateTime?>(lastRetry), 'lastRetry': serializer.toJson<DateTime?>(lastRetry),
@ -4784,6 +4855,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
Value<String?> messageId = const Value.absent(), Value<String?> messageId = const Value.absent(),
Uint8List? message, Uint8List? message,
bool? contactWillSendsReceipt, bool? contactWillSendsReceipt,
Value<DateTime?> markForRetry = const Value.absent(),
Value<DateTime?> ackByServerAt = const Value.absent(), Value<DateTime?> ackByServerAt = const Value.absent(),
int? retryCount, int? retryCount,
Value<DateTime?> lastRetry = const Value.absent(), Value<DateTime?> lastRetry = const Value.absent(),
@ -4795,6 +4867,8 @@ class Receipt extends DataClass implements Insertable<Receipt> {
message: message ?? this.message, message: message ?? this.message,
contactWillSendsReceipt: contactWillSendsReceipt:
contactWillSendsReceipt ?? this.contactWillSendsReceipt, contactWillSendsReceipt ?? this.contactWillSendsReceipt,
markForRetry:
markForRetry.present ? markForRetry.value : this.markForRetry,
ackByServerAt: ackByServerAt:
ackByServerAt.present ? ackByServerAt.value : this.ackByServerAt, ackByServerAt.present ? ackByServerAt.value : this.ackByServerAt,
retryCount: retryCount ?? this.retryCount, retryCount: retryCount ?? this.retryCount,
@ -4810,6 +4884,9 @@ class Receipt extends DataClass implements Insertable<Receipt> {
contactWillSendsReceipt: data.contactWillSendsReceipt.present contactWillSendsReceipt: data.contactWillSendsReceipt.present
? data.contactWillSendsReceipt.value ? data.contactWillSendsReceipt.value
: this.contactWillSendsReceipt, : this.contactWillSendsReceipt,
markForRetry: data.markForRetry.present
? data.markForRetry.value
: this.markForRetry,
ackByServerAt: data.ackByServerAt.present ackByServerAt: data.ackByServerAt.present
? data.ackByServerAt.value ? data.ackByServerAt.value
: this.ackByServerAt, : this.ackByServerAt,
@ -4828,6 +4905,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
..write('messageId: $messageId, ') ..write('messageId: $messageId, ')
..write('message: $message, ') ..write('message: $message, ')
..write('contactWillSendsReceipt: $contactWillSendsReceipt, ') ..write('contactWillSendsReceipt: $contactWillSendsReceipt, ')
..write('markForRetry: $markForRetry, ')
..write('ackByServerAt: $ackByServerAt, ') ..write('ackByServerAt: $ackByServerAt, ')
..write('retryCount: $retryCount, ') ..write('retryCount: $retryCount, ')
..write('lastRetry: $lastRetry, ') ..write('lastRetry: $lastRetry, ')
@ -4843,6 +4921,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
messageId, messageId,
$driftBlobEquality.hash(message), $driftBlobEquality.hash(message),
contactWillSendsReceipt, contactWillSendsReceipt,
markForRetry,
ackByServerAt, ackByServerAt,
retryCount, retryCount,
lastRetry, lastRetry,
@ -4856,6 +4935,7 @@ class Receipt extends DataClass implements Insertable<Receipt> {
other.messageId == this.messageId && other.messageId == this.messageId &&
$driftBlobEquality.equals(other.message, this.message) && $driftBlobEquality.equals(other.message, this.message) &&
other.contactWillSendsReceipt == this.contactWillSendsReceipt && other.contactWillSendsReceipt == this.contactWillSendsReceipt &&
other.markForRetry == this.markForRetry &&
other.ackByServerAt == this.ackByServerAt && other.ackByServerAt == this.ackByServerAt &&
other.retryCount == this.retryCount && other.retryCount == this.retryCount &&
other.lastRetry == this.lastRetry && other.lastRetry == this.lastRetry &&
@ -4868,6 +4948,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
final Value<String?> messageId; final Value<String?> messageId;
final Value<Uint8List> message; final Value<Uint8List> message;
final Value<bool> contactWillSendsReceipt; final Value<bool> contactWillSendsReceipt;
final Value<DateTime?> markForRetry;
final Value<DateTime?> ackByServerAt; final Value<DateTime?> ackByServerAt;
final Value<int> retryCount; final Value<int> retryCount;
final Value<DateTime?> lastRetry; final Value<DateTime?> lastRetry;
@ -4879,6 +4960,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
this.messageId = const Value.absent(), this.messageId = const Value.absent(),
this.message = const Value.absent(), this.message = const Value.absent(),
this.contactWillSendsReceipt = const Value.absent(), this.contactWillSendsReceipt = const Value.absent(),
this.markForRetry = const Value.absent(),
this.ackByServerAt = const Value.absent(), this.ackByServerAt = const Value.absent(),
this.retryCount = const Value.absent(), this.retryCount = const Value.absent(),
this.lastRetry = const Value.absent(), this.lastRetry = const Value.absent(),
@ -4891,6 +4973,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
this.messageId = const Value.absent(), this.messageId = const Value.absent(),
required Uint8List message, required Uint8List message,
this.contactWillSendsReceipt = const Value.absent(), this.contactWillSendsReceipt = const Value.absent(),
this.markForRetry = const Value.absent(),
this.ackByServerAt = const Value.absent(), this.ackByServerAt = const Value.absent(),
this.retryCount = const Value.absent(), this.retryCount = const Value.absent(),
this.lastRetry = const Value.absent(), this.lastRetry = const Value.absent(),
@ -4905,6 +4988,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
Expression<String>? messageId, Expression<String>? messageId,
Expression<Uint8List>? message, Expression<Uint8List>? message,
Expression<bool>? contactWillSendsReceipt, Expression<bool>? contactWillSendsReceipt,
Expression<DateTime>? markForRetry,
Expression<DateTime>? ackByServerAt, Expression<DateTime>? ackByServerAt,
Expression<int>? retryCount, Expression<int>? retryCount,
Expression<DateTime>? lastRetry, Expression<DateTime>? lastRetry,
@ -4918,6 +5002,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
if (message != null) 'message': message, if (message != null) 'message': message,
if (contactWillSendsReceipt != null) if (contactWillSendsReceipt != null)
'contact_will_sends_receipt': contactWillSendsReceipt, 'contact_will_sends_receipt': contactWillSendsReceipt,
if (markForRetry != null) 'mark_for_retry': markForRetry,
if (ackByServerAt != null) 'ack_by_server_at': ackByServerAt, if (ackByServerAt != null) 'ack_by_server_at': ackByServerAt,
if (retryCount != null) 'retry_count': retryCount, if (retryCount != null) 'retry_count': retryCount,
if (lastRetry != null) 'last_retry': lastRetry, if (lastRetry != null) 'last_retry': lastRetry,
@ -4932,6 +5017,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
Value<String?>? messageId, Value<String?>? messageId,
Value<Uint8List>? message, Value<Uint8List>? message,
Value<bool>? contactWillSendsReceipt, Value<bool>? contactWillSendsReceipt,
Value<DateTime?>? markForRetry,
Value<DateTime?>? ackByServerAt, Value<DateTime?>? ackByServerAt,
Value<int>? retryCount, Value<int>? retryCount,
Value<DateTime?>? lastRetry, Value<DateTime?>? lastRetry,
@ -4944,6 +5030,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
message: message ?? this.message, message: message ?? this.message,
contactWillSendsReceipt: contactWillSendsReceipt:
contactWillSendsReceipt ?? this.contactWillSendsReceipt, contactWillSendsReceipt ?? this.contactWillSendsReceipt,
markForRetry: markForRetry ?? this.markForRetry,
ackByServerAt: ackByServerAt ?? this.ackByServerAt, ackByServerAt: ackByServerAt ?? this.ackByServerAt,
retryCount: retryCount ?? this.retryCount, retryCount: retryCount ?? this.retryCount,
lastRetry: lastRetry ?? this.lastRetry, lastRetry: lastRetry ?? this.lastRetry,
@ -4971,6 +5058,9 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
map['contact_will_sends_receipt'] = map['contact_will_sends_receipt'] =
Variable<bool>(contactWillSendsReceipt.value); Variable<bool>(contactWillSendsReceipt.value);
} }
if (markForRetry.present) {
map['mark_for_retry'] = Variable<DateTime>(markForRetry.value);
}
if (ackByServerAt.present) { if (ackByServerAt.present) {
map['ack_by_server_at'] = Variable<DateTime>(ackByServerAt.value); map['ack_by_server_at'] = Variable<DateTime>(ackByServerAt.value);
} }
@ -4997,6 +5087,7 @@ class ReceiptsCompanion extends UpdateCompanion<Receipt> {
..write('messageId: $messageId, ') ..write('messageId: $messageId, ')
..write('message: $message, ') ..write('message: $message, ')
..write('contactWillSendsReceipt: $contactWillSendsReceipt, ') ..write('contactWillSendsReceipt: $contactWillSendsReceipt, ')
..write('markForRetry: $markForRetry, ')
..write('ackByServerAt: $ackByServerAt, ') ..write('ackByServerAt: $ackByServerAt, ')
..write('retryCount: $retryCount, ') ..write('retryCount: $retryCount, ')
..write('lastRetry: $lastRetry, ') ..write('lastRetry: $lastRetry, ')
@ -9345,6 +9436,7 @@ typedef $$MediaFilesTableCreateCompanionBuilder = MediaFilesCompanion Function({
Value<Uint8List?> encryptionKey, Value<Uint8List?> encryptionKey,
Value<Uint8List?> encryptionMac, Value<Uint8List?> encryptionMac,
Value<Uint8List?> encryptionNonce, Value<Uint8List?> encryptionNonce,
Value<Uint8List?> storedFileHash,
Value<DateTime> createdAt, Value<DateTime> createdAt,
Value<int> rowid, Value<int> rowid,
}); });
@ -9363,6 +9455,7 @@ typedef $$MediaFilesTableUpdateCompanionBuilder = MediaFilesCompanion Function({
Value<Uint8List?> encryptionKey, Value<Uint8List?> encryptionKey,
Value<Uint8List?> encryptionMac, Value<Uint8List?> encryptionMac,
Value<Uint8List?> encryptionNonce, Value<Uint8List?> encryptionNonce,
Value<Uint8List?> storedFileHash,
Value<DateTime> createdAt, Value<DateTime> createdAt,
Value<int> rowid, Value<int> rowid,
}); });
@ -9449,6 +9542,10 @@ class $$MediaFilesTableFilterComposer
column: $table.encryptionNonce, column: $table.encryptionNonce,
builder: (column) => ColumnFilters(column)); builder: (column) => ColumnFilters(column));
ColumnFilters<Uint8List> get storedFileHash => $composableBuilder(
column: $table.storedFileHash,
builder: (column) => ColumnFilters(column));
ColumnFilters<DateTime> get createdAt => $composableBuilder( ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnFilters(column)); column: $table.createdAt, builder: (column) => ColumnFilters(column));
@ -9534,6 +9631,10 @@ class $$MediaFilesTableOrderingComposer
column: $table.encryptionNonce, column: $table.encryptionNonce,
builder: (column) => ColumnOrderings(column)); builder: (column) => ColumnOrderings(column));
ColumnOrderings<Uint8List> get storedFileHash => $composableBuilder(
column: $table.storedFileHash,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<DateTime> get createdAt => $composableBuilder( ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnOrderings(column)); column: $table.createdAt, builder: (column) => ColumnOrderings(column));
} }
@ -9592,6 +9693,9 @@ class $$MediaFilesTableAnnotationComposer
GeneratedColumn<Uint8List> get encryptionNonce => $composableBuilder( GeneratedColumn<Uint8List> get encryptionNonce => $composableBuilder(
column: $table.encryptionNonce, builder: (column) => column); column: $table.encryptionNonce, builder: (column) => column);
GeneratedColumn<Uint8List> get storedFileHash => $composableBuilder(
column: $table.storedFileHash, builder: (column) => column);
GeneratedColumn<DateTime> get createdAt => GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column); $composableBuilder(column: $table.createdAt, builder: (column) => column);
@ -9654,6 +9758,7 @@ class $$MediaFilesTableTableManager extends RootTableManager<
Value<Uint8List?> encryptionKey = const Value.absent(), Value<Uint8List?> encryptionKey = const Value.absent(),
Value<Uint8List?> encryptionMac = const Value.absent(), Value<Uint8List?> encryptionMac = const Value.absent(),
Value<Uint8List?> encryptionNonce = const Value.absent(), Value<Uint8List?> encryptionNonce = const Value.absent(),
Value<Uint8List?> storedFileHash = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(), Value<DateTime> createdAt = const Value.absent(),
Value<int> rowid = const Value.absent(), Value<int> rowid = const Value.absent(),
}) => }) =>
@ -9672,6 +9777,7 @@ class $$MediaFilesTableTableManager extends RootTableManager<
encryptionKey: encryptionKey, encryptionKey: encryptionKey,
encryptionMac: encryptionMac, encryptionMac: encryptionMac,
encryptionNonce: encryptionNonce, encryptionNonce: encryptionNonce,
storedFileHash: storedFileHash,
createdAt: createdAt, createdAt: createdAt,
rowid: rowid, rowid: rowid,
), ),
@ -9690,6 +9796,7 @@ class $$MediaFilesTableTableManager extends RootTableManager<
Value<Uint8List?> encryptionKey = const Value.absent(), Value<Uint8List?> encryptionKey = const Value.absent(),
Value<Uint8List?> encryptionMac = const Value.absent(), Value<Uint8List?> encryptionMac = const Value.absent(),
Value<Uint8List?> encryptionNonce = const Value.absent(), Value<Uint8List?> encryptionNonce = const Value.absent(),
Value<Uint8List?> storedFileHash = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(), Value<DateTime> createdAt = const Value.absent(),
Value<int> rowid = const Value.absent(), Value<int> rowid = const Value.absent(),
}) => }) =>
@ -9708,6 +9815,7 @@ class $$MediaFilesTableTableManager extends RootTableManager<
encryptionKey: encryptionKey, encryptionKey: encryptionKey,
encryptionMac: encryptionMac, encryptionMac: encryptionMac,
encryptionNonce: encryptionNonce, encryptionNonce: encryptionNonce,
storedFileHash: storedFileHash,
createdAt: createdAt, createdAt: createdAt,
rowid: rowid, rowid: rowid,
), ),
@ -11662,6 +11770,7 @@ typedef $$ReceiptsTableCreateCompanionBuilder = ReceiptsCompanion Function({
Value<String?> messageId, Value<String?> messageId,
required Uint8List message, required Uint8List message,
Value<bool> contactWillSendsReceipt, Value<bool> contactWillSendsReceipt,
Value<DateTime?> markForRetry,
Value<DateTime?> ackByServerAt, Value<DateTime?> ackByServerAt,
Value<int> retryCount, Value<int> retryCount,
Value<DateTime?> lastRetry, Value<DateTime?> lastRetry,
@ -11674,6 +11783,7 @@ typedef $$ReceiptsTableUpdateCompanionBuilder = ReceiptsCompanion Function({
Value<String?> messageId, Value<String?> messageId,
Value<Uint8List> message, Value<Uint8List> message,
Value<bool> contactWillSendsReceipt, Value<bool> contactWillSendsReceipt,
Value<DateTime?> markForRetry,
Value<DateTime?> ackByServerAt, Value<DateTime?> ackByServerAt,
Value<int> retryCount, Value<int> retryCount,
Value<DateTime?> lastRetry, Value<DateTime?> lastRetry,
@ -11735,6 +11845,9 @@ class $$ReceiptsTableFilterComposer
column: $table.contactWillSendsReceipt, column: $table.contactWillSendsReceipt,
builder: (column) => ColumnFilters(column)); builder: (column) => ColumnFilters(column));
ColumnFilters<DateTime> get markForRetry => $composableBuilder(
column: $table.markForRetry, builder: (column) => ColumnFilters(column));
ColumnFilters<DateTime> get ackByServerAt => $composableBuilder( ColumnFilters<DateTime> get ackByServerAt => $composableBuilder(
column: $table.ackByServerAt, builder: (column) => ColumnFilters(column)); column: $table.ackByServerAt, builder: (column) => ColumnFilters(column));
@ -11807,6 +11920,10 @@ class $$ReceiptsTableOrderingComposer
column: $table.contactWillSendsReceipt, column: $table.contactWillSendsReceipt,
builder: (column) => ColumnOrderings(column)); builder: (column) => ColumnOrderings(column));
ColumnOrderings<DateTime> get markForRetry => $composableBuilder(
column: $table.markForRetry,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<DateTime> get ackByServerAt => $composableBuilder( ColumnOrderings<DateTime> get ackByServerAt => $composableBuilder(
column: $table.ackByServerAt, column: $table.ackByServerAt,
builder: (column) => ColumnOrderings(column)); builder: (column) => ColumnOrderings(column));
@ -11879,6 +11996,9 @@ class $$ReceiptsTableAnnotationComposer
GeneratedColumn<bool> get contactWillSendsReceipt => $composableBuilder( GeneratedColumn<bool> get contactWillSendsReceipt => $composableBuilder(
column: $table.contactWillSendsReceipt, builder: (column) => column); column: $table.contactWillSendsReceipt, builder: (column) => column);
GeneratedColumn<DateTime> get markForRetry => $composableBuilder(
column: $table.markForRetry, builder: (column) => column);
GeneratedColumn<DateTime> get ackByServerAt => $composableBuilder( GeneratedColumn<DateTime> get ackByServerAt => $composableBuilder(
column: $table.ackByServerAt, builder: (column) => column); column: $table.ackByServerAt, builder: (column) => column);
@ -11960,6 +12080,7 @@ class $$ReceiptsTableTableManager extends RootTableManager<
Value<String?> messageId = const Value.absent(), Value<String?> messageId = const Value.absent(),
Value<Uint8List> message = const Value.absent(), Value<Uint8List> message = const Value.absent(),
Value<bool> contactWillSendsReceipt = const Value.absent(), Value<bool> contactWillSendsReceipt = const Value.absent(),
Value<DateTime?> markForRetry = const Value.absent(),
Value<DateTime?> ackByServerAt = const Value.absent(), Value<DateTime?> ackByServerAt = const Value.absent(),
Value<int> retryCount = const Value.absent(), Value<int> retryCount = const Value.absent(),
Value<DateTime?> lastRetry = const Value.absent(), Value<DateTime?> lastRetry = const Value.absent(),
@ -11972,6 +12093,7 @@ class $$ReceiptsTableTableManager extends RootTableManager<
messageId: messageId, messageId: messageId,
message: message, message: message,
contactWillSendsReceipt: contactWillSendsReceipt, contactWillSendsReceipt: contactWillSendsReceipt,
markForRetry: markForRetry,
ackByServerAt: ackByServerAt, ackByServerAt: ackByServerAt,
retryCount: retryCount, retryCount: retryCount,
lastRetry: lastRetry, lastRetry: lastRetry,
@ -11984,6 +12106,7 @@ class $$ReceiptsTableTableManager extends RootTableManager<
Value<String?> messageId = const Value.absent(), Value<String?> messageId = const Value.absent(),
required Uint8List message, required Uint8List message,
Value<bool> contactWillSendsReceipt = const Value.absent(), Value<bool> contactWillSendsReceipt = const Value.absent(),
Value<DateTime?> markForRetry = const Value.absent(),
Value<DateTime?> ackByServerAt = const Value.absent(), Value<DateTime?> ackByServerAt = const Value.absent(),
Value<int> retryCount = const Value.absent(), Value<int> retryCount = const Value.absent(),
Value<DateTime?> lastRetry = const Value.absent(), Value<DateTime?> lastRetry = const Value.absent(),
@ -11996,6 +12119,7 @@ class $$ReceiptsTableTableManager extends RootTableManager<
messageId: messageId, messageId: messageId,
message: message, message: message,
contactWillSendsReceipt: contactWillSendsReceipt, contactWillSendsReceipt: contactWillSendsReceipt,
markForRetry: markForRetry,
ackByServerAt: ackByServerAt, ackByServerAt: ackByServerAt,
retryCount: retryCount, retryCount: retryCount,
lastRetry: lastRetry, lastRetry: lastRetry,

View file

@ -1946,10 +1946,458 @@ final class Schema4 extends i0.VersionedSchema {
i1.GeneratedColumn<int> _column_101(String aliasedName) => i1.GeneratedColumn<int> _column_101(String aliasedName) =>
i1.GeneratedColumn<int>('affected_contact_id', aliasedName, true, i1.GeneratedColumn<int>('affected_contact_id', aliasedName, true,
type: i1.DriftSqlType.int); type: i1.DriftSqlType.int);
final class Schema5 extends i0.VersionedSchema {
Schema5({required super.database}) : super(version: 5);
@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 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 Shape19 receipts = Shape19(
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_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 Shape18 extends i0.VersionedTable {
Shape18({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get mediaId =>
columnsByName['media_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get type =>
columnsByName['type']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get uploadState =>
columnsByName['upload_state']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<String> get downloadState =>
columnsByName['download_state']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<bool> get requiresAuthentication =>
columnsByName['requires_authentication']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get stored =>
columnsByName['stored']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<bool> get isDraftMedia =>
columnsByName['is_draft_media']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<String> get reuploadRequestedBy =>
columnsByName['reupload_requested_by']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get displayLimitInMilliseconds =>
columnsByName['display_limit_in_milliseconds']!
as i1.GeneratedColumn<int>;
i1.GeneratedColumn<bool> get removeAudio =>
columnsByName['remove_audio']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<i2.Uint8List> get downloadToken =>
columnsByName['download_token']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<i2.Uint8List> get encryptionKey =>
columnsByName['encryption_key']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<i2.Uint8List> get encryptionMac =>
columnsByName['encryption_mac']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<i2.Uint8List> get encryptionNonce =>
columnsByName['encryption_nonce']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<i2.Uint8List> get storedFileHash =>
columnsByName['stored_file_hash']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<i2.Uint8List> _column_102(String aliasedName) =>
i1.GeneratedColumn<i2.Uint8List>('stored_file_hash', aliasedName, true,
type: i1.DriftSqlType.blob);
class Shape19 extends i0.VersionedTable {
Shape19({required super.source, required super.alias}) : super.aliased();
i1.GeneratedColumn<String> get receiptId =>
columnsByName['receipt_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<int> get contactId =>
columnsByName['contact_id']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<String> get messageId =>
columnsByName['message_id']! as i1.GeneratedColumn<String>;
i1.GeneratedColumn<i2.Uint8List> get message =>
columnsByName['message']! as i1.GeneratedColumn<i2.Uint8List>;
i1.GeneratedColumn<bool> get contactWillSendsReceipt =>
columnsByName['contact_will_sends_receipt']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<DateTime> get markForRetry =>
columnsByName['mark_for_retry']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get ackByServerAt =>
columnsByName['ack_by_server_at']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<int> get retryCount =>
columnsByName['retry_count']! as i1.GeneratedColumn<int>;
i1.GeneratedColumn<DateTime> get lastRetry =>
columnsByName['last_retry']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get createdAt =>
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<DateTime> _column_103(String aliasedName) =>
i1.GeneratedColumn<DateTime>('mark_for_retry', aliasedName, true,
type: i1.DriftSqlType.dateTime);
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,
}) { }) {
return (currentVersion, database) async { return (currentVersion, database) async {
switch (currentVersion) { switch (currentVersion) {
@ -1968,6 +2416,11 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema); final migrator = i1.Migrator(database, schema);
await from3To4(migrator, schema); await from3To4(migrator, schema);
return 4; return 4;
case 4:
final schema = Schema5(database: database);
final migrator = i1.Migrator(database, schema);
await from4To5(migrator, schema);
return 5;
default: default:
throw ArgumentError.value('Unknown migration from $currentVersion'); throw ArgumentError.value('Unknown migration from $currentVersion');
} }
@ -1978,10 +2431,12 @@ 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, 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,
}) => }) =>
i0.VersionedSchema.stepByStepHelper( i0.VersionedSchema.stepByStepHelper(
step: migrationSteps( step: migrationSteps(
from1To2: from1To2, from1To2: from1To2,
from2To3: from2To3, from2To3: from2To3,
from3To4: from3To4, from3To4: from3To4,
from4To5: from4To5,
)); ));

View file

@ -461,5 +461,8 @@
"showImagePreviewWhenSending": "Bildvorschau bei der Auswahl von Empfängern anzeigen", "showImagePreviewWhenSending": "Bildvorschau bei der Auswahl von Empfängern anzeigen",
"verifiedPublicKey": "Der öffentliche Schlüssel von {username} wurde überprüft und ist gültig.", "verifiedPublicKey": "Der öffentliche Schlüssel von {username} wurde überprüft und ist gültig.",
"memoriesAYearAgo": "Vor einem Jahr", "memoriesAYearAgo": "Vor einem Jahr",
"memoriesXYearsAgo": "Vor {years} Jahren" "memoriesXYearsAgo": "Vor {years} Jahren",
"migrationOfMemories": "Migration von Mediendateien: {open} noch offen.",
"autoStoreAllSendUnlimitedMediaFiles": "Alle gesendeten Medien speichern",
"autoStoreAllSendUnlimitedMediaFilesSubtitle": "Wenn du diese Option aktivierst, werden alle Bilder, die du sendest, gespeichert, sofern sie mit einem unendlichen Countdown und nicht im twonly-Modus gesendet wurden."
} }

View file

@ -491,5 +491,8 @@
"showImagePreviewWhenSending": "Display image preview when selecting recipients", "showImagePreviewWhenSending": "Display image preview when selecting recipients",
"verifiedPublicKey": "The public key of {username} has been verified and is valid.", "verifiedPublicKey": "The public key of {username} has been verified and is valid.",
"memoriesAYearAgo": "One year ago", "memoriesAYearAgo": "One year ago",
"memoriesXYearsAgo": "{years} years ago" "memoriesXYearsAgo": "{years} years ago",
"migrationOfMemories": "Migration of media files: {open} still to be processed.",
"autoStoreAllSendUnlimitedMediaFiles": "Save all sent media",
"autoStoreAllSendUnlimitedMediaFilesSubtitle": "If you enable this option, all images you send will be saved as long as they were sent with an infinite countdown and not in twonly mode."
} }

View file

@ -2869,6 +2869,24 @@ abstract class AppLocalizations {
/// In en, this message translates to: /// In en, this message translates to:
/// **'{years} years ago'** /// **'{years} years ago'**
String memoriesXYearsAgo(Object years); String memoriesXYearsAgo(Object years);
/// No description provided for @migrationOfMemories.
///
/// In en, this message translates to:
/// **'Migration of media files: {open} still to be processed.'**
String migrationOfMemories(Object open);
/// No description provided for @autoStoreAllSendUnlimitedMediaFiles.
///
/// In en, this message translates to:
/// **'Save all sent media'**
String get autoStoreAllSendUnlimitedMediaFiles;
/// No description provided for @autoStoreAllSendUnlimitedMediaFilesSubtitle.
///
/// In en, this message translates to:
/// **'If you enable this option, all images you send will be saved as long as they were sent with an infinite countdown and not in twonly mode.'**
String get autoStoreAllSendUnlimitedMediaFilesSubtitle;
} }
class _AppLocalizationsDelegate class _AppLocalizationsDelegate

View file

@ -1590,4 +1590,17 @@ class AppLocalizationsDe extends AppLocalizations {
String memoriesXYearsAgo(Object years) { String memoriesXYearsAgo(Object years) {
return 'Vor $years Jahren'; return 'Vor $years Jahren';
} }
@override
String migrationOfMemories(Object open) {
return 'Migration von Mediendateien: $open noch offen.';
}
@override
String get autoStoreAllSendUnlimitedMediaFiles =>
'Alle gesendeten Medien speichern';
@override
String get autoStoreAllSendUnlimitedMediaFilesSubtitle =>
'Wenn du diese Option aktivierst, werden alle Bilder, die du sendest, gespeichert, sofern sie mit einem unendlichen Countdown und nicht im twonly-Modus gesendet wurden.';
} }

View file

@ -1580,4 +1580,16 @@ class AppLocalizationsEn extends AppLocalizations {
String memoriesXYearsAgo(Object years) { String memoriesXYearsAgo(Object years) {
return '$years years ago'; return '$years years ago';
} }
@override
String migrationOfMemories(Object open) {
return 'Migration of media files: $open still to be processed.';
}
@override
String get autoStoreAllSendUnlimitedMediaFiles => 'Save all sent media';
@override
String get autoStoreAllSendUnlimitedMediaFilesSubtitle =>
'If you enable this option, all images you send will be saved as long as they were sent with an infinite countdown and not in twonly mode.';
} }

View file

@ -59,8 +59,8 @@ class UserData {
@JsonKey(defaultValue: true) @JsonKey(defaultValue: true)
bool showFeedbackShortcut = true; bool showFeedbackShortcut = true;
@JsonKey(defaultValue: true) @JsonKey(defaultValue: false)
bool showShowImagePreviewWhenSending = true; bool showShowImagePreviewWhenSending = false;
@JsonKey(defaultValue: true) @JsonKey(defaultValue: true)
bool startWithCameraOpen = true; bool startWithCameraOpen = true;
@ -72,6 +72,9 @@ class UserData {
@JsonKey(defaultValue: false) @JsonKey(defaultValue: false)
bool storeMediaFilesInGallery = false; bool storeMediaFilesInGallery = false;
@JsonKey(defaultValue: false)
bool autoStoreAllSendUnlimitedMediaFiles = false;
String? lastPlanBallance; String? lastPlanBallance;
String? additionalUserInvites; String? additionalUserInvites;

View file

@ -33,7 +33,7 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
json['requestedAudioPermission'] as bool? ?? false json['requestedAudioPermission'] as bool? ?? false
..showFeedbackShortcut = json['showFeedbackShortcut'] as bool? ?? true ..showFeedbackShortcut = json['showFeedbackShortcut'] as bool? ?? true
..showShowImagePreviewWhenSending = ..showShowImagePreviewWhenSending =
json['showShowImagePreviewWhenSending'] as bool? ?? true json['showShowImagePreviewWhenSending'] as bool? ?? false
..startWithCameraOpen = json['startWithCameraOpen'] as bool? ?? true ..startWithCameraOpen = json['startWithCameraOpen'] as bool? ?? true
..preSelectedEmojies = (json['preSelectedEmojies'] as List<dynamic>?) ..preSelectedEmojies = (json['preSelectedEmojies'] as List<dynamic>?)
?.map((e) => e as String) ?.map((e) => e as String)
@ -45,6 +45,8 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
) )
..storeMediaFilesInGallery = ..storeMediaFilesInGallery =
json['storeMediaFilesInGallery'] as bool? ?? false json['storeMediaFilesInGallery'] as bool? ?? false
..autoStoreAllSendUnlimitedMediaFiles =
json['autoStoreAllSendUnlimitedMediaFiles'] as bool? ?? false
..lastPlanBallance = json['lastPlanBallance'] as String? ..lastPlanBallance = json['lastPlanBallance'] as String?
..additionalUserInvites = json['additionalUserInvites'] as String? ..additionalUserInvites = json['additionalUserInvites'] as String?
..tutorialDisplayed = (json['tutorialDisplayed'] as List<dynamic>?) ..tutorialDisplayed = (json['tutorialDisplayed'] as List<dynamic>?)
@ -102,6 +104,8 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
'preSelectedEmojies': instance.preSelectedEmojies, 'preSelectedEmojies': instance.preSelectedEmojies,
'autoDownloadOptions': instance.autoDownloadOptions, 'autoDownloadOptions': instance.autoDownloadOptions,
'storeMediaFilesInGallery': instance.storeMediaFilesInGallery, 'storeMediaFilesInGallery': instance.storeMediaFilesInGallery,
'autoStoreAllSendUnlimitedMediaFiles':
instance.autoStoreAllSendUnlimitedMediaFiles,
'lastPlanBallance': instance.lastPlanBallance, 'lastPlanBallance': instance.lastPlanBallance,
'additionalUserInvites': instance.additionalUserInvites, 'additionalUserInvites': instance.additionalUserInvites,
'tutorialDisplayed': instance.tutorialDisplayed, 'tutorialDisplayed': instance.tutorialDisplayed,

View file

@ -93,7 +93,7 @@ Future<void> handleContactUpdate(
switch (contactUpdate.type) { switch (contactUpdate.type) {
case EncryptedContent_ContactUpdate_Type.REQUEST: case EncryptedContent_ContactUpdate_Type.REQUEST:
Log.info('Got a contact update request from $fromUserId'); Log.info('Got a contact update request from $fromUserId');
await notifyContactsAboutProfileChange(onlyToContact: fromUserId); await sendContactMyProfileData(fromUserId);
case EncryptedContent_ContactUpdate_Type.UPDATE: case EncryptedContent_ContactUpdate_Type.UPDATE:
Log.info('Got a contact update $fromUserId'); Log.info('Got a contact update $fromUserId');

View file

@ -132,6 +132,15 @@ Future<void> startBackgroundMediaUpload(MediaFileService mediaService) async {
} }
} }
// if the user has enabled auto storing and the file
// was send with unlimited counter not in twonly-Mode then store the file
if (gUser.autoStoreAllSendUnlimitedMediaFiles &&
!mediaService.mediaFile.requiresAuthentication &&
!mediaService.storedPath.existsSync() &&
mediaService.mediaFile.displayLimitInMilliseconds == null) {
await mediaService.storeMediaFile();
}
if (!mediaService.encryptedPath.existsSync()) { if (!mediaService.encryptedPath.existsSync()) {
await _encryptMediaFiles(mediaService); await _encryptMediaFiles(mediaService);
if (!mediaService.encryptedPath.existsSync()) { if (!mediaService.encryptedPath.existsSync()) {

View file

@ -21,7 +21,7 @@ final lockRetransmission = Mutex();
Future<void> tryTransmitMessages() async { Future<void> tryTransmitMessages() async {
return lockRetransmission.protect(() async { return lockRetransmission.protect(() async {
final receipts = await twonlyDB.receiptsDao.getReceiptsNotAckByServer(); final receipts = await twonlyDB.receiptsDao.getReceiptsForRetransmission();
if (receipts.isEmpty) return; if (receipts.isEmpty) return;
@ -289,26 +289,18 @@ Future<void> notifyContactAboutOpeningMessage(
await updateLastMessageId(contactId, biggestMessageId); await updateLastMessageId(contactId, biggestMessageId);
} }
Future<void> notifyContactsAboutProfileChange({int? onlyToContact}) async { Future<void> sendContactMyProfileData(int contactId) async {
if (gUser.avatarSvg == null) return; List<int>? avatarSvgCompressed;
if (gUser.avatarSvg != null) {
avatarSvgCompressed = gzip.encode(utf8.encode(gUser.avatarSvg!));
}
final encryptedContent = pb.EncryptedContent( final encryptedContent = pb.EncryptedContent(
contactUpdate: pb.EncryptedContent_ContactUpdate( contactUpdate: pb.EncryptedContent_ContactUpdate(
type: pb.EncryptedContent_ContactUpdate_Type.UPDATE, type: pb.EncryptedContent_ContactUpdate_Type.UPDATE,
avatarSvgCompressed: gzip.encode(utf8.encode(gUser.avatarSvg!)), avatarSvgCompressed: avatarSvgCompressed,
displayName: gUser.displayName, displayName: gUser.displayName,
username: gUser.username, username: gUser.username,
), ),
); );
await sendCipherText(contactId, encryptedContent);
if (onlyToContact != null) {
await sendCipherText(onlyToContact, encryptedContent);
return;
}
final contacts = await twonlyDB.contactsDao.getAllNotBlockedContacts();
for (final contact in contacts) {
await sendCipherText(contact.userId, encryptedContent);
}
} }

View file

@ -40,7 +40,11 @@ Future<void> handleServerMessage(server.ServerToClient msg) async {
await handleClient2ClientMessage(msg.v0.newMessage); await handleClient2ClientMessage(msg.v0.newMessage);
} else if (msg.v0.hasNewMessages()) { } else if (msg.v0.hasNewMessages()) {
for (final newMessage in msg.v0.newMessages.newMessages) { for (final newMessage in msg.v0.newMessages.newMessages) {
try {
await handleClient2ClientMessage(newMessage); await handleClient2ClientMessage(newMessage);
} catch (e) {
Log.error(e);
}
} }
} else { } else {
Log.error('Unknown server message: $msg'); Log.error('Unknown server message: $msg');
@ -185,6 +189,15 @@ Future<PlaintextContent?> handleEncryptedMessage(
..type = decryptionErrorType!); ..type = decryptionErrorType!);
} }
// We got a valid message fromUserId, so mark all messages which where
// send to the user but not yet ACK for retransmission. All marked messages
// will be either transmitted again after a new server connection (minimum 20 seconds).
// In case the server sends the ACK before they will be deleted.
// This ensures that 1. all messages will be received by the other person and
// that they will be retransmitted in case the server deleted them as they
// where not downloaded within the 40 days
await twonlyDB.receiptsDao.markMessagesForRetry(fromUserId);
final senderProfileCounter = await checkForProfileUpdate(fromUserId, content); final senderProfileCounter = await checkForProfileUpdate(fromUserId, content);
if (content.hasContactRequest()) { if (content.hasContactRequest()) {

View file

@ -8,6 +8,7 @@ import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/mediafiles/compression.service.dart'; import 'package:twonly/src/services/mediafiles/compression.service.dart';
import 'package:twonly/src/services/mediafiles/thumbnail.service.dart'; import 'package:twonly/src/services/mediafiles/thumbnail.service.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
class MediaFileService { class MediaFileService {
MediaFileService(this.mediaFile); MediaFileService(this.mediaFile);
@ -223,6 +224,22 @@ class MediaFileService {
); );
} }
unawaited(createThumbnail()); unawaited(createThumbnail());
await hashStoredMedia();
// updateFromDb is done in hashStoredMedia()
}
Future<void> hashStoredMedia() async {
if (!storedPath.existsSync()) {
Log.error('could not create hash value as media file is not stored.');
return;
}
final checksum = await sha256File(storedPath);
await twonlyDB.mediaFilesDao.updateMedia(
mediaFile.mediaId,
MediaFilesCompanion(
storedFileHash: Value(Uint8List.fromList(checksum)),
),
);
await updateFromDB(); await updateFromDB();
} }

View file

@ -1,6 +1,8 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart';
@ -367,3 +369,12 @@ void printWrapped(String text) {
// ignore: avoid_print // ignore: avoid_print
pattern.allMatches(text).forEach((match) => print(match.group(0))); pattern.allMatches(text).forEach((match) => print(match.group(0)));
} }
Future<List<int>> sha256File(File file) async {
final input = file.openRead();
final sha256Sink = AccumulatorSink<Digest>();
final converter = sha256.startChunkedConversion(sha256Sink);
await input.forEach(converter.add);
converter.close();
return sha256Sink.events.single.bytes;
}

View file

@ -284,7 +284,7 @@ class ContactsListView extends StatelessWidget {
), ),
), ),
); );
await notifyContactsAboutProfileChange(); await sendContactMyProfileData(contact.userId);
}, },
), ),
]; ];

View file

@ -7,6 +7,7 @@ import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/memory_item.model.dart'; import 'package:twonly/src/model/memory_item.model.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/views/components/loader.dart';
import 'package:twonly/src/views/memories/memories_item_thumbnail.dart'; import 'package:twonly/src/views/memories/memories_item_thumbnail.dart';
import 'package:twonly/src/views/memories/memories_photo_slider.view.dart'; import 'package:twonly/src/views/memories/memories_photo_slider.view.dart';
@ -18,6 +19,7 @@ class MemoriesView extends StatefulWidget {
} }
class MemoriesViewState extends State<MemoriesView> { class MemoriesViewState extends State<MemoriesView> {
int _filesToMigrate = 0;
List<MemoryItem> galleryItems = []; List<MemoryItem> galleryItems = [];
Map<String, List<int>> orderedByMonth = {}; Map<String, List<int>> orderedByMonth = {};
List<String> months = []; List<String> months = [];
@ -38,6 +40,21 @@ class MemoriesViewState extends State<MemoriesView> {
} }
Future<void> initAsync() async { Future<void> initAsync() async {
final nonHashedFiles =
await twonlyDB.mediaFilesDao.getAllNonHashedStoredMediaFiles();
if (nonHashedFiles.isNotEmpty) {
setState(() {
_filesToMigrate = nonHashedFiles.length;
});
for (final mediaFile in nonHashedFiles) {
final mediaService = MediaFileService(mediaFile);
await mediaService.hashStoredMedia();
setState(() {
_filesToMigrate -= 1;
});
}
_filesToMigrate = 0;
}
await messageSub?.cancel(); await messageSub?.cancel();
final msgStream = twonlyDB.mediaFilesDao.watchAllStoredMediaFiles(); final msgStream = twonlyDB.mediaFilesDao.watchAllStoredMediaFiles();
@ -96,19 +113,33 @@ class MemoriesViewState extends State<MemoriesView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( Widget child = Center(
appBar: AppBar(title: const Text('Memories')),
body: Scrollbar(
child: (galleryItems.isEmpty)
? Center(
child: Text( child: Text(
context.lang.memoriesEmpty, context.lang.memoriesEmpty,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
) );
: ListView.builder( if (_filesToMigrate > 0) {
itemCount: (months.length * 2) + child = Center(
(_galleryItemsLastYears.isEmpty ? 0 : 1), child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ThreeRotatingDots(
size: 40,
color: context.color.primary,
),
const SizedBox(height: 10),
Text(
context.lang.migrationOfMemories(_filesToMigrate),
textAlign: TextAlign.center,
),
],
),
);
} else if (galleryItems.isNotEmpty) {
child = ListView.builder(
itemCount:
(months.length * 2) + (_galleryItemsLastYears.isEmpty ? 0 : 1),
itemBuilder: (context, mIndex) { itemBuilder: (context, mIndex) {
if (_galleryItemsLastYears.isNotEmpty && mIndex == 0) { if (_galleryItemsLastYears.isNotEmpty && mIndex == 0) {
return SizedBox( return SizedBox(
@ -145,8 +176,7 @@ class MemoriesViewState extends State<MemoriesView> {
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
child: Image.file( child: Image.file(
item.value.first.mediaService item.value.first.mediaService.storedPath,
.storedPath,
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
), ),
@ -164,8 +194,7 @@ class MemoriesViewState extends State<MemoriesView> {
fontSize: 20, fontSize: 20,
shadows: [ shadows: [
Shadow( Shadow(
color: color: Color.fromARGB(122, 0, 0, 0),
Color.fromARGB(122, 0, 0, 0),
blurRadius: 5, blurRadius: 5,
), ),
], ],
@ -194,8 +223,7 @@ class MemoriesViewState extends State<MemoriesView> {
return GridView.builder( return GridView.builder(
shrinkWrap: true, shrinkWrap: true,
physics: const ClampingScrollPhysics(), physics: const ClampingScrollPhysics(),
gridDelegate: gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4, crossAxisCount: 4,
childAspectRatio: 9 / 16, childAspectRatio: 9 / 16,
), ),
@ -211,7 +239,13 @@ class MemoriesViewState extends State<MemoriesView> {
}, },
); );
}, },
), );
}
return Scaffold(
appBar: AppBar(title: const Text('Memories')),
body: Scrollbar(
child: child,
), ),
); );
} }

View file

@ -46,6 +46,15 @@ class _DataAndStorageViewState extends State<DataAndStorageView> {
setState(() {}); setState(() {});
} }
Future<void> toggleAutoStoreMediaFiles() async {
await updateUserdata((u) {
u.autoStoreAllSendUnlimitedMediaFiles =
!u.autoStoreAllSendUnlimitedMediaFiles;
return u;
});
setState(() {});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final autoDownloadOptions = final autoDownloadOptions =
@ -65,6 +74,18 @@ class _DataAndStorageViewState extends State<DataAndStorageView> {
onChanged: (a) => toggleStoreInGallery(), onChanged: (a) => toggleStoreInGallery(),
), ),
), ),
ListTile(
title: Text(context.lang.autoStoreAllSendUnlimitedMediaFiles),
subtitle: Text(
context.lang.autoStoreAllSendUnlimitedMediaFilesSubtitle,
style: const TextStyle(fontSize: 9),
),
onTap: toggleAutoStoreMediaFiles,
trailing: Switch(
value: gUser.autoStoreAllSendUnlimitedMediaFiles,
onChanged: (a) => toggleAutoStoreMediaFiles(),
),
),
if (Platform.isAndroid) if (Platform.isAndroid)
ListTile( ListTile(
title: Text( title: Text(

View file

@ -3,7 +3,6 @@ import 'package:avatar_maker/avatar_maker.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/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/services/api/messages.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/utils/storage.dart';
@ -31,7 +30,6 @@ class _ModifyAvatarState extends State<ModifyAvatar> {
..avatarCounter = user.avatarCounter + 1; ..avatarCounter = user.avatarCounter + 1;
return user; return user;
}); });
await notifyContactsAboutProfileChange();
} }
AvatarMakerThemeData getAvatarMakerTheme(BuildContext context) { AvatarMakerThemeData getAvatarMakerTheme(BuildContext context) {

View file

@ -5,7 +5,6 @@ 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:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart'; import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
import 'package:twonly/src/services/api/messages.dart';
import 'package:twonly/src/services/twonly_safe/common.twonly_safe.dart'; import 'package:twonly/src/services/twonly_safe/common.twonly_safe.dart';
import 'package:twonly/src/services/twonly_safe/create_backup.twonly_safe.dart'; import 'package:twonly/src/services/twonly_safe/create_backup.twonly_safe.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
@ -52,7 +51,6 @@ class _ProfileViewState extends State<ProfileView> {
..avatarCounter = user.avatarCounter + 1; ..avatarCounter = user.avatarCounter + 1;
return user; return user;
}); });
await notifyContactsAboutProfileChange();
setState(() {}); // gUser has updated setState(() {}); // gUser has updated
} }
@ -86,7 +84,6 @@ class _ProfileViewState extends State<ProfileView> {
..avatarCounter = user.avatarCounter + 1; ..avatarCounter = user.avatarCounter + 1;
return user; return user;
}); });
await notifyContactsAboutProfileChange();
setState(() {}); // gUser has updated setState(() {}); // gUser has updated
} }

View file

@ -3,7 +3,7 @@ description: "twonly, a privacy-friendly way to connect with friends through sec
publish_to: 'none' publish_to: 'none'
version: 0.0.81+81 version: 0.0.82+82
environment: environment:
sdk: ^3.6.0 sdk: ^3.6.0

View file

@ -7,6 +7,7 @@ import 'schema_v1.dart' as v1;
import 'schema_v2.dart' as v2; import 'schema_v2.dart' as v2;
import 'schema_v3.dart' as v3; import 'schema_v3.dart' as v3;
import 'schema_v4.dart' as v4; import 'schema_v4.dart' as v4;
import 'schema_v5.dart' as v5;
class GeneratedHelper implements SchemaInstantiationHelper { class GeneratedHelper implements SchemaInstantiationHelper {
@override @override
@ -20,10 +21,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v3.DatabaseAtV3(db); return v3.DatabaseAtV3(db);
case 4: case 4:
return v4.DatabaseAtV4(db); return v4.DatabaseAtV4(db);
case 5:
return v5.DatabaseAtV5(db);
default: default:
throw MissingSchemaException(version, versions); throw MissingSchemaException(version, versions);
} }
} }
static const versions = const [1, 2, 3, 4]; static const versions = const [1, 2, 3, 4, 5];
} }

File diff suppressed because it is too large Load diff