Fix: Issue with media files required to be reuploaded

This commit is contained in:
otsmr 2026-03-14 16:25:43 +01:00
parent 48b0715b18
commit 4b1e6a9a44
36 changed files with 48309 additions and 26402 deletions

View file

@ -8,6 +8,7 @@
- Improve: Video compression with progress updates - Improve: Video compression with progress updates
- Improve: Show message "Flames restored" - Improve: Show message "Flames restored"
- Improve: Show toast message if user was added via QR - Improve: Show toast message if user was added via QR
- Fix: Issue with media files required to be reuploaded
- Fix: Problem during contact requests - Fix: Problem during contact requests
- Fix: Problem with deleting a contact - Fix: Problem with deleting a contact
- Fix: Problem with restoring from backup - Fix: Problem with restoring from backup

View file

@ -131,6 +131,10 @@ PODS:
- google_mlkit_commons (0.11.1): - google_mlkit_commons (0.11.1):
- Flutter - Flutter
- MLKitVision (~> 10.0.0) - MLKitVision (~> 10.0.0)
- google_mlkit_face_detection (0.13.2):
- Flutter
- google_mlkit_commons
- GoogleMLKit/FaceDetection (~> 9.0.0)
- GoogleAdsOnDeviceConversion (3.2.0): - GoogleAdsOnDeviceConversion (3.2.0):
- GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Logger (~> 8.1) - GoogleUtilities/Logger (~> 8.1)
@ -164,6 +168,9 @@ PODS:
- GoogleMLKit/BarcodeScanning (9.0.0): - GoogleMLKit/BarcodeScanning (9.0.0):
- GoogleMLKit/MLKitCore - GoogleMLKit/MLKitCore
- MLKitBarcodeScanning (~> 8.0.0) - MLKitBarcodeScanning (~> 8.0.0)
- GoogleMLKit/FaceDetection (9.0.0):
- GoogleMLKit/MLKitCore
- MLKitFaceDetection (~> 8.0.0)
- GoogleMLKit/MLKitCore (9.0.0): - GoogleMLKit/MLKitCore (9.0.0):
- MLKitCommon (~> 14.0.0) - MLKitCommon (~> 14.0.0)
- GoogleToolboxForMac/Defines (4.2.1) - GoogleToolboxForMac/Defines (4.2.1)
@ -246,6 +253,9 @@ PODS:
- GoogleUtilities/Logger (~> 8.0) - GoogleUtilities/Logger (~> 8.0)
- GoogleUtilities/UserDefaults (~> 8.0) - GoogleUtilities/UserDefaults (~> 8.0)
- GTMSessionFetcher/Core (< 4.0, >= 3.3.2) - GTMSessionFetcher/Core (< 4.0, >= 3.3.2)
- MLKitFaceDetection (8.0.0):
- MLKitCommon (~> 14.0)
- MLKitVision (~> 10.0)
- MLKitVision (10.0.0): - MLKitVision (10.0.0):
- GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1)
- "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)"
@ -348,6 +358,7 @@ DEPENDENCIES:
- gal (from `.symlinks/plugins/gal/darwin`) - gal (from `.symlinks/plugins/gal/darwin`)
- google_mlkit_barcode_scanning (from `.symlinks/plugins/google_mlkit_barcode_scanning/ios`) - google_mlkit_barcode_scanning (from `.symlinks/plugins/google_mlkit_barcode_scanning/ios`)
- google_mlkit_commons (from `.symlinks/plugins/google_mlkit_commons/ios`) - google_mlkit_commons (from `.symlinks/plugins/google_mlkit_commons/ios`)
- google_mlkit_face_detection (from `.symlinks/plugins/google_mlkit_face_detection/ios`)
- GoogleUtilities - GoogleUtilities
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`) - in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`)
@ -389,6 +400,7 @@ SPEC REPOS:
- MLImage - MLImage
- MLKitBarcodeScanning - MLKitBarcodeScanning
- MLKitCommon - MLKitCommon
- MLKitFaceDetection
- MLKitVision - MLKitVision
- nanopb - nanopb
- PromisesObjC - PromisesObjC
@ -442,6 +454,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/google_mlkit_barcode_scanning/ios" :path: ".symlinks/plugins/google_mlkit_barcode_scanning/ios"
google_mlkit_commons: google_mlkit_commons:
:path: ".symlinks/plugins/google_mlkit_commons/ios" :path: ".symlinks/plugins/google_mlkit_commons/ios"
google_mlkit_face_detection:
:path: ".symlinks/plugins/google_mlkit_face_detection/ios"
image_picker_ios: image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios" :path: ".symlinks/plugins/image_picker_ios/ios"
in_app_purchase_storekit: in_app_purchase_storekit:
@ -505,6 +519,7 @@ SPEC CHECKSUMS:
gal: baecd024ebfd13c441269ca7404792a7152fde89 gal: baecd024ebfd13c441269ca7404792a7152fde89
google_mlkit_barcode_scanning: 12d8422d8f7b00726dedf9cac00188a2b98750c2 google_mlkit_barcode_scanning: 12d8422d8f7b00726dedf9cac00188a2b98750c2
google_mlkit_commons: a5e4ffae5bc59ea4c7b9025dc72cb6cb79dc1166 google_mlkit_commons: a5e4ffae5bc59ea4c7b9025dc72cb6cb79dc1166
google_mlkit_face_detection: ee4b72cfae062b4c972204be955d83055a4bfd36
GoogleAdsOnDeviceConversion: d68c69dd9581a0f5da02617b6f377e5be483970f GoogleAdsOnDeviceConversion: d68c69dd9581a0f5da02617b6f377e5be483970f
GoogleAppMeasurement: fce7c1c90640d2f9f5c56771f71deacb2ba3f98c GoogleAppMeasurement: fce7c1c90640d2f9f5c56771f71deacb2ba3f98c
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
@ -520,6 +535,7 @@ SPEC CHECKSUMS:
MLImage: 0de5c6c2bf9e93b80ef752e2797f0836f03b58c0 MLImage: 0de5c6c2bf9e93b80ef752e2797f0836f03b58c0
MLKitBarcodeScanning: 39de223e7b1b8a8fbf10816a536dd292d8a39343 MLKitBarcodeScanning: 39de223e7b1b8a8fbf10816a536dd292d8a39343
MLKitCommon: 47d47b50a031d00db62f1b0efe5a1d8b09a3b2e6 MLKitCommon: 47d47b50a031d00db62f1b0efe5a1d8b09a3b2e6
MLKitFaceDetection: 32549f1e70e6e7731261bf9cea2b74095e2531cb
MLKitVision: 39a5a812db83c4a0794445088e567f3631c11961 MLKitVision: 39a5a812db83c4a0794445088e567f3631c11961
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
no_screenshot: 03c8ac6586f9652cd45e3d12d74e5992256403ac no_screenshot: 03c8ac6586f9652cd45e3d12d74e5992256403ac

