fixing avatar icon

This commit is contained in:
otsmr 2025-11-03 11:22:22 +01:00
parent 562d9cbb74
commit 48de7faaa2
30 changed files with 263 additions and 95 deletions

View file

@ -215,10 +215,10 @@ func getPushNotificationText(pushNotification: PushNotification) -> (String, Str
if systemLanguage.contains("de") { // German
title = "Jemand"
pushNotificationText = [
.text: "hat eine Nachricht gesendet.",
.twonly: "hat ein twonly gesendet.",
.video: "hat ein Video gesendet.",
.image: "hat ein Bild gesendet.",
.text: "hat eine Nachricht{inGroup} gesendet.",
.twonly: "hat ein twonly{inGroup} gesendet.",
.video: "hat ein Video{inGroup} gesendet.",
.image: "hat ein Bild{inGroup} gesendet.",
.contactRequest: "möchte sich mit dir vernetzen.",
.acceptRequest: "ist jetzt mit dir vernetzt.",
.storedMediaFile: "hat dein Bild gespeichert.",
@ -228,15 +228,15 @@ func getPushNotificationText(pushNotification: PushNotification) -> (String, Str
.reactionToVideo: "hat mit {{content}} auf dein Video reagiert.",
.reactionToText: "hat mit {{content}} auf deinen Text reagiert.",
.reactionToImage: "hat mit {{content}} auf dein Bild reagiert.",
.response: "hat dir geantwortet.",
.response: "hat dir{inGroup} geantwortet.",
.addedToGroup: "hat dich zu \"{{content}}\" hinzugefügt."
]
} else { // Default to English
pushNotificationText = [
.text: "has sent you a message.",
.twonly: "has sent you a twonly.",
.video: "has sent you a video.",
.image: "has sent you an image.",
.text: "sent a message{inGroup}.",
.twonly: "sent a twonly{inGroup}.",
.video: "sent a video{inGroup}.",
.image: "sent a image{inGroup}.",
.contactRequest: "wants to connect with you.",
.acceptRequest: "is now connected with you.",
.storedMediaFile: "has stored your image.",
@ -246,7 +246,7 @@ func getPushNotificationText(pushNotification: PushNotification) -> (String, Str
.reactionToVideo: "has reacted with {{content}} to your video.",
.reactionToText: "has reacted with {{content}} to your text.",
.reactionToImage: "has reacted with {{content}} to your image.",
.response: "has responded.",
.response: "has responded{inGroup}.",
.addedToGroup: "has added you to \"{{content}}\""
]
}
@ -255,6 +255,8 @@ func getPushNotificationText(pushNotification: PushNotification) -> (String, Str
if pushNotification.hasAdditionalContent {
content.replace("{{content}}", with: pushNotification.additionalContent)
content.replace("{inGroup}", with: " in {inGroup}")
content.replace("{inGroup}", with: pushNotification.additionalContent)
}
// Return the corresponding message or an empty string if not found

View file

@ -1,3 +1,5 @@
import 'dart:ui';
import 'package:camera/camera.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/userdata.dart';
@ -26,4 +28,6 @@ void Function() globalCallbackAppIsOutdated = () {};
void Function() globalCallbackNewDeviceRegistered = () {};
void Function(String planId) globalCallbackUpdatePlan = (String planId) {};
Map<String, VoidCallback> globalUserDataChangedCallBack = {};
bool globalIsAppInBackground = true;

View file

@ -20,6 +20,7 @@ import 'package:twonly/src/services/api/mediafiles/media_background.service.dart
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
import 'package:twonly/src/services/fcm.service.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
import 'package:twonly/src/services/notifications/setup.notifications.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/storage.dart';
@ -59,6 +60,7 @@ void main() async {
unawaited(finishStartedPreprocessing());
unawaited(MediaFileService.purgeTempFolder());
unawaited(createPushAvatars());
await twonlyDB.messagesDao.purgeMessageTable();
// await twonlyDB.messagesDao.resetPendingDownloadState();

View file

@ -129,8 +129,10 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
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();
}
@ -140,8 +142,10 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
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();
}

View file

@ -381,6 +381,16 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
),
);
if (message.senderId.present) {
await twonlyDB.groupsDao.updateMember(
message.groupId.value,
message.senderId.value!,
GroupMembersCompanion(
lastMessage: Value(DateTime.now()),
),
);
}
return await (select(messages)..where((t) => t.rowId.equals(rowId)))
.getSingle();
} catch (e) {

View file

@ -60,6 +60,7 @@ class GroupMembers extends Table {
TextColumn get memberState => textEnum<MemberState>().nullable()();
BlobColumn get groupPublicKey => blob().nullable()();
DateTimeColumn get lastMessage => dateTime().nullable()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override

View file

@ -3987,6 +3987,12 @@ class $GroupMembersTable extends GroupMembers
late final GeneratedColumn<Uint8List> groupPublicKey =
GeneratedColumn<Uint8List>('group_public_key', aliasedName, true,
type: DriftSqlType.blob, requiredDuringInsert: false);
static const VerificationMeta _lastMessageMeta =
const VerificationMeta('lastMessage');
@override
late final GeneratedColumn<DateTime> lastMessage = GeneratedColumn<DateTime>(
'last_message', aliasedName, true,
type: DriftSqlType.dateTime, requiredDuringInsert: false);
static const VerificationMeta _createdAtMeta =
const VerificationMeta('createdAt');
@override
@ -3997,7 +4003,7 @@ class $GroupMembersTable extends GroupMembers
defaultValue: currentDateAndTime);
@override
List<GeneratedColumn> get $columns =>
[groupId, contactId, memberState, groupPublicKey, createdAt];
[groupId, contactId, memberState, groupPublicKey, lastMessage, createdAt];
@override
String get aliasedName => _alias ?? actualTableName;
@override
@ -4026,6 +4032,12 @@ class $GroupMembersTable extends GroupMembers
groupPublicKey.isAcceptableOrUnknown(
data['group_public_key']!, _groupPublicKeyMeta));
}
if (data.containsKey('last_message')) {
context.handle(
_lastMessageMeta,
lastMessage.isAcceptableOrUnknown(
data['last_message']!, _lastMessageMeta));
}
if (data.containsKey('created_at')) {
context.handle(_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
@ -4048,6 +4060,8 @@ class $GroupMembersTable extends GroupMembers
DriftSqlType.string, data['${effectivePrefix}member_state'])),
groupPublicKey: attachedDatabase.typeMapping
.read(DriftSqlType.blob, data['${effectivePrefix}group_public_key']),
lastMessage: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}last_message']),
createdAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
);
@ -4070,12 +4084,14 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
final int contactId;
final MemberState? memberState;
final Uint8List? groupPublicKey;
final DateTime? lastMessage;
final DateTime createdAt;
const GroupMember(
{required this.groupId,
required this.contactId,
this.memberState,
this.groupPublicKey,
this.lastMessage,
required this.createdAt});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
@ -4089,6 +4105,9 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
if (!nullToAbsent || groupPublicKey != null) {
map['group_public_key'] = Variable<Uint8List>(groupPublicKey);
}
if (!nullToAbsent || lastMessage != null) {
map['last_message'] = Variable<DateTime>(lastMessage);
}
map['created_at'] = Variable<DateTime>(createdAt);
return map;
}
@ -4103,6 +4122,9 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
groupPublicKey: groupPublicKey == null && nullToAbsent
? const Value.absent()
: Value(groupPublicKey),
lastMessage: lastMessage == null && nullToAbsent
? const Value.absent()
: Value(lastMessage),
createdAt: Value(createdAt),
);
}
@ -4116,6 +4138,7 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
memberState: $GroupMembersTable.$convertermemberStaten
.fromJson(serializer.fromJson<String?>(json['memberState'])),
groupPublicKey: serializer.fromJson<Uint8List?>(json['groupPublicKey']),
lastMessage: serializer.fromJson<DateTime?>(json['lastMessage']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
);
}
@ -4128,6 +4151,7 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
'memberState': serializer.toJson<String?>(
$GroupMembersTable.$convertermemberStaten.toJson(memberState)),
'groupPublicKey': serializer.toJson<Uint8List?>(groupPublicKey),
'lastMessage': serializer.toJson<DateTime?>(lastMessage),
'createdAt': serializer.toJson<DateTime>(createdAt),
};
}
@ -4137,6 +4161,7 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
int? contactId,
Value<MemberState?> memberState = const Value.absent(),
Value<Uint8List?> groupPublicKey = const Value.absent(),
Value<DateTime?> lastMessage = const Value.absent(),
DateTime? createdAt}) =>
GroupMember(
groupId: groupId ?? this.groupId,
@ -4144,6 +4169,7 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
memberState: memberState.present ? memberState.value : this.memberState,
groupPublicKey:
groupPublicKey.present ? groupPublicKey.value : this.groupPublicKey,
lastMessage: lastMessage.present ? lastMessage.value : this.lastMessage,
createdAt: createdAt ?? this.createdAt,
);
GroupMember copyWithCompanion(GroupMembersCompanion data) {
@ -4155,6 +4181,8 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
groupPublicKey: data.groupPublicKey.present
? data.groupPublicKey.value
: this.groupPublicKey,
lastMessage:
data.lastMessage.present ? data.lastMessage.value : this.lastMessage,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
);
}
@ -4166,6 +4194,7 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
..write('contactId: $contactId, ')
..write('memberState: $memberState, ')
..write('groupPublicKey: $groupPublicKey, ')
..write('lastMessage: $lastMessage, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
@ -4173,7 +4202,7 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
@override
int get hashCode => Object.hash(groupId, contactId, memberState,
$driftBlobEquality.hash(groupPublicKey), createdAt);
$driftBlobEquality.hash(groupPublicKey), lastMessage, createdAt);
@override
bool operator ==(Object other) =>
identical(this, other) ||
@ -4183,6 +4212,7 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
other.memberState == this.memberState &&
$driftBlobEquality.equals(
other.groupPublicKey, this.groupPublicKey) &&
other.lastMessage == this.lastMessage &&
other.createdAt == this.createdAt);
}
@ -4191,6 +4221,7 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
final Value<int> contactId;
final Value<MemberState?> memberState;
final Value<Uint8List?> groupPublicKey;
final Value<DateTime?> lastMessage;
final Value<DateTime> createdAt;
final Value<int> rowid;
const GroupMembersCompanion({
@ -4198,6 +4229,7 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
this.contactId = const Value.absent(),
this.memberState = const Value.absent(),
this.groupPublicKey = const Value.absent(),
this.lastMessage = const Value.absent(),
this.createdAt = const Value.absent(),
this.rowid = const Value.absent(),
});
@ -4206,6 +4238,7 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
required int contactId,
this.memberState = const Value.absent(),
this.groupPublicKey = const Value.absent(),
this.lastMessage = const Value.absent(),
this.createdAt = const Value.absent(),
this.rowid = const Value.absent(),
}) : groupId = Value(groupId),
@ -4215,6 +4248,7 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
Expression<int>? contactId,
Expression<String>? memberState,
Expression<Uint8List>? groupPublicKey,
Expression<DateTime>? lastMessage,
Expression<DateTime>? createdAt,
Expression<int>? rowid,
}) {
@ -4223,6 +4257,7 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
if (contactId != null) 'contact_id': contactId,
if (memberState != null) 'member_state': memberState,
if (groupPublicKey != null) 'group_public_key': groupPublicKey,
if (lastMessage != null) 'last_message': lastMessage,
if (createdAt != null) 'created_at': createdAt,
if (rowid != null) 'rowid': rowid,
});
@ -4233,6 +4268,7 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
Value<int>? contactId,
Value<MemberState?>? memberState,
Value<Uint8List?>? groupPublicKey,
Value<DateTime?>? lastMessage,
Value<DateTime>? createdAt,
Value<int>? rowid}) {
return GroupMembersCompanion(
@ -4240,6 +4276,7 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
contactId: contactId ?? this.contactId,
memberState: memberState ?? this.memberState,
groupPublicKey: groupPublicKey ?? this.groupPublicKey,
lastMessage: lastMessage ?? this.lastMessage,
createdAt: createdAt ?? this.createdAt,
rowid: rowid ?? this.rowid,
);
@ -4261,6 +4298,9 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
if (groupPublicKey.present) {
map['group_public_key'] = Variable<Uint8List>(groupPublicKey.value);
}
if (lastMessage.present) {
map['last_message'] = Variable<DateTime>(lastMessage.value);
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
@ -4277,6 +4317,7 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
..write('contactId: $contactId, ')
..write('memberState: $memberState, ')
..write('groupPublicKey: $groupPublicKey, ')
..write('lastMessage: $lastMessage, ')
..write('createdAt: $createdAt, ')
..write('rowid: $rowid')
..write(')'))
@ -10870,6 +10911,7 @@ typedef $$GroupMembersTableCreateCompanionBuilder = GroupMembersCompanion
required int contactId,
Value<MemberState?> memberState,
Value<Uint8List?> groupPublicKey,
Value<DateTime?> lastMessage,
Value<DateTime> createdAt,
Value<int> rowid,
});
@ -10879,6 +10921,7 @@ typedef $$GroupMembersTableUpdateCompanionBuilder = GroupMembersCompanion
Value<int> contactId,
Value<MemberState?> memberState,
Value<Uint8List?> groupPublicKey,
Value<DateTime?> lastMessage,
Value<DateTime> createdAt,
Value<int> rowid,
});
@ -10935,6 +10978,9 @@ class $$GroupMembersTableFilterComposer
column: $table.groupPublicKey,
builder: (column) => ColumnFilters(column));
ColumnFilters<DateTime> get lastMessage => $composableBuilder(
column: $table.lastMessage, builder: (column) => ColumnFilters(column));
ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnFilters(column));
@ -10995,6 +11041,9 @@ class $$GroupMembersTableOrderingComposer
column: $table.groupPublicKey,
builder: (column) => ColumnOrderings(column));
ColumnOrderings<DateTime> get lastMessage => $composableBuilder(
column: $table.lastMessage, builder: (column) => ColumnOrderings(column));
ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnOrderings(column));
@ -11055,6 +11104,9 @@ class $$GroupMembersTableAnnotationComposer
GeneratedColumn<Uint8List> get groupPublicKey => $composableBuilder(
column: $table.groupPublicKey, builder: (column) => column);
GeneratedColumn<DateTime> get lastMessage => $composableBuilder(
column: $table.lastMessage, builder: (column) => column);
GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
@ -11126,6 +11178,7 @@ class $$GroupMembersTableTableManager extends RootTableManager<
Value<int> contactId = const Value.absent(),
Value<MemberState?> memberState = const Value.absent(),
Value<Uint8List?> groupPublicKey = const Value.absent(),
Value<DateTime?> lastMessage = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) =>
@ -11134,6 +11187,7 @@ class $$GroupMembersTableTableManager extends RootTableManager<
contactId: contactId,
memberState: memberState,
groupPublicKey: groupPublicKey,
lastMessage: lastMessage,
createdAt: createdAt,
rowid: rowid,
),
@ -11142,6 +11196,7 @@ class $$GroupMembersTableTableManager extends RootTableManager<
required int contactId,
Value<MemberState?> memberState = const Value.absent(),
Value<Uint8List?> groupPublicKey = const Value.absent(),
Value<DateTime?> lastMessage = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) =>
@ -11150,6 +11205,7 @@ class $$GroupMembersTableTableManager extends RootTableManager<
contactId: contactId,
memberState: memberState,
groupPublicKey: groupPublicKey,
lastMessage: lastMessage,
createdAt: createdAt,
rowid: rowid,
),

View file

@ -774,10 +774,11 @@
"@twonlySafeRecoverDesc": {},
"twonlySafeRecoverBtn": "Backup wiederherstellen",
"@twonlySafeRecoverBtn": {},
"notificationText": "hat eine Nachricht gesendet.",
"notificationTwonly": "hat ein twonly gesendet.",
"notificationVideo": "hat ein Video gesendet.",
"notificationImage": "hat ein Bild gesendet.",
"notificationFillerIn": "in",
"notificationText": "hat eine Nachricht{inGroup} gesendet.",
"notificationTwonly": "hat ein twonly{inGroup} gesendet.",
"notificationVideo": "hat ein Video{inGroup} gesendet.",
"notificationImage": "hat ein Bild{inGroup} gesendet.",
"notificationAddedToGroup": "hat dich zu \"{groupname}\" hinzugefügt.",
"notificationContactRequest": "möchte sich mit dir vernetzen.",
"notificationAcceptRequest": "ist jetzt mit dir vernetzt.",
@ -787,7 +788,7 @@
"notificationReactionToVideo": "hat mit {reaction} auf dein Video reagiert.",
"notificationReactionToText": "hat mit {reaction} auf deine Nachricht reagiert.",
"notificationReactionToImage": "hat mit {reaction} auf dein Bild reagiert.",
"notificationResponse": "hat dir geantwortet.",
"notificationResponse": "hat dir{inGroup} geantwortet.",
"notificationTitleUnknownUser": "Jemand",
"notificationCategoryMessageTitle": "Nachrichten",
"notificationCategoryMessageDesc": "Nachrichten von anderen Benutzern."

View file

@ -553,10 +553,11 @@
"makerLeftGroup": "{maker} has left the group.",
"groupActionYou": "you",
"groupActionYour": "your",
"notificationText": "sent a message.",
"notificationTwonly": "sent a twonly.",
"notificationVideo": "sent a video.",
"notificationImage": "sent a image.",
"notificationFillerIn": "in",
"notificationText": "sent a message{inGroup}.",
"notificationTwonly": "sent a twonly{inGroup}.",
"notificationVideo": "sent a video{inGroup}.",
"notificationImage": "sent a image{inGroup}.",
"notificationAddedToGroup": "has added you to \"{groupname}\"",
"notificationContactRequest": "wants to connect with you.",
"notificationAcceptRequest": "is now connected with you.",
@ -566,7 +567,7 @@
"notificationReactionToVideo": "has reacted with {reaction} to your video.",
"notificationReactionToText": "has reacted with {reaction} to your message.",
"notificationReactionToImage": "has reacted with {reaction} to your image.",
"notificationResponse": "has responded.",
"notificationResponse": "has responded{inGroup}.",
"notificationTitleUnknownUser": "Someone",
"notificationCategoryMessageTitle": "Messages",
"notificationCategoryMessageDesc": "Messages from other users."

View file

@ -2420,29 +2420,35 @@ abstract class AppLocalizations {
/// **'your'**
String get groupActionYour;
/// No description provided for @notificationFillerIn.
///
/// In en, this message translates to:
/// **'in'**
String get notificationFillerIn;
/// No description provided for @notificationText.
///
/// In en, this message translates to:
/// **'sent a message.'**
String get notificationText;
/// **'sent a message{inGroup}.'**
String notificationText(Object inGroup);
/// No description provided for @notificationTwonly.
///
/// In en, this message translates to:
/// **'sent a twonly.'**
String get notificationTwonly;
/// **'sent a twonly{inGroup}.'**
String notificationTwonly(Object inGroup);
/// No description provided for @notificationVideo.
///
/// In en, this message translates to:
/// **'sent a video.'**
String get notificationVideo;
/// **'sent a video{inGroup}.'**
String notificationVideo(Object inGroup);
/// No description provided for @notificationImage.
///
/// In en, this message translates to:
/// **'sent a image.'**
String get notificationImage;
/// **'sent a image{inGroup}.'**
String notificationImage(Object inGroup);
/// No description provided for @notificationAddedToGroup.
///
@ -2501,8 +2507,8 @@ abstract class AppLocalizations {
/// No description provided for @notificationResponse.
///
/// In en, this message translates to:
/// **'has responded.'**
String get notificationResponse;
/// **'has responded{inGroup}.'**
String notificationResponse(Object inGroup);
/// No description provided for @notificationTitleUnknownUser.
///

View file

@ -1310,16 +1310,27 @@ class AppLocalizationsDe extends AppLocalizations {
String get groupActionYour => 'deine';
@override
String get notificationText => 'hat eine Nachricht gesendet.';
String get notificationFillerIn => 'in';
@override
String get notificationTwonly => 'hat ein twonly gesendet.';
String notificationText(Object inGroup) {
return 'hat eine Nachricht$inGroup gesendet.';
}
@override
String get notificationVideo => 'hat ein Video gesendet.';
String notificationTwonly(Object inGroup) {
return 'hat ein twonly$inGroup gesendet.';
}
@override
String get notificationImage => 'hat ein Bild gesendet.';
String notificationVideo(Object inGroup) {
return 'hat ein Video$inGroup gesendet.';
}
@override
String notificationImage(Object inGroup) {
return 'hat ein Bild$inGroup gesendet.';
}
@override
String notificationAddedToGroup(Object groupname) {
@ -1357,7 +1368,9 @@ class AppLocalizationsDe extends AppLocalizations {
}
@override
String get notificationResponse => 'hat dir geantwortet.';
String notificationResponse(Object inGroup) {
return 'hat dir$inGroup geantwortet.';
}
@override
String get notificationTitleUnknownUser => 'Jemand';

View file

@ -1303,16 +1303,27 @@ class AppLocalizationsEn extends AppLocalizations {
String get groupActionYour => 'your';
@override
String get notificationText => 'sent a message.';
String get notificationFillerIn => 'in';
@override
String get notificationTwonly => 'sent a twonly.';
String notificationText(Object inGroup) {
return 'sent a message$inGroup.';
}
@override
String get notificationVideo => 'sent a video.';
String notificationTwonly(Object inGroup) {
return 'sent a twonly$inGroup.';
}
@override
String get notificationImage => 'sent a image.';
String notificationVideo(Object inGroup) {
return 'sent a video$inGroup.';
}
@override
String notificationImage(Object inGroup) {
return 'sent a image$inGroup.';
}
@override
String notificationAddedToGroup(Object groupname) {
@ -1350,7 +1361,9 @@ class AppLocalizationsEn extends AppLocalizations {
}
@override
String get notificationResponse => 'has responded.';
String notificationResponse(Object inGroup) {
return 'has responded$inGroup.';
}
@override
String get notificationTitleUnknownUser => 'Someone';

View file

@ -237,11 +237,18 @@ AppLocalizations getLocalizations() {
String getPushNotificationText(PushNotification pushNotification) {
final lang = getLocalizations();
var inGroup = '';
if (pushNotification.hasAdditionalContent()) {
inGroup =
' ${lang.notificationFillerIn} ${pushNotification.additionalContent}';
}
final pushNotificationText = {
PushKind.text.name: lang.notificationText,
PushKind.twonly.name: lang.notificationTwonly,
PushKind.video.name: lang.notificationVideo,
PushKind.image.name: lang.notificationImage,
PushKind.text.name: lang.notificationText(inGroup),
PushKind.twonly.name: lang.notificationTwonly(inGroup),
PushKind.video.name: lang.notificationVideo(inGroup),
PushKind.image.name: lang.notificationImage(inGroup),
PushKind.contactRequest.name: lang.notificationContactRequest,
PushKind.acceptRequest.name: lang.notificationAcceptRequest,
PushKind.storedMediaFile.name: lang.notificationStoredMediaFile,
@ -253,7 +260,7 @@ String getPushNotificationText(PushNotification pushNotification) {
lang.notificationReactionToText(pushNotification.additionalContent),
PushKind.reactionToImage.name:
lang.notificationReactionToImage(pushNotification.additionalContent),
PushKind.response.name: lang.notificationResponse,
PushKind.response.name: lang.notificationResponse(inGroup),
PushKind.addedToGroup.name:
lang.notificationAddedToGroup(pushNotification.additionalContent),
};

View file

@ -234,6 +234,10 @@ Future<PushNotification?> getPushNotificationFromEncryptedContent(
if (content.textMessage.hasQuoteMessageId()) {
kind = PushKind.response;
}
final group = await twonlyDB.groupsDao.getGroup(content.groupId);
if (group != null && !group.isDirectChat) {
additionalContent = group.groupName;
}
}
if (content.hasMedia()) {
switch (content.media.type) {
@ -248,6 +252,10 @@ Future<PushNotification?> getPushNotificationFromEncryptedContent(
if (content.media.requiresAuthentication) {
kind = PushKind.twonly;
}
final group = await twonlyDB.groupsDao.getGroup(content.groupId);
if (group != null && !group.isDirectChat) {
additionalContent = group.groupName;
}
}
if (content.hasContactRequest()) {

View file

@ -62,7 +62,7 @@ Future<void> createPushAvatars() async {
final contacts = await twonlyDB.contactsDao.getAllNotBlockedContacts();
for (final contact in contacts) {
if (contact.avatarSvgCompressed == null) return;
if (contact.avatarSvgCompressed == null) continue;
final avatarSvg = getAvatarSvg(contact.avatarSvgCompressed!);

View file

@ -67,6 +67,16 @@ Uint8List getRandomUint8List(int length) {
return randomBytes;
}
const _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
Random _rnd = Random();
String getRandomString(int length) => String.fromCharCodes(
Iterable.generate(
length,
(_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length)),
),
);
String errorCodeToText(BuildContext context, ErrorCode code) {
// ignore: exhaustive_cases
switch (code) {

View file

@ -52,7 +52,7 @@ Mutex updateProtection = Mutex();
Future<UserData?> updateUserdata(
UserData Function(UserData userData) updateUser,
) async {
return updateProtection.protect<UserData?>(() async {
final userData = await updateProtection.protect<UserData?>(() async {
final user = await getUser();
if (user == null) return null;
final updated = updateUser(user);
@ -61,6 +61,14 @@ Future<UserData?> updateUserdata(
gUser = updated;
return updated;
});
try {
for (final callBack in globalUserDataChangedCallBack.values) {
callBack();
}
} catch (e) {
Log.error(e);
}
return userData;
}
Future<bool> deleteLocalUserData() async {

View file

@ -279,7 +279,7 @@ class ContactsListView extends StatelessWidget {
final contact = contacts[index];
return ListTile(
title: Text(substringBy(contact.username, 25)),
leading: AvatarIcon(contact: contact),
leading: AvatarIcon(contactId: contact.userId),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: contact.requested

View file

@ -123,7 +123,7 @@ class _ChatListViewState extends State<ChatListView> {
setState(() {}); // gUser has updated
},
child: AvatarIcon(
userData: gUser,
myAvatar: true,
fontSize: 14,
color: context.color.onSurface.withAlpha(20),
),

View file

@ -386,7 +386,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
children: messages[i].lastOpenedPosition!.map((w) {
return AvatarIcon(
key: GlobalKey(),
contact: w,
contactId: w.userId,
fontSize: 12,
);
}).toList(),

View file

@ -116,8 +116,8 @@ class _AllReactionsViewState extends State<AllReactionsView> {
child: Row(
children: [
AvatarIcon(
contact: entry.$2,
userData: (entry.$2 == null) ? gUser : null,
contactId: entry.$2?.userId,
myAvatar: entry.$2 == null,
fontSize: 15,
),
const SizedBox(width: 6),

View file

@ -127,7 +127,7 @@ class _MessageInfoViewState extends State<MessageInfoView> {
child: Row(
children: [
AvatarIcon(
contact: groupMember.$2,
contactId: groupMember.$2.userId,
fontSize: 15,
),
const SizedBox(width: 6),

View file

@ -175,7 +175,7 @@ class UserList extends StatelessWidget {
],
),
leading: AvatarIcon(
contact: user,
contactId: user.userId,
fontSize: 13,
),
onTap: () async {

View file

@ -1,24 +1,22 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/json/userdata.dart';
import 'package:twonly/src/utils/misc.dart';
class AvatarIcon extends StatefulWidget {
const AvatarIcon({
super.key,
this.group,
this.contact,
this.contactId,
this.userData,
this.myAvatar = false,
this.fontSize = 20,
this.color,
});
final Group? group;
final Contact? contact;
final int? contactId;
final UserData? userData;
final bool myAvatar;
final double? fontSize;
final Color? color;
@ -27,7 +25,12 @@ class AvatarIcon extends StatefulWidget {
}
class _AvatarIconState extends State<AvatarIcon> {
final List<String> _avatarSVGs = [];
List<String> _avatarSVGs = [];
String? _globalUserDataCallBackId;
StreamSubscription<List<Contact>>? groupStream;
StreamSubscription<List<Contact>>? contactsStream;
StreamSubscription<Contact?>? contactStream;
@override
void initState() {
@ -35,32 +38,53 @@ class _AvatarIconState extends State<AvatarIcon> {
super.initState();
}
@override
void dispose() {
groupStream?.cancel();
contactStream?.cancel();
contactsStream?.cancel();
if (_globalUserDataCallBackId != null) {
globalUserDataChangedCallBack.remove(_globalUserDataCallBackId);
}
super.dispose();
}
Future<void> initAsync() async {
if (widget.group != null) {
final contacts =
await twonlyDB.groupsDao.getGroupContact(widget.group!.groupId);
if (contacts.length == 1) {
if (contacts.first.avatarSvgCompressed != null) {
_avatarSVGs.add(getAvatarSvg(contacts.first.avatarSvgCompressed!));
}
} else {
for (final contact in contacts) {
if (contact.avatarSvgCompressed != null) {
_avatarSVGs.add(getAvatarSvg(contact.avatarSvgCompressed!));
groupStream = twonlyDB.groupsDao
.watchGroupContact(widget.group!.groupId)
.listen((contacts) {
_avatarSVGs = [];
if (contacts.length == 1) {
if (contacts.first.avatarSvgCompressed != null) {
_avatarSVGs.add(getAvatarSvg(contacts.first.avatarSvgCompressed!));
}
} else {
for (final contact in contacts) {
if (contact.avatarSvgCompressed != null) {
_avatarSVGs.add(getAvatarSvg(contact.avatarSvgCompressed!));
}
}
}
}
// avatarSvg = group!.avatarSvg;
} else if (widget.userData?.avatarSvg != null) {
_avatarSVGs.add(widget.userData!.avatarSvg!);
} else if (widget.contact?.avatarSvgCompressed != null) {
_avatarSVGs.add(getAvatarSvg(widget.contact!.avatarSvgCompressed!));
setState(() {});
});
} else if (widget.myAvatar) {
_globalUserDataCallBackId = 'avatar_${getRandomString(10)}';
globalUserDataChangedCallBack[_globalUserDataCallBackId!] = () {
setState(() {
_avatarSVGs = [gUser.avatarSvg!];
});
};
_avatarSVGs.add(gUser.avatarSvg!);
} else if (widget.contactId != null) {
final contact =
await twonlyDB.contactsDao.getContactById(widget.contactId!);
if (contact != null && contact.avatarSvgCompressed != null) {
_avatarSVGs.add(getAvatarSvg(contact.avatarSvgCompressed!));
}
contactStream = twonlyDB.contactsDao
.watchContact(widget.contactId!)
.listen((contact) {
if (contact != null && contact.avatarSvgCompressed != null) {
_avatarSVGs = [getAvatarSvg(contact.avatarSvgCompressed!)];
setState(() {});
}
});
}
if (mounted) setState(() {});
}

View file

@ -115,7 +115,7 @@ class _ContactViewState extends State<ContactView> {
children: [
Padding(
padding: const EdgeInsets.all(10),
child: AvatarIcon(contact: contact, fontSize: 30),
child: AvatarIcon(contactId: contact.userId, fontSize: 30),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,

View file

@ -153,9 +153,8 @@ class _GroupViewState extends State<GroupView> {
),
BetterListTile(
padding: const EdgeInsets.only(left: 13),
leading: AvatarIcon(
key: GlobalKey(),
userData: gUser,
leading: const AvatarIcon(
myAvatar: true,
fontSize: 16,
),
text: context.lang.you,
@ -177,8 +176,7 @@ class _GroupViewState extends State<GroupView> {
child: BetterListTile(
padding: const EdgeInsets.only(left: 13),
leading: AvatarIcon(
key: GlobalKey(),
contact: member.$1,
contactId: member.$1.userId,
fontSize: 16,
),
text: getContactDisplayName(member.$1, maxLength: 25),

View file

@ -114,7 +114,7 @@ class _GroupCreateSelectGroupNameViewState
],
),
leading: AvatarIcon(
contact: user,
contactId: user.userId,
fontSize: 13,
),
),

View file

@ -204,7 +204,7 @@ class _StartNewChatView extends State<GroupCreateSelectMembersView> {
? Text(context.lang.alreadyInGroup)
: null,
leading: AvatarIcon(
contact: user,
contactId: user.userId,
fontSize: 13,
),
trailing: Checkbox(
@ -256,7 +256,7 @@ class _Chip extends StatelessWidget {
child: Chip(
key: GlobalKey(),
avatar: AvatarIcon(
contact: contact,
contactId: contact.userId,
fontSize: 10,
),
label: Row(

View file

@ -112,7 +112,7 @@ class UserList extends StatelessWidget {
Text(getContactDisplayName(user)),
],
),
leading: AvatarIcon(contact: user, fontSize: 15),
leading: AvatarIcon(contactId: user.userId, fontSize: 15),
trailing: Checkbox(
value: user.blocked,
onChanged: (bool? value) async {

View file

@ -55,8 +55,8 @@ class _SettingsMainViewState extends State<SettingsMainView> {
color: context.color.surface.withAlpha(0),
child: Row(
children: [
AvatarIcon(
userData: gUser,
const AvatarIcon(
myAvatar: true,
fontSize: 30,
),
Container(width: 20, color: Colors.transparent),