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: Show message "Flames restored"
- 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 with deleting a contact
- Fix: Problem with restoring from backup

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -30,8 +30,12 @@ class ReceiptsDaoManager {
$$ReceiptsTableTableManager(_db.attachedDatabase, _db.receipts);
$$MessageActionsTableTableManager get messageActions =>
$$MessageActionsTableTableManager(
_db.attachedDatabase, _db.messageActions);
_db.attachedDatabase,
_db.messageActions,
);
$$ReceivedReceiptsTableTableManager get receivedReceipts =>
$$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 =>
boolean().withDefault(const Constant(true))();
BoolColumn get willBeRetriedByMediaUpload =>
boolean().withDefault(const Constant(false))();
DateTimeColumn get markForRetry => dateTime().nullable()();
DateTimeColumn get markForRetryAfterAccepted => dateTime().nullable()();

View file

@ -54,15 +54,15 @@ part 'twonly.db.g.dart';
)
class TwonlyDB extends _$TwonlyDB {
TwonlyDB([QueryExecutor? e])
: super(
e ?? _openConnection(),
);
: super(
e ?? _openConnection(),
);
// ignore: matching_super_parameters
TwonlyDB.forTesting(DatabaseConnection super.connection);
@override
int get schemaVersion => 9;
int get schemaVersion => 10;
static QueryExecutor _openConnection() {
return driftDatabase(
@ -137,6 +137,12 @@ class TwonlyDB extends _$TwonlyDB {
schema.mediaFiles.preProgressingProcess,
);
},
from9To10: (m, schema) async {
await m.addColumn(
schema.receipts,
schema.receipts.willBeRetriedByMediaUpload,
);
},
)(m, from, to);
},
);
@ -160,21 +166,20 @@ class TwonlyDB extends _$TwonlyDB {
}
Future<void> deleteDataForTwonlySafe() async {
await (delete(messages)
..where(
(t) => (t.mediaStored.equals(false) &
t.isDeletedFromSender.equals(false)),
))
await (delete(messages)..where(
(t) =>
(t.mediaStored.equals(false) &
t.isDeletedFromSender.equals(false)),
))
.go();
await update(messages).write(
const MessagesCompanion(
downloadToken: Value(null),
),
);
await (delete(mediaFiles)
..where(
(t) => (t.stored.equals(false)),
))
await (delete(mediaFiles)..where(
(t) => (t.stored.equals(false)),
))
.go();
await delete(receipts).go();
await delete(receivedReceipts).go();
@ -184,14 +189,13 @@ class TwonlyDB extends _$TwonlyDB {
senderProfileCounter: Value(0),
),
);
await (delete(signalPreKeyStores)
..where(
(t) => (t.createdAt.isSmallerThanValue(
clock.now().subtract(
const Duration(days: 25),
),
)),
))
await (delete(signalPreKeyStores)..where(
(t) => (t.createdAt.isSmallerThanValue(
clock.now().subtract(
const Duration(days: 25),
),
)),
))
.go();
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -64,7 +64,7 @@ import 'app_localizations_sv.dart';
/// property.
abstract class AppLocalizations {
AppLocalizations(String locale)
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
final String localeName;
@ -87,17 +87,17 @@ abstract class AppLocalizations {
/// of delegates is preferred or required.
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
<LocalizationsDelegate<dynamic>>[
delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
];
delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
];
/// A list of this localizations delegate's supported locales.
static const List<Locale> supportedLocales = <Locale>[
Locale('de'),
Locale('en'),
Locale('sv')
Locale('sv'),
];
/// No description provided for @registerTitle.
@ -3088,8 +3088,9 @@ AppLocalizations lookupAppLocalizations(Locale locale) {
}
throw FlutterError(
'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
'an issue with the localizations generation tool. Please file an issue '
'on GitHub with a reproducible sample app and the gen-l10n configuration '
'that was used.');
'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
'an issue with the localizations generation tool. Please file an issue '
'on GitHub with a reproducible sample app and the gen-l10n configuration '
'that was used.',
);
}

View file

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

View file

@ -6,12 +6,13 @@ part of 'userdata.dart';
// JsonSerializableGenerator
// **************************************************************************
UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
userId: (json['userId'] as num).toInt(),
username: json['username'] as String,
displayName: json['displayName'] as String,
subscriptionPlan: json['subscriptionPlan'] as String? ?? 'Free',
)
UserData _$UserDataFromJson(Map<String, dynamic> json) =>
UserData(
userId: (json['userId'] as num).toInt(),
username: json['username'] as String,
displayName: json['displayName'] as String,
subscriptionPlan: json['subscriptionPlan'] as String? ?? 'Free',
)
..avatarSvg = json['avatarSvg'] as String?
..avatarJson = json['avatarJson'] as String?
..appVersion = (json['appVersion'] as num?)?.toInt() ?? 0
@ -25,7 +26,7 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
..todaysImageCounter = (json['todaysImageCounter'] as num?)?.toInt()
..themeMode =
$enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ??
ThemeMode.system
ThemeMode.system
..defaultShowTime = (json['defaultShowTime'] as num?)?.toInt()
..requestedAudioPermission =
json['requestedAudioPermission'] as bool? ?? false
@ -38,9 +39,11 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
.toList()
..autoDownloadOptions =
(json['autoDownloadOptions'] as Map<String, dynamic>?)?.map(
(k, e) =>
MapEntry(k, (e as List<dynamic>).map((e) => e as String).toList()),
)
(k, e) => MapEntry(
k,
(e as List<dynamic>).map((e) => e as String).toList(),
),
)
..storeMediaFilesInGallery =
json['storeMediaFilesInGallery'] as bool? ?? false
..autoStoreAllSendUnlimitedMediaFiles =
@ -53,8 +56,8 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
..myBestFriendGroupId = json['myBestFriendGroupId'] as String?
..signalLastSignedPreKeyUpdated =
json['signalLastSignedPreKeyUpdated'] == null
? null
: DateTime.parse(json['signalLastSignedPreKeyUpdated'] as String)
? null
: DateTime.parse(json['signalLastSignedPreKeyUpdated'] as String)
..allowErrorTrackingViaSentry =
json['allowErrorTrackingViaSentry'] as bool? ?? false
..currentPreKeyIndexStart =
@ -75,7 +78,8 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
..twonlySafeBackup = json['twonlySafeBackup'] == null
? null
: TwonlySafeBackup.fromJson(
json['twonlySafeBackup'] as Map<String, dynamic>)
json['twonlySafeBackup'] as Map<String, dynamic>,
)
..askedForUserStudyPermission =
json['askedForUserStudyPermission'] as bool? ?? false
..userStudyParticipantsToken =
@ -85,52 +89,51 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
: DateTime.parse(json['lastUserStudyDataUpload'] as String);
Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
'userId': instance.userId,
'username': instance.username,
'displayName': instance.displayName,
'avatarSvg': instance.avatarSvg,
'avatarJson': instance.avatarJson,
'appVersion': instance.appVersion,
'avatarCounter': instance.avatarCounter,
'isDeveloper': instance.isDeveloper,
'deviceId': instance.deviceId,
'subscriptionPlan': instance.subscriptionPlan,
'subscriptionPlanIdStore': instance.subscriptionPlanIdStore,
'lastImageSend': instance.lastImageSend?.toIso8601String(),
'todaysImageCounter': instance.todaysImageCounter,
'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,
'defaultShowTime': instance.defaultShowTime,
'requestedAudioPermission': instance.requestedAudioPermission,
'showFeedbackShortcut': instance.showFeedbackShortcut,
'showShowImagePreviewWhenSending':
instance.showShowImagePreviewWhenSending,
'startWithCameraOpen': instance.startWithCameraOpen,
'preSelectedEmojies': instance.preSelectedEmojies,
'autoDownloadOptions': instance.autoDownloadOptions,
'storeMediaFilesInGallery': instance.storeMediaFilesInGallery,
'autoStoreAllSendUnlimitedMediaFiles':
instance.autoStoreAllSendUnlimitedMediaFiles,
'lastPlanBallance': instance.lastPlanBallance,
'additionalUserInvites': instance.additionalUserInvites,
'tutorialDisplayed': instance.tutorialDisplayed,
'myBestFriendGroupId': instance.myBestFriendGroupId,
'signalLastSignedPreKeyUpdated':
instance.signalLastSignedPreKeyUpdated?.toIso8601String(),
'allowErrorTrackingViaSentry': instance.allowErrorTrackingViaSentry,
'currentPreKeyIndexStart': instance.currentPreKeyIndexStart,
'currentSignedPreKeyIndexStart': instance.currentSignedPreKeyIndexStart,
'lastChangeLogHash': instance.lastChangeLogHash,
'hideChangeLog': instance.hideChangeLog,
'updateFCMToken': instance.updateFCMToken,
'nextTimeToShowBackupNotice':
instance.nextTimeToShowBackupNotice?.toIso8601String(),
'backupServer': instance.backupServer,
'twonlySafeBackup': instance.twonlySafeBackup,
'askedForUserStudyPermission': instance.askedForUserStudyPermission,
'userStudyParticipantsToken': instance.userStudyParticipantsToken,
'lastUserStudyDataUpload':
instance.lastUserStudyDataUpload?.toIso8601String(),
};
'userId': instance.userId,
'username': instance.username,
'displayName': instance.displayName,
'avatarSvg': instance.avatarSvg,
'avatarJson': instance.avatarJson,
'appVersion': instance.appVersion,
'avatarCounter': instance.avatarCounter,
'isDeveloper': instance.isDeveloper,
'deviceId': instance.deviceId,
'subscriptionPlan': instance.subscriptionPlan,
'subscriptionPlanIdStore': instance.subscriptionPlanIdStore,
'lastImageSend': instance.lastImageSend?.toIso8601String(),
'todaysImageCounter': instance.todaysImageCounter,
'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,
'defaultShowTime': instance.defaultShowTime,
'requestedAudioPermission': instance.requestedAudioPermission,
'showFeedbackShortcut': instance.showFeedbackShortcut,
'showShowImagePreviewWhenSending': instance.showShowImagePreviewWhenSending,
'startWithCameraOpen': instance.startWithCameraOpen,
'preSelectedEmojies': instance.preSelectedEmojies,
'autoDownloadOptions': instance.autoDownloadOptions,
'storeMediaFilesInGallery': instance.storeMediaFilesInGallery,
'autoStoreAllSendUnlimitedMediaFiles':
instance.autoStoreAllSendUnlimitedMediaFiles,
'lastPlanBallance': instance.lastPlanBallance,
'additionalUserInvites': instance.additionalUserInvites,
'tutorialDisplayed': instance.tutorialDisplayed,
'myBestFriendGroupId': instance.myBestFriendGroupId,
'signalLastSignedPreKeyUpdated': instance.signalLastSignedPreKeyUpdated
?.toIso8601String(),
'allowErrorTrackingViaSentry': instance.allowErrorTrackingViaSentry,
'currentPreKeyIndexStart': instance.currentPreKeyIndexStart,
'currentSignedPreKeyIndexStart': instance.currentSignedPreKeyIndexStart,
'lastChangeLogHash': instance.lastChangeLogHash,
'hideChangeLog': instance.hideChangeLog,
'updateFCMToken': instance.updateFCMToken,
'nextTimeToShowBackupNotice': instance.nextTimeToShowBackupNotice
?.toIso8601String(),
'backupServer': instance.backupServer,
'twonlySafeBackup': instance.twonlySafeBackup,
'askedForUserStudyPermission': instance.askedForUserStudyPermission,
'userStudyParticipantsToken': instance.userStudyParticipantsToken,
'lastUserStudyDataUpload': instance.lastUserStudyDataUpload
?.toIso8601String(),
};
const _$ThemeModeEnumMap = {
ThemeMode.system: 'system',
@ -140,16 +143,18 @@ const _$ThemeModeEnumMap = {
TwonlySafeBackup _$TwonlySafeBackupFromJson(Map<String, dynamic> json) =>
TwonlySafeBackup(
backupId: (json['backupId'] as List<dynamic>)
.map((e) => (e as num).toInt())
.toList(),
encryptionKey: (json['encryptionKey'] as List<dynamic>)
.map((e) => (e as num).toInt())
.toList(),
)
backupId: (json['backupId'] as List<dynamic>)
.map((e) => (e as num).toInt())
.toList(),
encryptionKey: (json['encryptionKey'] as List<dynamic>)
.map((e) => (e as num).toInt())
.toList(),
)
..lastBackupSize = (json['lastBackupSize'] as num).toInt()
..backupUploadState =
$enumDecode(_$LastBackupUploadStateEnumMap, json['backupUploadState'])
..backupUploadState = $enumDecode(
_$LastBackupUploadStateEnumMap,
json['backupUploadState'],
)
..lastBackupDone = json['lastBackupDone'] == null
? null
: DateTime.parse(json['lastBackupDone'] as String);
@ -172,10 +177,10 @@ const _$LastBackupUploadStateEnumMap = {
};
BackupServer _$BackupServerFromJson(Map<String, dynamic> json) => BackupServer(
serverUrl: json['serverUrl'] as String,
retentionDays: (json['retentionDays'] as num).toInt(),
maxBackupBytes: (json['maxBackupBytes'] as num).toInt(),
);
serverUrl: json['serverUrl'] as String,
retentionDays: (json['retentionDays'] as num).toInt(),
maxBackupBytes: (json['maxBackupBytes'] as num).toInt(),
);
Map<String, dynamic> _$BackupServerToJson(BackupServer instance) =>
<String, dynamic>{

View file

@ -27,8 +27,8 @@ import 'package:twonly/src/utils/misc.dart';
import 'package:workmanager/workmanager.dart' hide TaskStatus;
Future<void> finishStartedPreprocessing() async {
final mediaFiles =
await twonlyDB.mediaFilesDao.getAllMediaFilesPendingUpload();
final mediaFiles = await twonlyDB.mediaFilesDao
.getAllMediaFilesPendingUpload();
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.
/// In case the the message receipts or a reaction was received, mark the media file as been uploaded.
Future<void> handleMediaRelatedResponseFromReceiver(String messageId) async {
final message =
await twonlyDB.messagesDao.getMessageById(messageId).getSingleOrNull();
final message = await twonlyDB.messagesDao
.getMessageById(messageId)
.getSingleOrNull();
if (message == null || message.mediaId == null) return;
final media = await twonlyDB.mediaFilesDao.getMediaFileById(message.mediaId!);
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.
final messages =
await twonlyDB.messagesDao.getMessagesByMediaId(media.mediaId);
final messages = await twonlyDB.messagesDao.getMessagesByMediaId(
media.mediaId,
);
for (final message in messages) {
final contacts =
await twonlyDB.groupsDao.getGroupNonLeftMembers(message.groupId);
final contacts = await twonlyDB.groupsDao.getGroupNonLeftMembers(
message.groupId,
);
for (final contact in contacts) {
await twonlyDB.messagesDao.handleMessageAckByServer(
contact.contactId,
@ -147,8 +150,9 @@ Future<void> insertMediaFileInMessagesTable(
groupId: Value(groupId),
mediaId: Value(mediaService.mediaFile.mediaId),
type: Value(MessageType.media.name),
additionalMessageData:
Value.absentIfNull(additionalData?.writeToBuffer()),
additionalMessageData: Value.absentIfNull(
additionalData?.writeToBuffer(),
),
),
);
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));
mediaService.encryptedPath
.writeAsBytesSync(Uint8List.fromList(secretBox.cipherText));
mediaService.encryptedPath.writeAsBytesSync(
Uint8List.fromList(secretBox.cipherText),
);
}
Future<void> _createUploadRequest(MediaFileService media) async {
@ -245,8 +250,9 @@ Future<void> _createUploadRequest(MediaFileService media) async {
final messagesOnSuccess = <TextMessage>[];
final messages =
await twonlyDB.messagesDao.getMessagesByMediaId(media.mediaFile.mediaId);
final messages = await twonlyDB.messagesDao.getMessagesByMediaId(
media.mediaFile.mediaId,
);
if (messages.isEmpty) {
// 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) {
final groupMembers =
await twonlyDB.groupsDao.getGroupNonLeftMembers(message.groupId);
final groupMembers = await twonlyDB.groupsDao.getGroupNonLeftMembers(
message.groupId,
);
if (media.mediaFile.reuploadRequestedBy == null) {
await incFlameCounter(message.groupId, false, message.createdAt);
@ -264,8 +271,9 @@ Future<void> _createUploadRequest(MediaFileService media) async {
for (final groupMember in groupMembers) {
/// only send the upload to the users
if (media.mediaFile.reuploadRequestedBy != null) {
if (!media.mediaFile.reuploadRequestedBy!
.contains(groupMember.contactId)) {
if (!media.mediaFile.reuploadRequestedBy!.contains(
groupMember.contactId,
)) {
continue;
}
}
@ -304,8 +312,9 @@ Future<void> _createUploadRequest(MediaFileService media) async {
);
if (media.mediaFile.displayLimitInMilliseconds != null) {
notEncryptedContent.media.displayLimitInMilliseconds =
Int64(media.mediaFile.displayLimitInMilliseconds!);
notEncryptedContent.media.displayLimitInMilliseconds = Int64(
media.mediaFile.displayLimitInMilliseconds!,
);
}
final cipherText = await sendCipherText(
@ -345,6 +354,14 @@ Future<void> _createUploadRequest(MediaFileService media) async {
if (uploadRequestBytes.length > 49_000_000) {
await media.setUploadState(UploadState.fileLimitReached);
await twonlyDB.messagesDao.updateMessagesByMediaId(
media.mediaFile.mediaId,
MessagesCompanion(
openedAt: Value(DateTime.now()),
ackByServer: Value(DateTime.now()),
),
);
return;
}
@ -355,8 +372,9 @@ Mutex protectUpload = Mutex();
Future<void> _uploadUploadRequest(MediaFileService media) async {
await protectUpload.protect(() async {
final currentMedia =
await twonlyDB.mediaFilesDao.getMediaFileById(media.mediaFile.mediaId);
final currentMedia = await twonlyDB.mediaFilesDao.getMediaFileById(
media.mediaFile.mediaId,
);
if (currentMedia == null ||
currentMedia.uploadState == UploadState.backgroundUploadTaskStarted) {
@ -364,8 +382,9 @@ Future<void> _uploadUploadRequest(MediaFileService media) async {
return null;
}
final apiAuthTokenRaw = await const FlutterSecureStorage()
.read(key: SecureStorageKeys.apiAuthToken);
final apiAuthTokenRaw = await const FlutterSecureStorage().read(
key: SecureStorageKeys.apiAuthToken,
);
if (apiAuthTokenRaw == null) {
Log.error('api auth token not defined.');

View file

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

View file

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

View file

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

View file

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

View file

@ -52,8 +52,7 @@ class _AvatarIconState extends State<AvatarIcon> {
super.dispose();
}
// ignore: strict_top_level_inference
Widget errorBuilder(_, __, ___) {
Widget errorBuilder(_, _, _) {
return const SvgPicture(
AssetBytesLoader('assets/images/default_avatar.svg.vec'),
);
@ -81,20 +80,20 @@ class _AvatarIconState extends State<AvatarIcon> {
groupStream = twonlyDB.groupsDao
.watchGroupContact(widget.group!.groupId)
.listen((contacts) {
_avatarContacts = [];
if (contacts.length == 1) {
if (contacts.first.avatarSvgCompressed != null) {
_avatarContacts.add(contacts.first);
}
} else {
for (final contact in contacts) {
if (contact.avatarSvgCompressed != null) {
_avatarContacts.add(contact);
_avatarContacts = [];
if (contacts.length == 1) {
if (contacts.first.avatarSvgCompressed != null) {
_avatarContacts.add(contacts.first);
}
} else {
for (final contact in contacts) {
if (contact.avatarSvgCompressed != null) {
_avatarContacts.add(contact);
}
}
}
}
}
setState(() {});
});
setState(() {});
});
} else if (widget.myAvatar) {
_globalUserDataCallBackId = 'avatar_${getRandomString(10)}';
globalUserDataChangedCallBack[_globalUserDataCallBackId!] = () {
@ -113,11 +112,11 @@ class _AvatarIconState extends State<AvatarIcon> {
contactStream = twonlyDB.contactsDao
.watchContact(widget.contactId!)
.listen((contact) {
if (contact != null && contact.avatarSvgCompressed != null) {
_avatarContacts = [contact];
setState(() {});
}
});
if (contact != null && contact.avatarSvgCompressed != null) {
_avatarContacts = [contact];
setState(() {});
}
});
}
if (mounted) setState(() {});
}

View file

@ -74,7 +74,7 @@ class _ThreeRotatingDotsState extends State<ThreeRotatingDots>
height: size,
child: AnimatedBuilder(
animation: _animationController,
builder: (_, __) => Transform.translate(
builder: (_, _) => Transform.translate(
offset: Offset(0, size / 12),
child: Stack(
alignment: Alignment.center,
@ -110,7 +110,6 @@ class _ThreeRotatingDotsState extends State<ThreeRotatingDots>
),
/// Next 3 dots
_BuildDot.second(
controller: _animationController,
beginAngle: 0,
@ -217,9 +216,9 @@ class DrawDot extends StatelessWidget {
required double dotSize,
required this.color,
super.key,
}) : width = dotSize,
height = dotSize,
circular = true;
}) : width = dotSize,
height = dotSize,
circular = true;
const DrawDot.elliptical({
required this.width,

View file

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

View file

@ -6,7 +6,7 @@ publish_to: 'none'
version: 0.0.97+97
environment:
sdk: ^3.6.0
sdk: ^3.11.0
dependencies:
flutter:
@ -161,13 +161,7 @@ dependency_overrides:
flutter_android_volume_keydown:
git:
url: https://github.com/yenchieh/flutter_android_volume_keydown.git
branch: 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
ref: fix/lStar-not-found-error
dev_dependencies:
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_v8.dart' as v8;
import 'schema_v9.dart' as v9;
import 'schema_v10.dart' as v10;
class GeneratedHelper implements SchemaInstantiationHelper {
@override
@ -36,10 +37,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v8.DatabaseAtV8(db);
case 9:
return v9.DatabaseAtV9(db);
case 10:
return v10.DatabaseAtV10(db);
default:
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