View file

@ -23,9 +23,10 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
GroupsDao(super.db); GroupsDao(super.db);
Future<bool> isContactInGroup(int contactId, String groupId) async { Future<bool> isContactInGroup(int contactId, String groupId) async {
final entry = await (select(groupMembers)..where( final entry =
// ignore: require_trailing_commas await (select(groupMembers)..where(
(t) => t.contactId.equals(contactId) & t.groupId.equals(groupId))) (t) => t.contactId.equals(contactId) & t.groupId.equals(groupId),
))
.getSingleOrNull(); .getSingleOrNull();
return entry != null; return entry != null;
} }
@ -38,13 +39,13 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
String groupId, String groupId,
GroupsCompanion updates, GroupsCompanion updates,
) async { ) async {
await (update(groups)..where((c) => c.groupId.equals(groupId))) await (update(
.write(updates); groups,
)..where((c) => c.groupId.equals(groupId))).write(updates);
} }
Future<List<GroupMember>> getGroupNonLeftMembers(String groupId) async { Future<List<GroupMember>> getGroupNonLeftMembers(String groupId) async {
return (select(groupMembers) return (select(groupMembers)..where(
..where(
(t) => (t) =>
t.groupId.equals(groupId) & t.groupId.equals(groupId) &
(t.memberState.equals(MemberState.leftGroup.name).not() | (t.memberState.equals(MemberState.leftGroup.name).not() |
@ -54,14 +55,15 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
} }
Future<List<GroupMember>> getAllGroupMembers(String groupId) async { Future<List<GroupMember>> getAllGroupMembers(String groupId) async {
return (select(groupMembers)..where((t) => t.groupId.equals(groupId))) return (select(
.get(); groupMembers,
)..where((t) => t.groupId.equals(groupId))).get();
} }
Future<GroupMember?> getGroupMemberByPublicKey(Uint8List publicKey) async { Future<GroupMember?> getGroupMemberByPublicKey(Uint8List publicKey) async {
return (select(groupMembers) return (select(
..where((t) => t.groupPublicKey.equals(publicKey))) groupMembers,
.getSingleOrNull(); )..where((t) => t.groupPublicKey.equals(publicKey))).getSingleOrNull();
} }
Future<Group?> createNewGroup(GroupsCompanion group) async { Future<Group?> createNewGroup(GroupsCompanion group) async {
@ -94,16 +96,14 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
int contactId, int contactId,
GroupMembersCompanion updates, GroupMembersCompanion updates,
) async { ) async {
await (update(groupMembers) await (update(groupMembers)..where(
..where(
(c) => c.groupId.equals(groupId) & c.contactId.equals(contactId), (c) => c.groupId.equals(groupId) & c.contactId.equals(contactId),
)) ))
.write(updates); .write(updates);
} }
Future<void> removeMember(String groupId, int contactId) async { Future<void> removeMember(String groupId, int contactId) async {
await (delete(groupMembers) await (delete(groupMembers)..where(
..where(
(c) => c.groupId.equals(groupId) & c.contactId.equals(contactId), (c) => c.groupId.equals(groupId) & c.contactId.equals(contactId),
)) ))
.go(); .go();
@ -138,9 +138,9 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
Future<Group?> _insertGroup(GroupsCompanion group) async { Future<Group?> _insertGroup(GroupsCompanion group) async {
try { try {
await into(groups).insert(group); await into(groups).insert(group);
return await (select(groups) return await (select(
..where((t) => t.groupId.equals(group.groupId.value))) groups,
.getSingle(); )..where((t) => t.groupId.equals(group.groupId.value))).getSingle();
} catch (e) { } catch (e) {
Log.error('Could not insert group: $e'); Log.error('Could not insert group: $e');
return null; return null;
@ -148,7 +148,8 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
} }
Future<List<Contact>> getGroupContact(String groupId) async { Future<List<Contact>> getGroupContact(String groupId) async {
final query = (select(contacts).join([ final query =
(select(contacts).join([
leftOuterJoin( leftOuterJoin(
groupMembers, groupMembers,
groupMembers.contactId.equalsExp(contacts.userId), groupMembers.contactId.equalsExp(contacts.userId),
@ -161,7 +162,8 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
} }
Stream<List<Contact>> watchGroupContact(String groupId) { Stream<List<Contact>> watchGroupContact(String groupId) {
final query = (select(contacts).join([ final query =
(select(contacts).join([
leftOuterJoin( leftOuterJoin(
groupMembers, groupMembers,
groupMembers.contactId.equalsExp(contacts.userId), groupMembers.contactId.equalsExp(contacts.userId),
@ -187,30 +189,30 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
} }
Stream<List<Group>> watchGroupsForShareImage() { Stream<List<Group>> watchGroupsForShareImage() {
return (select(groups) return (select(groups)..where(
..where(
(g) => g.leftGroup.equals(false) & g.deletedContent.equals(false), (g) => g.leftGroup.equals(false) & g.deletedContent.equals(false),
)) ))
.watch(); .watch();
} }
Stream<List<GroupMember>> watchContactGroupMember(int contactId) { Stream<List<GroupMember>> watchContactGroupMember(int contactId) {
return (select(groupMembers) return (select(groupMembers)..where(
..where(
(g) => g.contactId.equals(contactId), (g) => g.contactId.equals(contactId),
)) ))
.watch(); .watch();
} }
Stream<Group?> watchGroup(String groupId) { Stream<Group?> watchGroup(String groupId) {
return (select(groups)..where((t) => t.groupId.equals(groupId))) return (select(
.watchSingleOrNull(); groups,
)..where((t) => t.groupId.equals(groupId))).watchSingleOrNull();
} }
Stream<Group?> watchDirectChat(int contactId) { Stream<Group?> watchDirectChat(int contactId) {
final groupId = getUUIDforDirectChat(contactId, gUser.userId); final groupId = getUUIDforDirectChat(contactId, gUser.userId);
return (select(groups)..where((t) => t.groupId.equals(groupId))) return (select(
.watchSingleOrNull(); groups,
)..where((t) => t.groupId.equals(groupId))).watchSingleOrNull();
} }
Stream<List<Group>> watchGroupsForChatList() { Stream<List<Group>> watchGroupsForChatList() {
@ -228,13 +230,13 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
} }
Future<Group?> getGroup(String groupId) { Future<Group?> getGroup(String groupId) {
return (select(groups)..where((t) => t.groupId.equals(groupId))) return (select(
.getSingleOrNull(); groups,
)..where((t) => t.groupId.equals(groupId))).getSingleOrNull();
} }
Stream<int> watchFlameCounter(String groupId) { Stream<int> watchFlameCounter(String groupId) {
return (select(groups) return (select(groups)..where(
..where(
(u) => (u) =>
u.groupId.equals(groupId) & u.groupId.equals(groupId) &
u.lastMessageReceived.isNotNull() & u.lastMessageReceived.isNotNull() &
@ -253,8 +255,7 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
} }
Future<List<Group>> getAllNotJoinedGroups() { Future<List<Group>> getAllNotJoinedGroups() {
return (select(groups) return (select(groups)..where(
..where(
(t) => t.joinedGroup.equals(false) & t.isDirectChat.equals(false), (t) => t.joinedGroup.equals(false) & t.isDirectChat.equals(false),
)) ))
.get(); .get();
@ -262,15 +263,15 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
Future<List<GroupMember>> getAllGroupMemberWithoutPublicKey() async { Future<List<GroupMember>> getAllGroupMemberWithoutPublicKey() async {
try { try {
final query = ((select(groupMembers) final query =
..where((t) => t.groupPublicKey.isNull())) ((select(groupMembers)..where((t) => t.groupPublicKey.isNull())).join(
.join([ [
leftOuterJoin( leftOuterJoin(
groups, groups,
groups.groupId.equalsExp(groupMembers.groupId), groups.groupId.equalsExp(groupMembers.groupId),
), ),
]) ],
..where(groups.isDirectChat.isNull())); )..where(groups.isDirectChat.isNull()));
return query.map((row) => row.readTable(groupMembers)).get(); return query.map((row) => row.readTable(groupMembers)).get();
} catch (e) { } catch (e) {
Log.error(e); Log.error(e);
@ -285,8 +286,7 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
groupMembers, groupMembers,
groupMembers.groupId.equalsExp(groups.groupId), groupMembers.groupId.equalsExp(groups.groupId),
), ),
]) ])..where(groupMembers.contactId.equals(userId)));
..where(groupMembers.contactId.equals(userId)));
return query.map((row) => row.readTable(groups)).getSingleOrNull(); return query.map((row) => row.readTable(groups)).getSingleOrNull();
} }
@ -304,8 +304,7 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
String groupId, String groupId,
DateTime newLastMessage, DateTime newLastMessage,
) async { ) async {
await (update(groups) await (update(groups)..where(
..where(
(t) => (t) =>
t.groupId.equals(groupId) & t.groupId.equals(groupId) &
(t.lastMessageExchange.isSmallerThanValue(newLastMessage)), (t.lastMessageExchange.isSmallerThanValue(newLastMessage)),

View file

@ -22,5 +22,7 @@ class GroupsDaoManager {
$$GroupMembersTableTableManager(_db.attachedDatabase, _db.groupMembers); $$GroupMembersTableTableManager(_db.attachedDatabase, _db.groupMembers);
$$GroupHistoriesTableTableManager get groupHistories => $$GroupHistoriesTableTableManager get groupHistories =>
$$GroupHistoriesTableTableManager( $$GroupHistoriesTableTableManager(
_db.attachedDatabase, _db.groupHistories); _db.attachedDatabase,
_db.groupHistories,
);
} }

View file

@ -51,6 +51,12 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
mediaFiles.downloadState mediaFiles.downloadState
.equals(DownloadState.reuploadRequested.name) .equals(DownloadState.reuploadRequested.name)
.not() & .not() &
mediaFiles.uploadState
.equals(UploadState.fileLimitReached.name)
.not() &
mediaFiles.uploadState
.equals(UploadState.uploadLimitReached.name)
.not() &
mediaFiles.type.equals(MediaType.audio.name).not() & mediaFiles.type.equals(MediaType.audio.name).not() &
messages.openedAt.isNull() & messages.openedAt.isNull() &
messages.groupId.equals(groupId) & messages.groupId.equals(groupId) &

View file

@ -31,10 +31,14 @@ class MessagesDaoManager {
$$ReactionsTableTableManager(_db.attachedDatabase, _db.reactions); $$ReactionsTableTableManager(_db.attachedDatabase, _db.reactions);
$$MessageHistoriesTableTableManager get messageHistories => $$MessageHistoriesTableTableManager get messageHistories =>
$$MessageHistoriesTableTableManager( $$MessageHistoriesTableTableManager(
_db.attachedDatabase, _db.messageHistories); _db.attachedDatabase,
_db.messageHistories,
);
$$GroupMembersTableTableManager get groupMembers => $$GroupMembersTableTableManager get groupMembers =>
$$GroupMembersTableTableManager(_db.attachedDatabase, _db.groupMembers); $$GroupMembersTableTableManager(_db.attachedDatabase, _db.groupMembers);
$$MessageActionsTableTableManager get messageActions => $$MessageActionsTableTableManager get messageActions =>
$$MessageActionsTableTableManager( $$MessageActionsTableTableManager(
_db.attachedDatabase, _db.messageActions); _db.attachedDatabase,
_db.messageActions,
);
} }

View file

@ -17,10 +17,11 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
ReceiptsDao(super.db); ReceiptsDao(super.db);
Future<void> confirmReceipt(String receiptId, int fromUserId) async { Future<void> confirmReceipt(String receiptId, int fromUserId) async {
final receipt = await (select(receipts) final receipt =
..where( await (select(receipts)..where(
(t) => (t) =>
t.receiptId.equals(receiptId) & t.contactId.equals(fromUserId), t.receiptId.equals(receiptId) &
t.contactId.equals(fromUserId),
)) ))
.getSingleOrNull(); .getSingleOrNull();
@ -37,25 +38,21 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
await handleMediaRelatedResponseFromReceiver(receipt.messageId!); await handleMediaRelatedResponseFromReceiver(receipt.messageId!);
} }
await (delete(receipts) await (delete(receipts)..where(
..where( (t) => t.receiptId.equals(receiptId) & t.contactId.equals(fromUserId),
(t) =>
t.receiptId.equals(receiptId) & t.contactId.equals(fromUserId),
)) ))
.go(); .go();
} }
Future<void> deleteReceipt(String receiptId) async { Future<void> deleteReceipt(String receiptId) async {
await (delete(receipts) await (delete(receipts)..where(
..where(
(t) => t.receiptId.equals(receiptId), (t) => t.receiptId.equals(receiptId),
)) ))
.go(); .go();
} }
Future<void> purgeReceivedReceipts() async { Future<void> purgeReceivedReceipts() async {
await (delete(receivedReceipts) await (delete(receivedReceipts)..where(
..where(
(t) => (t.createdAt.isSmallerThanValue( (t) => (t.createdAt.isSmallerThanValue(
clock.now().subtract( clock.now().subtract(
const Duration(days: 25), const Duration(days: 25),
@ -74,8 +71,9 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
); );
} }
final id = await into(receipts).insert(insertEntry); final id = await into(receipts).insert(insertEntry);
return await (select(receipts)..where((t) => t.rowId.equals(id))) return await (select(
.getSingle(); receipts,
)..where((t) => t.rowId.equals(id))).getSingle();
} catch (e) { } catch (e) {
// ignore error, receipts is already in the database... // ignore error, receipts is already in the database...
return null; return null;
@ -84,8 +82,7 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
Future<Receipt?> getReceiptById(String receiptId) async { Future<Receipt?> getReceiptById(String receiptId) async {
try { try {
return await (select(receipts) return await (select(receipts)..where(
..where(
(t) => t.receiptId.equals(receiptId), (t) => t.receiptId.equals(receiptId),
)) ))
.getSingleOrNull(); .getSingleOrNull();
@ -102,13 +99,14 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
seconds: 20, seconds: 20,
), ),
); );
return (select(receipts) return (select(receipts)..where(
..where(
(t) => (t) =>
t.ackByServerAt.isNull() | (t.ackByServerAt.isNull() |
t.markForRetry.isSmallerThanValue(markedRetriesTime) | t.markForRetry.isSmallerThanValue(markedRetriesTime) |
t.markForRetryAfterAccepted t.markForRetryAfterAccepted.isSmallerThanValue(
.isSmallerThanValue(markedRetriesTime), markedRetriesTime,
)) &
t.willBeRetriedByMediaUpload.equals(false),
)) ))
.get(); .get();
} }
@ -121,8 +119,9 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
String receiptId, String receiptId,
ReceiptsCompanion updates, ReceiptsCompanion updates,
) async { ) async {
await (update(receipts)..where((c) => c.receiptId.equals(receiptId))) await (update(
.write(updates); receipts,
)..where((c) => c.receiptId.equals(receiptId))).write(updates);
} }
Future<void> updateReceiptWidthUserId( Future<void> updateReceiptWidthUserId(
@ -130,16 +129,19 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
String receiptId, String receiptId,
ReceiptsCompanion updates, ReceiptsCompanion updates,
) async { ) async {
await (update(receipts) await (update(receipts)..where(
..where( (c) => c.receiptId.equals(receiptId) & c.contactId.equals(fromUserId),
(c) =>
c.receiptId.equals(receiptId) & c.contactId.equals(fromUserId),
)) ))
.write(updates); .write(updates);
} }
Future<void> markMessagesForRetry(int contactId) async { Future<void> markMessagesForRetry(int contactId) async {
await (update(receipts)..where((c) => c.contactId.equals(contactId))).write( await (update(receipts)..where(
(c) =>
c.contactId.equals(contactId) &
c.willBeRetriedByMediaUpload.equals(false),
))
.write(
ReceiptsCompanion( ReceiptsCompanion(
markForRetry: Value(clock.now()), markForRetry: Value(clock.now()),
), ),
@ -147,14 +149,15 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
} }
Future<bool> isDuplicated(String receiptId) async { Future<bool> isDuplicated(String receiptId) async {
return await (select(receivedReceipts) return await (select(
..where((t) => t.receiptId.equals(receiptId))) receivedReceipts,
.getSingleOrNull() != )..where((t) => t.receiptId.equals(receiptId))).getSingleOrNull() !=
null; null;
} }
Future<void> gotReceipt(String receiptId) async { Future<void> gotReceipt(String receiptId) async {
await into(receivedReceipts) await into(
.insert(ReceivedReceiptsCompanion(receiptId: Value(receiptId))); receivedReceipts,
).insert(ReceivedReceiptsCompanion(receiptId: Value(receiptId)));
} }
} }

View file

@ -30,8 +30,12 @@ class ReceiptsDaoManager {
$$ReceiptsTableTableManager(_db.attachedDatabase, _db.receipts); $$ReceiptsTableTableManager(_db.attachedDatabase, _db.receipts);
$$MessageActionsTableTableManager get messageActions => $$MessageActionsTableTableManager get messageActions =>
$$MessageActionsTableTableManager( $$MessageActionsTableTableManager(
_db.attachedDatabase, _db.messageActions); _db.attachedDatabase,
_db.messageActions,
);
$$ReceivedReceiptsTableTableManager get receivedReceipts => $$ReceivedReceiptsTableTableManager get receivedReceipts =>
$$ReceivedReceiptsTableTableManager( $$ReceivedReceiptsTableTableManager(
_db.attachedDatabase, _db.receivedReceipts); _db.attachedDatabase,
_db.receivedReceipts,
);
} }

File diff suppressed because it is too large Load diff

View file

@ -20,6 +20,9 @@ class Receipts extends Table {
BoolColumn get contactWillSendsReceipt => BoolColumn get contactWillSendsReceipt =>
boolean().withDefault(const Constant(true))(); boolean().withDefault(const Constant(true))();
BoolColumn get willBeRetriedByMediaUpload =>
boolean().withDefault(const Constant(false))();
DateTimeColumn get markForRetry => dateTime().nullable()(); DateTimeColumn get markForRetry => dateTime().nullable()();
DateTimeColumn get markForRetryAfterAccepted => dateTime().nullable()(); DateTimeColumn get markForRetryAfterAccepted => dateTime().nullable()();

View file

@ -62,7 +62,7 @@ class TwonlyDB extends _$TwonlyDB {
TwonlyDB.forTesting(DatabaseConnection super.connection); TwonlyDB.forTesting(DatabaseConnection super.connection);
@override @override
int get schemaVersion => 9; int get schemaVersion => 10;
static QueryExecutor _openConnection() { static QueryExecutor _openConnection() {
return driftDatabase( return driftDatabase(
@ -137,6 +137,12 @@ class TwonlyDB extends _$TwonlyDB {
schema.mediaFiles.preProgressingProcess, schema.mediaFiles.preProgressingProcess,
); );
}, },
from9To10: (m, schema) async {
await m.addColumn(
schema.receipts,
schema.receipts.willBeRetriedByMediaUpload,
);
},
)(m, from, to); )(m, from, to);
}, },
); );
@ -160,9 +166,9 @@ class TwonlyDB extends _$TwonlyDB {
} }
Future<void> deleteDataForTwonlySafe() async { Future<void> deleteDataForTwonlySafe() async {
await (delete(messages) await (delete(messages)..where(
..where( (t) =>
(t) => (t.mediaStored.equals(false) & (t.mediaStored.equals(false) &
t.isDeletedFromSender.equals(false)), t.isDeletedFromSender.equals(false)),
)) ))
.go(); .go();
@ -171,8 +177,7 @@ class TwonlyDB extends _$TwonlyDB {
downloadToken: Value(null), downloadToken: Value(null),
), ),
); );
await (delete(mediaFiles) await (delete(mediaFiles)..where(
..where(
(t) => (t.stored.equals(false)), (t) => (t.stored.equals(false)),
)) ))
.go(); .go();
@ -184,8 +189,7 @@ class TwonlyDB extends _$TwonlyDB {
senderProfileCounter: Value(0), senderProfileCounter: Value(0),
), ),
); );
await (delete(signalPreKeyStores) await (delete(signalPreKeyStores)..where(
..where(
(t) => (t.createdAt.isSmallerThanValue( (t) => (t.createdAt.isSmallerThanValue(
clock.now().subtract( clock.now().subtract(
const Duration(days: 25), const Duration(days: 25),

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -97,7 +97,7 @@ abstract class AppLocalizations {
static const List<Locale> supportedLocales = <Locale>[ static const List<Locale> supportedLocales = <Locale>[
Locale('de'), Locale('de'),
Locale('en'), Locale('en'),
Locale('sv') Locale('sv'),
]; ];
/// No description provided for @registerTitle. /// No description provided for @registerTitle.
@ -3091,5 +3091,6 @@ AppLocalizations lookupAppLocalizations(Locale locale) {
'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
'an issue with the localizations generation tool. Please file an issue ' 'an issue with the localizations generation tool. Please file an issue '
'on GitHub with a reproducible sample app and the gen-l10n configuration ' 'on GitHub with a reproducible sample app and the gen-l10n configuration '
'that was used.'); 'that was used.',
);
} }

View file

@ -8,14 +8,16 @@ part of 'signal_identity.dart';
SignalIdentity _$SignalIdentityFromJson(Map<String, dynamic> json) => SignalIdentity _$SignalIdentityFromJson(Map<String, dynamic> json) =>
SignalIdentity( SignalIdentity(
identityKeyPairU8List: const Uint8ListConverter() identityKeyPairU8List: const Uint8ListConverter().fromJson(
.fromJson(json['identityKeyPairU8List'] as String), json['identityKeyPairU8List'] as String,
),
registrationId: (json['registrationId'] as num).toInt(), registrationId: (json['registrationId'] as num).toInt(),
); );
Map<String, dynamic> _$SignalIdentityToJson(SignalIdentity instance) => Map<String, dynamic> _$SignalIdentityToJson(SignalIdentity instance) =>
<String, dynamic>{ <String, dynamic>{
'registrationId': instance.registrationId, 'registrationId': instance.registrationId,
'identityKeyPairU8List': 'identityKeyPairU8List': const Uint8ListConverter().toJson(
const Uint8ListConverter().toJson(instance.identityKeyPairU8List), instance.identityKeyPairU8List,
),
}; };

View file

@ -6,7 +6,8 @@ part of 'userdata.dart';
// JsonSerializableGenerator // JsonSerializableGenerator
// ************************************************************************** // **************************************************************************
UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData( UserData _$UserDataFromJson(Map<String, dynamic> json) =>
UserData(
userId: (json['userId'] as num).toInt(), userId: (json['userId'] as num).toInt(),
username: json['username'] as String, username: json['username'] as String,
displayName: json['displayName'] as String, displayName: json['displayName'] as String,
@ -38,8 +39,10 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
.toList() .toList()
..autoDownloadOptions = ..autoDownloadOptions =
(json['autoDownloadOptions'] as Map<String, dynamic>?)?.map( (json['autoDownloadOptions'] as Map<String, dynamic>?)?.map(
(k, e) => (k, e) => MapEntry(
MapEntry(k, (e as List<dynamic>).map((e) => e as String).toList()), k,
(e as List<dynamic>).map((e) => e as String).toList(),
),
) )
..storeMediaFilesInGallery = ..storeMediaFilesInGallery =
json['storeMediaFilesInGallery'] as bool? ?? false json['storeMediaFilesInGallery'] as bool? ?? false
@ -75,7 +78,8 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
..twonlySafeBackup = json['twonlySafeBackup'] == null ..twonlySafeBackup = json['twonlySafeBackup'] == null
? null ? null
: TwonlySafeBackup.fromJson( : TwonlySafeBackup.fromJson(
json['twonlySafeBackup'] as Map<String, dynamic>) json['twonlySafeBackup'] as Map<String, dynamic>,
)
..askedForUserStudyPermission = ..askedForUserStudyPermission =
json['askedForUserStudyPermission'] as bool? ?? false json['askedForUserStudyPermission'] as bool? ?? false
..userStudyParticipantsToken = ..userStudyParticipantsToken =
@ -102,8 +106,7 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
'defaultShowTime': instance.defaultShowTime, 'defaultShowTime': instance.defaultShowTime,
'requestedAudioPermission': instance.requestedAudioPermission, 'requestedAudioPermission': instance.requestedAudioPermission,
'showFeedbackShortcut': instance.showFeedbackShortcut, 'showFeedbackShortcut': instance.showFeedbackShortcut,
'showShowImagePreviewWhenSending': 'showShowImagePreviewWhenSending': instance.showShowImagePreviewWhenSending,
instance.showShowImagePreviewWhenSending,
'startWithCameraOpen': instance.startWithCameraOpen, 'startWithCameraOpen': instance.startWithCameraOpen,
'preSelectedEmojies': instance.preSelectedEmojies, 'preSelectedEmojies': instance.preSelectedEmojies,
'autoDownloadOptions': instance.autoDownloadOptions, 'autoDownloadOptions': instance.autoDownloadOptions,
@ -114,22 +117,22 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
'additionalUserInvites': instance.additionalUserInvites, 'additionalUserInvites': instance.additionalUserInvites,
'tutorialDisplayed': instance.tutorialDisplayed, 'tutorialDisplayed': instance.tutorialDisplayed,
'myBestFriendGroupId': instance.myBestFriendGroupId, 'myBestFriendGroupId': instance.myBestFriendGroupId,
'signalLastSignedPreKeyUpdated': 'signalLastSignedPreKeyUpdated': instance.signalLastSignedPreKeyUpdated
instance.signalLastSignedPreKeyUpdated?.toIso8601String(), ?.toIso8601String(),
'allowErrorTrackingViaSentry': instance.allowErrorTrackingViaSentry, 'allowErrorTrackingViaSentry': instance.allowErrorTrackingViaSentry,
'currentPreKeyIndexStart': instance.currentPreKeyIndexStart, 'currentPreKeyIndexStart': instance.currentPreKeyIndexStart,
'currentSignedPreKeyIndexStart': instance.currentSignedPreKeyIndexStart, 'currentSignedPreKeyIndexStart': instance.currentSignedPreKeyIndexStart,
'lastChangeLogHash': instance.lastChangeLogHash, 'lastChangeLogHash': instance.lastChangeLogHash,
'hideChangeLog': instance.hideChangeLog, 'hideChangeLog': instance.hideChangeLog,
'updateFCMToken': instance.updateFCMToken, 'updateFCMToken': instance.updateFCMToken,
'nextTimeToShowBackupNotice': 'nextTimeToShowBackupNotice': instance.nextTimeToShowBackupNotice
instance.nextTimeToShowBackupNotice?.toIso8601String(), ?.toIso8601String(),
'backupServer': instance.backupServer, 'backupServer': instance.backupServer,
'twonlySafeBackup': instance.twonlySafeBackup, 'twonlySafeBackup': instance.twonlySafeBackup,
'askedForUserStudyPermission': instance.askedForUserStudyPermission, 'askedForUserStudyPermission': instance.askedForUserStudyPermission,
'userStudyParticipantsToken': instance.userStudyParticipantsToken, 'userStudyParticipantsToken': instance.userStudyParticipantsToken,
'lastUserStudyDataUpload': 'lastUserStudyDataUpload': instance.lastUserStudyDataUpload
instance.lastUserStudyDataUpload?.toIso8601String(), ?.toIso8601String(),
}; };
const _$ThemeModeEnumMap = { const _$ThemeModeEnumMap = {
@ -148,8 +151,10 @@ TwonlySafeBackup _$TwonlySafeBackupFromJson(Map<String, dynamic> json) =>
.toList(), .toList(),
) )
..lastBackupSize = (json['lastBackupSize'] as num).toInt() ..lastBackupSize = (json['lastBackupSize'] as num).toInt()
..backupUploadState = ..backupUploadState = $enumDecode(
$enumDecode(_$LastBackupUploadStateEnumMap, json['backupUploadState']) _$LastBackupUploadStateEnumMap,
json['backupUploadState'],
)
..lastBackupDone = json['lastBackupDone'] == null ..lastBackupDone = json['lastBackupDone'] == null
? null ? null
: DateTime.parse(json['lastBackupDone'] as String); : DateTime.parse(json['lastBackupDone'] as String);

View file

@ -27,8 +27,8 @@ import 'package:twonly/src/utils/misc.dart';
import 'package:workmanager/workmanager.dart' hide TaskStatus; import 'package:workmanager/workmanager.dart' hide TaskStatus;
Future<void> finishStartedPreprocessing() async { Future<void> finishStartedPreprocessing() async {
final mediaFiles = final mediaFiles = await twonlyDB.mediaFilesDao
await twonlyDB.mediaFilesDao.getAllMediaFilesPendingUpload(); .getAllMediaFilesPendingUpload();
Log.info('There are ${mediaFiles.length} media files pending'); Log.info('There are ${mediaFiles.length} media files pending');
@ -62,8 +62,9 @@ Future<void> finishStartedPreprocessing() async {
/// For example because the background_downloader plugin has not yet reported the finished upload. /// For example because the background_downloader plugin has not yet reported the finished upload.
/// In case the the message receipts or a reaction was received, mark the media file as been uploaded. /// In case the the message receipts or a reaction was received, mark the media file as been uploaded.
Future<void> handleMediaRelatedResponseFromReceiver(String messageId) async { Future<void> handleMediaRelatedResponseFromReceiver(String messageId) async {
final message = final message = await twonlyDB.messagesDao
await twonlyDB.messagesDao.getMessageById(messageId).getSingleOrNull(); .getMessageById(messageId)
.getSingleOrNull();
if (message == null || message.mediaId == null) return; if (message == null || message.mediaId == null) return;
final media = await twonlyDB.mediaFilesDao.getMediaFileById(message.mediaId!); final media = await twonlyDB.mediaFilesDao.getMediaFileById(message.mediaId!);
if (media == null) return; if (media == null) return;
@ -84,11 +85,13 @@ Future<void> markUploadAsSuccessful(MediaFile media) async {
/// As the messages where send in a bulk acknowledge all messages. /// As the messages where send in a bulk acknowledge all messages.
final messages = final messages = await twonlyDB.messagesDao.getMessagesByMediaId(
await twonlyDB.messagesDao.getMessagesByMediaId(media.mediaId); media.mediaId,
);
for (final message in messages) { for (final message in messages) {
final contacts = final contacts = await twonlyDB.groupsDao.getGroupNonLeftMembers(
await twonlyDB.groupsDao.getGroupNonLeftMembers(message.groupId); message.groupId,
);
for (final contact in contacts) { for (final contact in contacts) {
await twonlyDB.messagesDao.handleMessageAckByServer( await twonlyDB.messagesDao.handleMessageAckByServer(
contact.contactId, contact.contactId,
@ -147,8 +150,9 @@ Future<void> insertMediaFileInMessagesTable(
groupId: Value(groupId), groupId: Value(groupId),
mediaId: Value(mediaService.mediaFile.mediaId), mediaId: Value(mediaService.mediaFile.mediaId),
type: Value(MessageType.media.name), type: Value(MessageType.media.name),
additionalMessageData: additionalMessageData: Value.absentIfNull(
Value.absentIfNull(additionalData?.writeToBuffer()), additionalData?.writeToBuffer(),
),
), ),
); );
await twonlyDB.groupsDao.increaseLastMessageExchange(groupId, clock.now()); await twonlyDB.groupsDao.increaseLastMessageExchange(groupId, clock.now());
@ -236,8 +240,9 @@ Future<void> _encryptMediaFiles(MediaFileService mediaService) async {
await mediaService.setEncryptedMac(Uint8List.fromList(secretBox.mac.bytes)); await mediaService.setEncryptedMac(Uint8List.fromList(secretBox.mac.bytes));
mediaService.encryptedPath mediaService.encryptedPath.writeAsBytesSync(
.writeAsBytesSync(Uint8List.fromList(secretBox.cipherText)); Uint8List.fromList(secretBox.cipherText),
);
} }
Future<void> _createUploadRequest(MediaFileService media) async { Future<void> _createUploadRequest(MediaFileService media) async {
@ -245,8 +250,9 @@ Future<void> _createUploadRequest(MediaFileService media) async {
final messagesOnSuccess = <TextMessage>[]; final messagesOnSuccess = <TextMessage>[];
final messages = final messages = await twonlyDB.messagesDao.getMessagesByMediaId(
await twonlyDB.messagesDao.getMessagesByMediaId(media.mediaFile.mediaId); media.mediaFile.mediaId,
);
if (messages.isEmpty) { if (messages.isEmpty) {
// There where no user selected who should receive the image, so waiting with this step... // There where no user selected who should receive the image, so waiting with this step...
@ -254,8 +260,9 @@ Future<void> _createUploadRequest(MediaFileService media) async {
} }
for (final message in messages) { for (final message in messages) {
final groupMembers = final groupMembers = await twonlyDB.groupsDao.getGroupNonLeftMembers(
await twonlyDB.groupsDao.getGroupNonLeftMembers(message.groupId); message.groupId,
);
if (media.mediaFile.reuploadRequestedBy == null) { if (media.mediaFile.reuploadRequestedBy == null) {
await incFlameCounter(message.groupId, false, message.createdAt); await incFlameCounter(message.groupId, false, message.createdAt);
@ -264,8 +271,9 @@ Future<void> _createUploadRequest(MediaFileService media) async {
for (final groupMember in groupMembers) { for (final groupMember in groupMembers) {
/// only send the upload to the users /// only send the upload to the users
if (media.mediaFile.reuploadRequestedBy != null) { if (media.mediaFile.reuploadRequestedBy != null) {
if (!media.mediaFile.reuploadRequestedBy! if (!media.mediaFile.reuploadRequestedBy!.contains(
.contains(groupMember.contactId)) { groupMember.contactId,
)) {
continue; continue;
} }
} }
@ -304,8 +312,9 @@ Future<void> _createUploadRequest(MediaFileService media) async {
); );
if (media.mediaFile.displayLimitInMilliseconds != null) { if (media.mediaFile.displayLimitInMilliseconds != null) {
notEncryptedContent.media.displayLimitInMilliseconds = notEncryptedContent.media.displayLimitInMilliseconds = Int64(
Int64(media.mediaFile.displayLimitInMilliseconds!); media.mediaFile.displayLimitInMilliseconds!,
);
} }
final cipherText = await sendCipherText( final cipherText = await sendCipherText(
@ -345,6 +354,14 @@ Future<void> _createUploadRequest(MediaFileService media) async {
if (uploadRequestBytes.length > 49_000_000) { if (uploadRequestBytes.length > 49_000_000) {
await media.setUploadState(UploadState.fileLimitReached); await media.setUploadState(UploadState.fileLimitReached);
await twonlyDB.messagesDao.updateMessagesByMediaId(
media.mediaFile.mediaId,
MessagesCompanion(
openedAt: Value(DateTime.now()),
ackByServer: Value(DateTime.now()),
),
);
return; return;
} }
@ -355,8 +372,9 @@ Mutex protectUpload = Mutex();
Future<void> _uploadUploadRequest(MediaFileService media) async { Future<void> _uploadUploadRequest(MediaFileService media) async {
await protectUpload.protect(() async { await protectUpload.protect(() async {
final currentMedia = final currentMedia = await twonlyDB.mediaFilesDao.getMediaFileById(
await twonlyDB.mediaFilesDao.getMediaFileById(media.mediaFile.mediaId); media.mediaFile.mediaId,
);
if (currentMedia == null || if (currentMedia == null ||
currentMedia.uploadState == UploadState.backgroundUploadTaskStarted) { currentMedia.uploadState == UploadState.backgroundUploadTaskStarted) {
@ -364,8 +382,9 @@ Future<void> _uploadUploadRequest(MediaFileService media) async {
return null; return null;
} }
final apiAuthTokenRaw = await const FlutterSecureStorage() final apiAuthTokenRaw = await const FlutterSecureStorage().read(
.read(key: SecureStorageKeys.apiAuthToken); key: SecureStorageKeys.apiAuthToken,
);
if (apiAuthTokenRaw == null) { if (apiAuthTokenRaw == null) {
Log.error('api auth token not defined.'); Log.error('api auth token not defined.');

View file

@ -79,8 +79,9 @@ Future<(Uint8List, Uint8List?)?> tryToSendCompleteMessage({
// ignore: parameter_assignments // ignore: parameter_assignments
receiptId = receipt.receiptId; receiptId = receipt.receiptId;
final contact = final contact = await twonlyDB.contactsDao.getContactById(
await twonlyDB.contactsDao.getContactById(receipt.contactId); receipt.contactId,
);
if (contact == null || contact.accountDeleted) { if (contact == null || contact.accountDeleted) {
Log.warn('Will not send message again as user does not exist anymore.'); Log.warn('Will not send message again as user does not exist anymore.');
await twonlyDB.receiptsDao.deleteReceipt(receiptId); await twonlyDB.receiptsDao.deleteReceipt(receiptId);
@ -99,8 +100,9 @@ Future<(Uint8List, Uint8List?)?> tryToSendCompleteMessage({
final message = pb.Message.fromBuffer(receipt.message) final message = pb.Message.fromBuffer(receipt.message)
..receiptId = receiptId; ..receiptId = receiptId;
final encryptedContent = final encryptedContent = pb.EncryptedContent.fromBuffer(
pb.EncryptedContent.fromBuffer(message.encryptedContent); message.encryptedContent,
);
final pushNotification = await getPushNotificationFromEncryptedContent( final pushNotification = await getPushNotificationFromEncryptedContent(
receipt.contactId, receipt.contactId,
@ -111,8 +113,10 @@ Future<(Uint8List, Uint8List?)?> tryToSendCompleteMessage({
Uint8List? pushData; Uint8List? pushData;
if (pushNotification != null && receipt.retryCount <= 3) { if (pushNotification != null && receipt.retryCount <= 3) {
/// In case the message has to be resend more than three times, do not show a notification again... /// In case the message has to be resend more than three times, do not show a notification again...
pushData = pushData = await encryptPushNotification(
await encryptPushNotification(receipt.contactId, pushNotification); receipt.contactId,
pushNotification,
);
} }
if (message.type == pb.Message_Type.TEST_NOTIFICATION) { if (message.type == pb.Message_Type.TEST_NOTIFICATION) {
@ -331,7 +335,7 @@ Future<(Uint8List, Uint8List?)?> sendCipherText(
contactId: Value(contactId), contactId: Value(contactId),
message: Value(response.writeToBuffer()), message: Value(response.writeToBuffer()),
messageId: Value(messageId), messageId: Value(messageId),
ackByServerAt: Value(onlyReturnEncryptedData ? clock.now() : null), willBeRetriedByMediaUpload: Value(onlyReturnEncryptedData),
), ),
); );

View file

@ -33,7 +33,7 @@ class FilterSkeleton extends StatelessWidget {
child: Stack( child: Stack(
children: [ children: [
Positioned.fill(child: Container()), Positioned.fill(child: Container()),
if (child != null) child!, ?child,
], ],
), ),
); );
@ -89,7 +89,8 @@ class _FilterLayerState extends State<FilterLayer> {
} }
Future<void> initAsync() async { Future<void> initAsync() async {
final stickers = (await getStickerIndex()) final stickers =
(await getStickerIndex())
.where((x) => x.imageSrc.contains('/imagefilter/')) .where((x) => x.imageSrc.contains('/imagefilter/'))
.toList() .toList()
..sortBy((x) => x.imageSrc); ..sortBy((x) => x.imageSrc);

View file

@ -26,15 +26,17 @@ class _ShareAdditionalViewState extends State<ShareAdditionalView> {
} }
Future<void> openShareContactView() async { Future<void> openShareContactView() async {
final selectedContacts = await context.navPush( final selectedContacts =
await context.navPush(
SelectContactsView( SelectContactsView(
text: SelectedContactViewText( text: SelectedContactViewText(
title: context.lang.shareContactsTitle, title: context.lang.shareContactsTitle,
submitButton: (_, __) => context.lang.shareContactsSubmit, submitButton: (_, _) => context.lang.shareContactsSubmit,
submitIcon: FontAwesomeIcons.shareNodes, submitIcon: FontAwesomeIcons.shareNodes,
), ),
), ),
) as List<int>?; )
as List<int>?;
if (selectedContacts != null && selectedContacts.isNotEmpty) { if (selectedContacts != null && selectedContacts.isNotEmpty) {
await insertAndSendContactShareMessage( await insertAndSendContactShareMessage(
widget.group.groupId, widget.group.groupId,

View file

@ -101,8 +101,9 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
final mediaFile = message.mediaId == null final mediaFile = message.mediaId == null
? null ? null
: widget.mediaFiles : widget.mediaFiles.firstWhereOrNull(
.firstWhereOrNull((t) => t.mediaId == message.mediaId); (t) => t.mediaId == message.mediaId,
);
final color = getMessageColorFromType(message, mediaFile, context); final color = getMessageColorFromType(message, mediaFile, context);
@ -144,8 +145,11 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
} }
} }
case MessageSendState.send: case MessageSendState.send:
icon = icon = FaIcon(
FaIcon(FontAwesomeIcons.solidPaperPlane, size: 12, color: color); FontAwesomeIcons.solidPaperPlane,
size: 12,
color: color,
);
text = context.lang.messageSendState_Send; text = context.lang.messageSendState_Send;
case MessageSendState.sending: case MessageSendState.sending:
icon = getLoaderIcon(color); icon = getLoaderIcon(color);
@ -163,22 +167,9 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
context.lang.uploadLimitReached, context.lang.uploadLimitReached,
style: const TextStyle(fontSize: 9), style: const TextStyle(fontSize: 9),
); );
}
if (mediaFile.uploadState == UploadState.fileLimitReached) {
icon = FaIcon(
FontAwesomeIcons.triangleExclamation,
size: 12,
color: color,
);
textWidget = Text(
context.lang.fileLimitReached,
style: const TextStyle(fontSize: 9),
);
onTap = () => context.push(Routes.settingsSubscription); onTap = () => context.push(Routes.settingsSubscription);
} }
if (mediaFile.uploadState == UploadState.initialized) { if (mediaFile.uploadState == UploadState.initialized) {
text = context.lang.inProcess; text = context.lang.inProcess;
} }
@ -211,13 +202,28 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
} }
if (mediaFile.downloadState == DownloadState.reuploadRequested) { if (mediaFile.downloadState == DownloadState.reuploadRequested) {
icon = icon = FaIcon(
FaIcon(FontAwesomeIcons.clockRotateLeft, size: 12, color: color); FontAwesomeIcons.clockRotateLeft,
size: 12,
color: color,
);
textWidget = Text( textWidget = Text(
context.lang.retransmissionRequested, context.lang.retransmissionRequested,
style: const TextStyle(fontSize: 9), style: const TextStyle(fontSize: 9),
); );
} }
if (mediaFile.uploadState == UploadState.fileLimitReached) {
icon = FaIcon(
FontAwesomeIcons.triangleExclamation,
size: 12,
color: color,
);
textWidget = Text(
context.lang.fileLimitReached,
style: const TextStyle(fontSize: 9),
);
}
} }
if (message.isDeletedFromSender) { if (message.isDeletedFromSender) {
@ -240,10 +246,12 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
if (!widget.messages.any((t) => t.openedAt == null)) { if (!widget.messages.any((t) => t.openedAt == null)) {
if (widget.lastReaction != 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(
.any((m) => m.createdAt.isAfter(widget.lastReaction!.createdAt))) { (m) => m.createdAt.isAfter(widget.lastReaction!.createdAt),
if (EmojiAnimation.animatedIcons )) {
.containsKey(widget.lastReaction!.emoji)) { if (EmojiAnimation.animatedIcons.containsKey(
widget.lastReaction!.emoji,
)) {
icons = [ icons = [
SizedBox( SizedBox(
height: 18, height: 18,

View file

@ -52,8 +52,7 @@ class _AvatarIconState extends State<AvatarIcon> {
super.dispose(); super.dispose();
} }
// ignore: strict_top_level_inference Widget errorBuilder(_, _, _) {
Widget errorBuilder(_, __, ___) {
return const SvgPicture( return const SvgPicture(
AssetBytesLoader('assets/images/default_avatar.svg.vec'), AssetBytesLoader('assets/images/default_avatar.svg.vec'),
); );

View file

@ -74,7 +74,7 @@ class _ThreeRotatingDotsState extends State<ThreeRotatingDots>
height: size, height: size,
child: AnimatedBuilder( child: AnimatedBuilder(
animation: _animationController, animation: _animationController,
builder: (_, __) => Transform.translate( builder: (_, _) => Transform.translate(
offset: Offset(0, size / 12), offset: Offset(0, size / 12),
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
@ -110,7 +110,6 @@ class _ThreeRotatingDotsState extends State<ThreeRotatingDots>
), ),
/// Next 3 dots /// Next 3 dots
_BuildDot.second( _BuildDot.second(
controller: _animationController, controller: _animationController,
beginAngle: 0, beginAngle: 0,

View file

@ -569,8 +569,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: HEAD ref: "fix/lStar-not-found-error"
resolved-ref: e8b6a650b9fbe76a077539272bbdd422509e6e44 resolved-ref: e926724e0c7e62bce4f94905f9805a264b736d11
url: "https://github.com/yenchieh/flutter_android_volume_keydown.git" url: "https://github.com/yenchieh/flutter_android_volume_keydown.git"
source: git source: git
version: "1.0.1" version: "1.0.1"

View file

@ -6,7 +6,7 @@ publish_to: 'none'
version: 0.0.97+97 version: 0.0.97+97
environment: environment:
sdk: ^3.6.0 sdk: ^3.11.0
dependencies: dependencies:
flutter: flutter:
@ -161,13 +161,7 @@ dependency_overrides:
flutter_android_volume_keydown: flutter_android_volume_keydown:
git: git:
url: https://github.com/yenchieh/flutter_android_volume_keydown.git url: https://github.com/yenchieh/flutter_android_volume_keydown.git
branch: fix/lStar-not-found-error ref: fix/lStar-not-found-error
# flutter_secure_storage_darwin:
# git:
# url: https://github.com/juliansteenbakker/flutter_secure_storage.git
# ref: a06ead81809c900e7fc421a30db0adf3b5919139 # from develop
# path: flutter_secure_storage_darwin/
# hardcoding the mirror mode of the VideCapture to MIRROR_MODE_ON_FRONT_ONLY
dev_dependencies: dev_dependencies:
build_runner: ^2.4.15 build_runner: ^2.4.15

View file

@ -13,6 +13,7 @@ import 'schema_v6.dart' as v6;
import 'schema_v7.dart' as v7; import 'schema_v7.dart' as v7;
import 'schema_v8.dart' as v8; import 'schema_v8.dart' as v8;
import 'schema_v9.dart' as v9; import 'schema_v9.dart' as v9;
import 'schema_v10.dart' as v10;
class GeneratedHelper implements SchemaInstantiationHelper { class GeneratedHelper implements SchemaInstantiationHelper {
@override @override
@ -36,10 +37,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v8.DatabaseAtV8(db); return v8.DatabaseAtV8(db);
case 9: case 9:
return v9.DatabaseAtV9(db); return v9.DatabaseAtV9(db);
case 10:
return v10.DatabaseAtV10(db);
default: default:
throw MissingSchemaException(version, versions); throw MissingSchemaException(version, versions);
} }
} }
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9]; static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff