add push notifications for groups and add sender name

This commit is contained in:
otsmr 2025-11-03 00:50:09 +01:00
parent 9356a1fc70
commit c786bb55f9
18 changed files with 514 additions and 282 deletions

View file

@ -84,7 +84,7 @@ func getPushNotificationData(pushData: String) -> (
pushUser = tryPushUser pushUser = tryPushUser
if isUUIDNewer(pushUser!.lastMessageID, pushNotification!.messageID) if isUUIDNewer(pushUser!.lastMessageID, pushNotification!.messageID)
{ {
return ("blocked", "blocked", 0) //return ("blocked", "blocked", 0)
} }
break break
} }
@ -109,11 +109,12 @@ func getPushNotificationData(pushData: String) -> (
} else if pushUser != nil { } else if pushUser != nil {
return ( return (
pushUser!.displayName, pushUser!.displayName,
getPushNotificationText(pushNotification: pushNotification), pushUser!.userID getPushNotificationText(pushNotification: pushNotification).0, pushUser!.userID
) )
} else { } else {
let content = getPushNotificationText(pushNotification: pushNotification)
return ( return (
"", getPushNotificationTextWithoutUserId(pushKind: pushNotification.kind), 1 content.1, content.0, 1
) )
} }
@ -204,28 +205,31 @@ func readFromKeychain(key: String) -> String? {
return nil return nil
} }
func getPushNotificationText(pushNotification: PushNotification) -> String { func getPushNotificationText(pushNotification: PushNotification) -> (String, String) {
let systemLanguage = Locale.current.language.languageCode?.identifier ?? "en" // Get the current system language let systemLanguage = Locale.current.language.languageCode?.identifier ?? "en" // Get the current system language
var pushNotificationText: [PushKind: String] = [:] var pushNotificationText: [PushKind: String] = [:]
var title = "Someone"
// Define the messages based on the system language // Define the messages based on the system language
if systemLanguage.contains("de") { // German if systemLanguage.contains("de") { // German
title = "Jemand"
pushNotificationText = [ pushNotificationText = [
.text: "hat dir eine Nachricht gesendet.", .text: "hat eine Nachricht gesendet.",
.twonly: "hat dir ein twonly gesendet.", .twonly: "hat ein twonly gesendet.",
.video: "hat dir ein Video gesendet.", .video: "hat ein Video gesendet.",
.image: "hat dir ein Bild gesendet.", .image: "hat ein Bild gesendet.",
.contactRequest: "möchte sich mit dir vernetzen.", .contactRequest: "möchte sich mit dir vernetzen.",
.acceptRequest: "ist jetzt mit dir vernetzt.", .acceptRequest: "ist jetzt mit dir vernetzt.",
.storedMediaFile: "hat dein Bild gespeichert.", .storedMediaFile: "hat dein Bild gespeichert.",
.reaction: "hat auf dein Bild reagiert.", .reaction: "hat auf dein Bild reagiert.",
.testNotification: "Das ist eine Testbenachrichtigung.", .testNotification: "Das ist eine Testbenachrichtigung.",
.reopenedMedia: "hat dein Bild erneut geöffnet.", .reopenedMedia: "hat dein Bild erneut geöffnet.",
.reactionToVideo: "hat mit {{reaction}} auf dein Video reagiert.", .reactionToVideo: "hat mit {{content}} auf dein Video reagiert.",
.reactionToText: "hat mit {{reaction}} auf deinen Text reagiert.", .reactionToText: "hat mit {{content}} auf deinen Text reagiert.",
.reactionToImage: "hat mit {{reaction}} auf dein Bild reagiert.", .reactionToImage: "hat mit {{content}} auf dein Bild reagiert.",
.response: "hat dir geantwortet.", .response: "hat dir geantwortet.",
.addedToGroup: "hat dich zu \"{{content}}\" hinzugefügt."
] ]
} else { // Default to English } else { // Default to English
pushNotificationText = [ pushNotificationText = [
@ -239,65 +243,21 @@ func getPushNotificationText(pushNotification: PushNotification) -> String {
.reaction: "has reacted to your image.", .reaction: "has reacted to your image.",
.testNotification: "This is a test notification.", .testNotification: "This is a test notification.",
.reopenedMedia: "has reopened your image.", .reopenedMedia: "has reopened your image.",
.reactionToVideo: "has reacted with {{reaction}} to your video.", .reactionToVideo: "has reacted with {{content}} to your video.",
.reactionToText: "has reacted with {{reaction}} to your text.", .reactionToText: "has reacted with {{content}} to your text.",
.reactionToImage: "has reacted with {{reaction}} to your image.", .reactionToImage: "has reacted with {{content}} to your image.",
.response: "has responded.", .response: "has responded.",
.addedToGroup: "has added you to \"{{content}}\""
] ]
} }
var content = pushNotificationText[pushNotification.kind] ?? "" var content = pushNotificationText[pushNotification.kind] ?? ""
if pushNotification.hasReactionContent { if pushNotification.hasAdditionalContent {
content.replace("{{reaction}}", with: pushNotification.reactionContent) content.replace("{{content}}", with: pushNotification.additionalContent)
} }
// Return the corresponding message or an empty string if not found // Return the corresponding message or an empty string if not found
return content return (content, title)
} }
func getPushNotificationTextWithoutUserId(pushKind: PushKind) -> String {
let systemLanguage = Locale.current.language.languageCode?.identifier ?? "en" // Get the current system language
var pushNotificationText: [PushKind: String] = [:]
// Define the messages based on the system language
if systemLanguage.contains("de") { // German
pushNotificationText = [
.text: "Du hast eine Nachricht erhalten.",
.twonly: "Du hast ein twonly erhalten.",
.video: "Du hast ein Video erhalten.",
.image: "Du hast ein Bild erhalten.",
.contactRequest: "Du hast eine Kontaktanfrage erhalten.",
.acceptRequest: "Deine Kontaktanfrage wurde angenommen.",
.storedMediaFile: "Dein Bild wurde gespeichert.",
.reaction: "Du hast eine Reaktion auf dein Bild erhalten.",
.testNotification: "Das ist eine Testbenachrichtigung.",
.reopenedMedia: "hat dein Bild erneut geöffnet.",
.reactionToVideo: "Du hast eine Reaktion auf dein Video erhalten.",
.reactionToText: "Du hast eine Reaktion auf deinen Text erhalten.",
.reactionToImage: "Du hast eine Reaktion auf dein Bild erhalten.",
.response: "Du hast eine Antwort erhalten.",
]
} else { // Default to English
pushNotificationText = [
.text: "You got a message.",
.twonly: "You got a twonly.",
.video: "You got a video.",
.image: "You got an image.",
.contactRequest: "You got a contact request.",
.acceptRequest: "Your contact request has been accepted.",
.storedMediaFile: "Your image has been saved.",
.reaction: "You got a reaction to your image.",
.testNotification: "This is a test notification.",
.reopenedMedia: "has reopened your image.",
.reactionToVideo: "You got a reaction to your video.",
.reactionToText: "You got a reaction to your text.",
.reactionToImage: "You got a reaction to your image.",
.response: "You got a response.",
]
}
// Return the corresponding message or an empty string if not found
return pushNotificationText[pushKind] ?? ""
}

View file

@ -37,6 +37,7 @@ enum PushKind: SwiftProtobuf.Enum, Swift.CaseIterable {
case reactionToVideo // = 11 case reactionToVideo // = 11
case reactionToText // = 12 case reactionToText // = 12
case reactionToImage // = 13 case reactionToImage // = 13
case addedToGroup // = 14
case UNRECOGNIZED(Int) case UNRECOGNIZED(Int)
init() { init() {
@ -59,6 +60,7 @@ enum PushKind: SwiftProtobuf.Enum, Swift.CaseIterable {
case 11: self = .reactionToVideo case 11: self = .reactionToVideo
case 12: self = .reactionToText case 12: self = .reactionToText
case 13: self = .reactionToImage case 13: self = .reactionToImage
case 14: self = .addedToGroup
default: self = .UNRECOGNIZED(rawValue) default: self = .UNRECOGNIZED(rawValue)
} }
} }
@ -79,6 +81,7 @@ enum PushKind: SwiftProtobuf.Enum, Swift.CaseIterable {
case .reactionToVideo: return 11 case .reactionToVideo: return 11
case .reactionToText: return 12 case .reactionToText: return 12
case .reactionToImage: return 13 case .reactionToImage: return 13
case .addedToGroup: return 14
case .UNRECOGNIZED(let i): return i case .UNRECOGNIZED(let i): return i
} }
} }
@ -99,6 +102,7 @@ enum PushKind: SwiftProtobuf.Enum, Swift.CaseIterable {
.reactionToVideo, .reactionToVideo,
.reactionToText, .reactionToText,
.reactionToImage, .reactionToImage,
.addedToGroup,
] ]
} }
@ -137,21 +141,21 @@ struct PushNotification: Sendable {
/// Clears the value of `messageID`. Subsequent reads from it will return its default value. /// Clears the value of `messageID`. Subsequent reads from it will return its default value.
mutating func clearMessageID() {self._messageID = nil} mutating func clearMessageID() {self._messageID = nil}
var reactionContent: String { var additionalContent: String {
get {return _reactionContent ?? String()} get {return _additionalContent ?? String()}
set {_reactionContent = newValue} set {_additionalContent = newValue}
} }
/// Returns true if `reactionContent` has been explicitly set. /// Returns true if `additionalContent` has been explicitly set.
var hasReactionContent: Bool {return self._reactionContent != nil} var hasAdditionalContent: Bool {return self._additionalContent != nil}
/// Clears the value of `reactionContent`. Subsequent reads from it will return its default value. /// Clears the value of `additionalContent`. Subsequent reads from it will return its default value.
mutating func clearReactionContent() {self._reactionContent = nil} mutating func clearAdditionalContent() {self._additionalContent = nil}
var unknownFields = SwiftProtobuf.UnknownStorage() var unknownFields = SwiftProtobuf.UnknownStorage()
init() {} init() {}
fileprivate var _messageID: String? = nil fileprivate var _messageID: String? = nil
fileprivate var _reactionContent: String? = nil fileprivate var _additionalContent: String? = nil
} }
struct PushUsers: Sendable { struct PushUsers: Sendable {
@ -214,7 +218,7 @@ struct PushKey: Sendable {
// MARK: - Code below here is support for the SwiftProtobuf runtime. // MARK: - Code below here is support for the SwiftProtobuf runtime.
extension PushKind: SwiftProtobuf._ProtoNameProviding { extension PushKind: SwiftProtobuf._ProtoNameProviding {
static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0reaction\0\u{1}response\0\u{1}text\0\u{1}video\0\u{1}twonly\0\u{1}image\0\u{1}contactRequest\0\u{1}acceptRequest\0\u{1}storedMediaFile\0\u{1}testNotification\0\u{1}reopenedMedia\0\u{1}reactionToVideo\0\u{1}reactionToText\0\u{1}reactionToImage\0") static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0reaction\0\u{1}response\0\u{1}text\0\u{1}video\0\u{1}twonly\0\u{1}image\0\u{1}contactRequest\0\u{1}acceptRequest\0\u{1}storedMediaFile\0\u{1}testNotification\0\u{1}reopenedMedia\0\u{1}reactionToVideo\0\u{1}reactionToText\0\u{1}reactionToImage\0\u{1}addedToGroup\0")
} }
extension EncryptedPushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { extension EncryptedPushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
@ -264,7 +268,7 @@ extension EncryptedPushNotification: SwiftProtobuf.Message, SwiftProtobuf._Messa
extension PushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { extension PushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "PushNotification" static let protoMessageName: String = "PushNotification"
static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}kind\0\u{1}messageId\0\u{1}reactionContent\0") static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}kind\0\u{1}messageId\0\u{1}additionalContent\0")
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws { mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() { while let fieldNumber = try decoder.nextFieldNumber() {
@ -274,7 +278,7 @@ extension PushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImpleme
switch fieldNumber { switch fieldNumber {
case 1: try { try decoder.decodeSingularEnumField(value: &self.kind) }() case 1: try { try decoder.decodeSingularEnumField(value: &self.kind) }()
case 2: try { try decoder.decodeSingularStringField(value: &self._messageID) }() case 2: try { try decoder.decodeSingularStringField(value: &self._messageID) }()
case 3: try { try decoder.decodeSingularStringField(value: &self._reactionContent) }() case 3: try { try decoder.decodeSingularStringField(value: &self._additionalContent) }()
default: break default: break
} }
} }
@ -291,7 +295,7 @@ extension PushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImpleme
try { if let v = self._messageID { try { if let v = self._messageID {
try visitor.visitSingularStringField(value: v, fieldNumber: 2) try visitor.visitSingularStringField(value: v, fieldNumber: 2)
} }() } }()
try { if let v = self._reactionContent { try { if let v = self._additionalContent {
try visitor.visitSingularStringField(value: v, fieldNumber: 3) try visitor.visitSingularStringField(value: v, fieldNumber: 3)
} }() } }()
try unknownFields.traverse(visitor: &visitor) try unknownFields.traverse(visitor: &visitor)
@ -300,7 +304,7 @@ extension PushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImpleme
static func ==(lhs: PushNotification, rhs: PushNotification) -> Bool { static func ==(lhs: PushNotification, rhs: PushNotification) -> Bool {
if lhs.kind != rhs.kind {return false} if lhs.kind != rhs.kind {return false}
if lhs._messageID != rhs._messageID {return false} if lhs._messageID != rhs._messageID {return false}
if lhs._reactionContent != rhs._reactionContent {return false} if lhs._additionalContent != rhs._additionalContent {return false}
if lhs.unknownFields != rhs.unknownFields {return false} if lhs.unknownFields != rhs.unknownFields {return false}
return true return true
} }

View file

@ -773,5 +773,22 @@
"twonlySafeRecoverDesc": "Wenn du ein Backup mit twonly Backup erstellt hast, kannst du es hier wiederherstellen.", "twonlySafeRecoverDesc": "Wenn du ein Backup mit twonly Backup erstellt hast, kannst du es hier wiederherstellen.",
"@twonlySafeRecoverDesc": {}, "@twonlySafeRecoverDesc": {},
"twonlySafeRecoverBtn": "Backup wiederherstellen", "twonlySafeRecoverBtn": "Backup wiederherstellen",
"@twonlySafeRecoverBtn": {} "@twonlySafeRecoverBtn": {},
} "notificationText": "hat eine Nachricht gesendet.",
"notificationTwonly": "hat ein twonly gesendet.",
"notificationVideo": "hat ein Video gesendet.",
"notificationImage": "hat ein Bild gesendet.",
"notificationAddedToGroup": "hat dich zu \"{groupname}\" hinzugefügt.",
"notificationContactRequest": "möchte sich mit dir vernetzen.",
"notificationAcceptRequest": "ist jetzt mit dir vernetzt.",
"notificationStoredMediaFile": "hat dein Bild gespeichert.",
"notificationReaction": "hat auf dein Bild reagiert.",
"notificationReopenedMedia": "hat dein Bild erneut geöffnet.",
"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.",
"notificationTitleUnknownUser": "Jemand",
"notificationCategoryMessageTitle": "Nachrichten",
"notificationCategoryMessageDesc": "Nachrichten von anderen Benutzern."
}

View file

@ -552,5 +552,22 @@
"youLeftGroup": "You have left the group.", "youLeftGroup": "You have left the group.",
"makerLeftGroup": "{maker} has left the group.", "makerLeftGroup": "{maker} has left the group.",
"groupActionYou": "you", "groupActionYou": "you",
"groupActionYour": "your" "groupActionYour": "your",
"notificationText": "sent a message.",
"notificationTwonly": "sent a twonly.",
"notificationVideo": "sent a video.",
"notificationImage": "sent a image.",
"notificationAddedToGroup": "has added you to \"{groupname}\"",
"notificationContactRequest": "wants to connect with you.",
"notificationAcceptRequest": "is now connected with you.",
"notificationStoredMediaFile": "has stored your image.",
"notificationReaction": "has reacted to your image.",
"notificationReopenedMedia": "has reopened your image.",
"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.",
"notificationTitleUnknownUser": "Someone",
"notificationCategoryMessageTitle": "Messages",
"notificationCategoryMessageDesc": "Messages from other users."
} }

View file

@ -2327,85 +2327,85 @@ abstract class AppLocalizations {
/// No description provided for @youChangedGroupName. /// No description provided for @youChangedGroupName.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Du hast den Gruppennamen zu „{newGroupName}“ geändert.'** /// **'You have changed the group name to \"{newGroupName}\".'**
String youChangedGroupName(Object newGroupName); String youChangedGroupName(Object newGroupName);
/// No description provided for @makerChangedGroupName. /// No description provided for @makerChangedGroupName.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'{maker} hat den Gruppennamen zu „{newGroupName}“ geändert.'** /// **'{maker} has changed the group name to \"{newGroupName}\".'**
String makerChangedGroupName(Object maker, Object newGroupName); String makerChangedGroupName(Object maker, Object newGroupName);
/// No description provided for @youCreatedGroup. /// No description provided for @youCreatedGroup.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Du hast die Gruppe erstellt.'** /// **'You have created the group.'**
String get youCreatedGroup; String get youCreatedGroup;
/// No description provided for @makerCreatedGroup. /// No description provided for @makerCreatedGroup.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'{maker} hat die Gruppe erstellt.'** /// **'{maker} has created the group.'**
String makerCreatedGroup(Object maker); String makerCreatedGroup(Object maker);
/// No description provided for @youRemovedMember. /// No description provided for @youRemovedMember.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Du hast {affected} aus der Gruppe entfernt.'** /// **'You have removed {affected} from the group.'**
String youRemovedMember(Object affected); String youRemovedMember(Object affected);
/// No description provided for @makerRemovedMember. /// No description provided for @makerRemovedMember.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'{maker} hat {affected} aus der Gruppe entfernt.'** /// **'{maker} has removed {affected} from the group.'**
String makerRemovedMember(Object affected, Object maker); String makerRemovedMember(Object affected, Object maker);
/// No description provided for @youAddedMember. /// No description provided for @youAddedMember.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Du hast {affected} zur Gruppe hinzugefügt.'** /// **'You have added {affected} to the group.'**
String youAddedMember(Object affected); String youAddedMember(Object affected);
/// No description provided for @makerAddedMember. /// No description provided for @makerAddedMember.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'{maker} hat {affected} zur Gruppe hinzugefügt.'** /// **'{maker} has added {affected} to the group.'**
String makerAddedMember(Object affected, Object maker); String makerAddedMember(Object affected, Object maker);
/// No description provided for @youMadeAdmin. /// No description provided for @youMadeAdmin.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Du hast {affected} zum Administrator gemacht.'** /// **'You made {affected} an admin.'**
String youMadeAdmin(Object affected); String youMadeAdmin(Object affected);
/// No description provided for @makerMadeAdmin. /// No description provided for @makerMadeAdmin.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'{maker} hat {affected} zum Administrator gemacht.'** /// **'{maker} made {affected} an admin.'**
String makerMadeAdmin(Object affected, Object maker); String makerMadeAdmin(Object affected, Object maker);
/// No description provided for @youRevokedAdminRights. /// No description provided for @youRevokedAdminRights.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Du hast {affectedR} die Administratorrechte entzogen.'** /// **'You revoked {affectedR} admin rights.'**
String youRevokedAdminRights(Object affectedR); String youRevokedAdminRights(Object affectedR);
/// No description provided for @makerRevokedAdminRights. /// No description provided for @makerRevokedAdminRights.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'{maker} hat {affectedR} die Administratorrechte entzogen.'** /// **'{maker} revoked {affectedR} admin rights.'**
String makerRevokedAdminRights(Object affectedR, Object maker); String makerRevokedAdminRights(Object affectedR, Object maker);
/// No description provided for @youLeftGroup. /// No description provided for @youLeftGroup.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Du hast die Gruppe verlassen.'** /// **'You have left the group.'**
String get youLeftGroup; String get youLeftGroup;
/// No description provided for @makerLeftGroup. /// No description provided for @makerLeftGroup.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'{maker} hat die Gruppe verlassen.'** /// **'{maker} has left the group.'**
String makerLeftGroup(Object maker); String makerLeftGroup(Object maker);
/// No description provided for @groupActionYou. /// No description provided for @groupActionYou.
@ -2419,6 +2419,108 @@ abstract class AppLocalizations {
/// In en, this message translates to: /// In en, this message translates to:
/// **'your'** /// **'your'**
String get groupActionYour; String get groupActionYour;
/// No description provided for @notificationText.
///
/// In en, this message translates to:
/// **'sent a message.'**
String get notificationText;
/// No description provided for @notificationTwonly.
///
/// In en, this message translates to:
/// **'sent a twonly.'**
String get notificationTwonly;
/// No description provided for @notificationVideo.
///
/// In en, this message translates to:
/// **'sent a video.'**
String get notificationVideo;
/// No description provided for @notificationImage.
///
/// In en, this message translates to:
/// **'sent a image.'**
String get notificationImage;
/// No description provided for @notificationAddedToGroup.
///
/// In en, this message translates to:
/// **'has added you to \"{groupname}\"'**
String notificationAddedToGroup(Object groupname);
/// No description provided for @notificationContactRequest.
///
/// In en, this message translates to:
/// **'wants to connect with you.'**
String get notificationContactRequest;
/// No description provided for @notificationAcceptRequest.
///
/// In en, this message translates to:
/// **'is now connected with you.'**
String get notificationAcceptRequest;
/// No description provided for @notificationStoredMediaFile.
///
/// In en, this message translates to:
/// **'has stored your image.'**
String get notificationStoredMediaFile;
/// No description provided for @notificationReaction.
///
/// In en, this message translates to:
/// **'has reacted to your image.'**
String get notificationReaction;
/// No description provided for @notificationReopenedMedia.
///
/// In en, this message translates to:
/// **'has reopened your image.'**
String get notificationReopenedMedia;
/// No description provided for @notificationReactionToVideo.
///
/// In en, this message translates to:
/// **'has reacted with {reaction} to your video.'**
String notificationReactionToVideo(Object reaction);
/// No description provided for @notificationReactionToText.
///
/// In en, this message translates to:
/// **'has reacted with {reaction} to your message.'**
String notificationReactionToText(Object reaction);
/// No description provided for @notificationReactionToImage.
///
/// In en, this message translates to:
/// **'has reacted with {reaction} to your image.'**
String notificationReactionToImage(Object reaction);
/// No description provided for @notificationResponse.
///
/// In en, this message translates to:
/// **'has responded.'**
String get notificationResponse;
/// No description provided for @notificationTitleUnknownUser.
///
/// In en, this message translates to:
/// **'Someone'**
String get notificationTitleUnknownUser;
/// No description provided for @notificationCategoryMessageTitle.
///
/// In en, this message translates to:
/// **'Messages'**
String get notificationCategoryMessageTitle;
/// No description provided for @notificationCategoryMessageDesc.
///
/// In en, this message translates to:
/// **'Messages from other users.'**
String get notificationCategoryMessageDesc;
} }
class _AppLocalizationsDelegate class _AppLocalizationsDelegate

View file

@ -1070,10 +1070,10 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get twonlySafeRecoverDesc => String get twonlySafeRecoverDesc =>
'If you have created a backup with twonly Backup, you can restore it here.'; 'Wenn du ein Backup mit twonly Backup erstellt hast, kannst du es hier wiederherstellen.';
@override @override
String get twonlySafeRecoverBtn => 'Restore backup'; String get twonlySafeRecoverBtn => 'Backup wiederherstellen';
@override @override
String get inviteFriends => 'Freunde einladen'; String get inviteFriends => 'Freunde einladen';
@ -1308,4 +1308,64 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get groupActionYour => 'deine'; String get groupActionYour => 'deine';
@override
String get notificationText => 'hat eine Nachricht gesendet.';
@override
String get notificationTwonly => 'hat ein twonly gesendet.';
@override
String get notificationVideo => 'hat ein Video gesendet.';
@override
String get notificationImage => 'hat ein Bild gesendet.';
@override
String notificationAddedToGroup(Object groupname) {
return 'hat dich zu \"$groupname\" hinzugefügt.';
}
@override
String get notificationContactRequest => 'möchte sich mit dir vernetzen.';
@override
String get notificationAcceptRequest => 'ist jetzt mit dir vernetzt.';
@override
String get notificationStoredMediaFile => 'hat dein Bild gespeichert.';
@override
String get notificationReaction => 'hat auf dein Bild reagiert.';
@override
String get notificationReopenedMedia => 'hat dein Bild erneut geöffnet.';
@override
String notificationReactionToVideo(Object reaction) {
return 'hat mit $reaction auf dein Video reagiert.';
}
@override
String notificationReactionToText(Object reaction) {
return 'hat mit $reaction auf deine Nachricht reagiert.';
}
@override
String notificationReactionToImage(Object reaction) {
return 'hat mit $reaction auf dein Bild reagiert.';
}
@override
String get notificationResponse => 'hat dir geantwortet.';
@override
String get notificationTitleUnknownUser => 'Jemand';
@override
String get notificationCategoryMessageTitle => 'Nachrichten';
@override
String get notificationCategoryMessageDesc =>
'Nachrichten von anderen Benutzern.';
} }

View file

@ -1232,68 +1232,68 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String youChangedGroupName(Object newGroupName) { String youChangedGroupName(Object newGroupName) {
return 'Du hast den Gruppennamen zu „$newGroupName“ geändert.'; return 'You have changed the group name to \"$newGroupName\".';
} }
@override @override
String makerChangedGroupName(Object maker, Object newGroupName) { String makerChangedGroupName(Object maker, Object newGroupName) {
return '$maker hat den Gruppennamen zu „$newGroupName“ geändert.'; return '$maker has changed the group name to \"$newGroupName\".';
} }
@override @override
String get youCreatedGroup => 'Du hast die Gruppe erstellt.'; String get youCreatedGroup => 'You have created the group.';
@override @override
String makerCreatedGroup(Object maker) { String makerCreatedGroup(Object maker) {
return '$maker hat die Gruppe erstellt.'; return '$maker has created the group.';
} }
@override @override
String youRemovedMember(Object affected) { String youRemovedMember(Object affected) {
return 'Du hast $affected aus der Gruppe entfernt.'; return 'You have removed $affected from the group.';
} }
@override @override
String makerRemovedMember(Object affected, Object maker) { String makerRemovedMember(Object affected, Object maker) {
return '$maker hat $affected aus der Gruppe entfernt.'; return '$maker has removed $affected from the group.';
} }
@override @override
String youAddedMember(Object affected) { String youAddedMember(Object affected) {
return 'Du hast $affected zur Gruppe hinzugefügt.'; return 'You have added $affected to the group.';
} }
@override @override
String makerAddedMember(Object affected, Object maker) { String makerAddedMember(Object affected, Object maker) {
return '$maker hat $affected zur Gruppe hinzugefügt.'; return '$maker has added $affected to the group.';
} }
@override @override
String youMadeAdmin(Object affected) { String youMadeAdmin(Object affected) {
return 'Du hast $affected zum Administrator gemacht.'; return 'You made $affected an admin.';
} }
@override @override
String makerMadeAdmin(Object affected, Object maker) { String makerMadeAdmin(Object affected, Object maker) {
return '$maker hat $affected zum Administrator gemacht.'; return '$maker made $affected an admin.';
} }
@override @override
String youRevokedAdminRights(Object affectedR) { String youRevokedAdminRights(Object affectedR) {
return 'Du hast $affectedR die Administratorrechte entzogen.'; return 'You revoked $affectedR admin rights.';
} }
@override @override
String makerRevokedAdminRights(Object affectedR, Object maker) { String makerRevokedAdminRights(Object affectedR, Object maker) {
return '$maker hat $affectedR die Administratorrechte entzogen.'; return '$maker revoked $affectedR admin rights.';
} }
@override @override
String get youLeftGroup => 'Du hast die Gruppe verlassen.'; String get youLeftGroup => 'You have left the group.';
@override @override
String makerLeftGroup(Object maker) { String makerLeftGroup(Object maker) {
return '$maker hat die Gruppe verlassen.'; return '$maker has left the group.';
} }
@override @override
@ -1301,4 +1301,63 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get groupActionYour => 'your'; String get groupActionYour => 'your';
@override
String get notificationText => 'sent a message.';
@override
String get notificationTwonly => 'sent a twonly.';
@override
String get notificationVideo => 'sent a video.';
@override
String get notificationImage => 'sent a image.';
@override
String notificationAddedToGroup(Object groupname) {
return 'has added you to \"$groupname\"';
}
@override
String get notificationContactRequest => 'wants to connect with you.';
@override
String get notificationAcceptRequest => 'is now connected with you.';
@override
String get notificationStoredMediaFile => 'has stored your image.';
@override
String get notificationReaction => 'has reacted to your image.';
@override
String get notificationReopenedMedia => 'has reopened your image.';
@override
String notificationReactionToVideo(Object reaction) {
return 'has reacted with $reaction to your video.';
}
@override
String notificationReactionToText(Object reaction) {
return 'has reacted with $reaction to your message.';
}
@override
String notificationReactionToImage(Object reaction) {
return 'has reacted with $reaction to your image.';
}
@override
String get notificationResponse => 'has responded.';
@override
String get notificationTitleUnknownUser => 'Someone';
@override
String get notificationCategoryMessageTitle => 'Messages';
@override
String get notificationCategoryMessageDesc => 'Messages from other users.';
} }

View file

@ -114,7 +114,7 @@ class PushNotification extends $pb.GeneratedMessage {
factory PushNotification({ factory PushNotification({
PushKind? kind, PushKind? kind,
$core.String? messageId, $core.String? messageId,
$core.String? reactionContent, $core.String? additionalContent,
}) { }) {
final $result = create(); final $result = create();
if (kind != null) { if (kind != null) {
@ -123,8 +123,8 @@ class PushNotification extends $pb.GeneratedMessage {
if (messageId != null) { if (messageId != null) {
$result.messageId = messageId; $result.messageId = messageId;
} }
if (reactionContent != null) { if (additionalContent != null) {
$result.reactionContent = reactionContent; $result.additionalContent = additionalContent;
} }
return $result; return $result;
} }
@ -135,7 +135,7 @@ class PushNotification extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'PushNotification', createEmptyInstance: create) static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'PushNotification', createEmptyInstance: create)
..e<PushKind>(1, _omitFieldNames ? '' : 'kind', $pb.PbFieldType.OE, defaultOrMaker: PushKind.reaction, valueOf: PushKind.valueOf, enumValues: PushKind.values) ..e<PushKind>(1, _omitFieldNames ? '' : 'kind', $pb.PbFieldType.OE, defaultOrMaker: PushKind.reaction, valueOf: PushKind.valueOf, enumValues: PushKind.values)
..aOS(2, _omitFieldNames ? '' : 'messageId', protoName: 'messageId') ..aOS(2, _omitFieldNames ? '' : 'messageId', protoName: 'messageId')
..aOS(3, _omitFieldNames ? '' : 'reactionContent', protoName: 'reactionContent') ..aOS(3, _omitFieldNames ? '' : 'additionalContent', protoName: 'additionalContent')
..hasRequiredFields = false ..hasRequiredFields = false
; ;
@ -179,13 +179,13 @@ class PushNotification extends $pb.GeneratedMessage {
void clearMessageId() => clearField(2); void clearMessageId() => clearField(2);
@$pb.TagNumber(3) @$pb.TagNumber(3)
$core.String get reactionContent => $_getSZ(2); $core.String get additionalContent => $_getSZ(2);
@$pb.TagNumber(3) @$pb.TagNumber(3)
set reactionContent($core.String v) { $_setString(2, v); } set additionalContent($core.String v) { $_setString(2, v); }
@$pb.TagNumber(3) @$pb.TagNumber(3)
$core.bool hasReactionContent() => $_has(2); $core.bool hasAdditionalContent() => $_has(2);
@$pb.TagNumber(3) @$pb.TagNumber(3)
void clearReactionContent() => clearField(3); void clearAdditionalContent() => clearField(3);
} }
class PushUsers extends $pb.GeneratedMessage { class PushUsers extends $pb.GeneratedMessage {

View file

@ -28,6 +28,7 @@ class PushKind extends $pb.ProtobufEnum {
static const PushKind reactionToVideo = PushKind._(11, _omitEnumNames ? '' : 'reactionToVideo'); static const PushKind reactionToVideo = PushKind._(11, _omitEnumNames ? '' : 'reactionToVideo');
static const PushKind reactionToText = PushKind._(12, _omitEnumNames ? '' : 'reactionToText'); static const PushKind reactionToText = PushKind._(12, _omitEnumNames ? '' : 'reactionToText');
static const PushKind reactionToImage = PushKind._(13, _omitEnumNames ? '' : 'reactionToImage'); static const PushKind reactionToImage = PushKind._(13, _omitEnumNames ? '' : 'reactionToImage');
static const PushKind addedToGroup = PushKind._(14, _omitEnumNames ? '' : 'addedToGroup');
static const $core.List<PushKind> values = <PushKind> [ static const $core.List<PushKind> values = <PushKind> [
reaction, reaction,
@ -44,6 +45,7 @@ class PushKind extends $pb.ProtobufEnum {
reactionToVideo, reactionToVideo,
reactionToText, reactionToText,
reactionToImage, reactionToImage,
addedToGroup,
]; ];
static final $core.Map<$core.int, PushKind> _byValue = $pb.ProtobufEnum.initByValue(values); static final $core.Map<$core.int, PushKind> _byValue = $pb.ProtobufEnum.initByValue(values);

View file

@ -31,6 +31,7 @@ const PushKind$json = {
{'1': 'reactionToVideo', '2': 11}, {'1': 'reactionToVideo', '2': 11},
{'1': 'reactionToText', '2': 12}, {'1': 'reactionToText', '2': 12},
{'1': 'reactionToImage', '2': 13}, {'1': 'reactionToImage', '2': 13},
{'1': 'addedToGroup', '2': 14},
], ],
}; };
@ -40,7 +41,7 @@ final $typed_data.Uint8List pushKindDescriptor = $convert.base64Decode(
'VvEAMSCgoGdHdvbmx5EAQSCQoFaW1hZ2UQBRISCg5jb250YWN0UmVxdWVzdBAGEhEKDWFjY2Vw' 'VvEAMSCgoGdHdvbmx5EAQSCQoFaW1hZ2UQBRISCg5jb250YWN0UmVxdWVzdBAGEhEKDWFjY2Vw'
'dFJlcXVlc3QQBxITCg9zdG9yZWRNZWRpYUZpbGUQCBIUChB0ZXN0Tm90aWZpY2F0aW9uEAkSEQ' 'dFJlcXVlc3QQBxITCg9zdG9yZWRNZWRpYUZpbGUQCBIUChB0ZXN0Tm90aWZpY2F0aW9uEAkSEQ'
'oNcmVvcGVuZWRNZWRpYRAKEhMKD3JlYWN0aW9uVG9WaWRlbxALEhIKDnJlYWN0aW9uVG9UZXh0' 'oNcmVvcGVuZWRNZWRpYRAKEhMKD3JlYWN0aW9uVG9WaWRlbxALEhIKDnJlYWN0aW9uVG9UZXh0'
'EAwSEwoPcmVhY3Rpb25Ub0ltYWdlEA0='); 'EAwSEwoPcmVhY3Rpb25Ub0ltYWdlEA0SEAoMYWRkZWRUb0dyb3VwEA4=');
@$core.Deprecated('Use encryptedPushNotificationDescriptor instead') @$core.Deprecated('Use encryptedPushNotificationDescriptor instead')
const EncryptedPushNotification$json = { const EncryptedPushNotification$json = {
@ -65,19 +66,20 @@ const PushNotification$json = {
'2': [ '2': [
{'1': 'kind', '3': 1, '4': 1, '5': 14, '6': '.PushKind', '10': 'kind'}, {'1': 'kind', '3': 1, '4': 1, '5': 14, '6': '.PushKind', '10': 'kind'},
{'1': 'messageId', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'messageId', '17': true}, {'1': 'messageId', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'messageId', '17': true},
{'1': 'reactionContent', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'reactionContent', '17': true}, {'1': 'additionalContent', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'additionalContent', '17': true},
], ],
'8': [ '8': [
{'1': '_messageId'}, {'1': '_messageId'},
{'1': '_reactionContent'}, {'1': '_additionalContent'},
], ],
}; };
/// Descriptor for `PushNotification`. Decode as a `google.protobuf.DescriptorProto`. /// Descriptor for `PushNotification`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List pushNotificationDescriptor = $convert.base64Decode( final $typed_data.Uint8List pushNotificationDescriptor = $convert.base64Decode(
'ChBQdXNoTm90aWZpY2F0aW9uEh0KBGtpbmQYASABKA4yCS5QdXNoS2luZFIEa2luZBIhCgltZX' 'ChBQdXNoTm90aWZpY2F0aW9uEh0KBGtpbmQYASABKA4yCS5QdXNoS2luZFIEa2luZBIhCgltZX'
'NzYWdlSWQYAiABKAlIAFIJbWVzc2FnZUlkiAEBEi0KD3JlYWN0aW9uQ29udGVudBgDIAEoCUgB' 'NzYWdlSWQYAiABKAlIAFIJbWVzc2FnZUlkiAEBEjEKEWFkZGl0aW9uYWxDb250ZW50GAMgASgJ'
'Ug9yZWFjdGlvbkNvbnRlbnSIAQFCDAoKX21lc3NhZ2VJZEISChBfcmVhY3Rpb25Db250ZW50'); 'SAFSEWFkZGl0aW9uYWxDb250ZW50iAEBQgwKCl9tZXNzYWdlSWRCFAoSX2FkZGl0aW9uYWxDb2'
'50ZW50');
@$core.Deprecated('Use pushUsersDescriptor instead') @$core.Deprecated('Use pushUsersDescriptor instead')
const PushUsers$json = { const PushUsers$json = {

View file

@ -22,12 +22,13 @@ enum PushKind {
reactionToVideo = 11; reactionToVideo = 11;
reactionToText = 12; reactionToText = 12;
reactionToImage = 13; reactionToImage = 13;
addedToGroup = 14;
}; };
message PushNotification { message PushNotification {
PushKind kind = 1; PushKind kind = 1;
optional string messageId = 2; optional string messageId = 2;
optional string reactionContent = 3; optional string additionalContent = 3;
} }

View file

@ -7,6 +7,9 @@ import 'package:cryptography_plus/cryptography_plus.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:twonly/src/constants/secure_storage_keys.dart'; import 'package:twonly/src/constants/secure_storage_keys.dart';
import 'package:twonly/src/localization/generated/app_localizations.dart';
import 'package:twonly/src/localization/generated/app_localizations_de.dart';
import 'package:twonly/src/localization/generated/app_localizations_en.dart';
import 'package:twonly/src/model/protobuf/client/generated/push_notification.pb.dart'; import 'package:twonly/src/model/protobuf/client/generated/push_notification.pb.dart';
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart'; import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
@ -148,10 +151,12 @@ Future<void> showLocalPushNotification(
styleInformation = FilePathAndroidBitmap(avatarPath); styleInformation = FilePathAndroidBitmap(avatarPath);
} }
final lang = getLocalizations();
final androidNotificationDetails = AndroidNotificationDetails( final androidNotificationDetails = AndroidNotificationDetails(
'0', '0',
'Messages', lang.notificationCategoryMessageTitle,
channelDescription: 'Messages from other users.', channelDescription: lang.notificationCategoryMessageDesc,
importance: Importance.max, importance: Importance.max,
priority: Priority.max, priority: Priority.max,
ticker: 'You got a new message.', ticker: 'You got a new message.',
@ -176,25 +181,29 @@ Future<void> showLocalPushNotification(
Future<void> showLocalPushNotificationWithoutUserId( Future<void> showLocalPushNotificationWithoutUserId(
PushNotification pushNotification, PushNotification pushNotification,
) async { ) async {
String? title;
String? body; String? body;
body = getPushNotificationTextWithoutUserId(pushNotification.kind); body = getPushNotificationText(pushNotification);
final lang = getLocalizations();
final title = lang.notificationTitleUnknownUser;
if (body == '') { if (body == '') {
Log.error('No push notification type defined!'); Log.error('No push notification type defined!');
} }
const androidNotificationDetails = AndroidNotificationDetails( final androidNotificationDetails = AndroidNotificationDetails(
'0', '0',
'Messages', lang.notificationCategoryMessageTitle,
channelDescription: 'Messages from other users.', channelDescription: lang.notificationCategoryMessageDesc,
importance: Importance.max, importance: Importance.max,
priority: Priority.max, priority: Priority.max,
ticker: 'You got a new message.', ticker: 'You got a new message.',
); );
const darwinNotificationDetails = DarwinNotificationDetails(); const darwinNotificationDetails = DarwinNotificationDetails();
const notificationDetails = NotificationDetails( final notificationDetails = NotificationDetails(
android: androidNotificationDetails, android: androidNotificationDetails,
iOS: darwinNotificationDetails, iOS: darwinNotificationDetails,
); );
@ -219,104 +228,35 @@ Future<String?> getAvatarIcon(int contactId) async {
return null; return null;
} }
String getPushNotificationTextWithoutUserId(PushKind pushKind) { AppLocalizations getLocalizations() {
Map<String, String> pushNotificationText;
final systemLanguage = Platform.localeName; final systemLanguage = Platform.localeName;
if (systemLanguage.contains('de')) return AppLocalizationsDe();
if (systemLanguage.contains('de')) { return AppLocalizationsEn();
pushNotificationText = {
PushKind.text.name: 'Du hast eine neue Nachricht erhalten.',
PushKind.twonly.name: 'Du hast ein neues twonly erhalten.',
PushKind.video.name: 'Du hast ein neues Video erhalten.',
PushKind.image.name: 'Du hast ein neues Bild erhalten.',
PushKind.contactRequest.name:
'Du hast eine neue Kontaktanfrage erhalten.',
PushKind.acceptRequest.name: 'Deine Kontaktanfrage wurde angenommen.',
PushKind.storedMediaFile.name: 'Dein Bild wurde gespeichert.',
PushKind.reaction.name: 'Du hast eine Reaktion auf dein Bild erhalten.',
PushKind.reopenedMedia.name: 'Dein Bild wurde erneut geöffnet.',
PushKind.reactionToVideo.name:
'Du hast eine Reaktion auf dein Video erhalten.',
PushKind.reactionToText.name:
'Du hast eine Reaktion auf deinen Text erhalten.',
PushKind.reactionToImage.name:
'Du hast eine Reaktion auf dein Bild erhalten.',
PushKind.response.name: 'Du hast eine Antwort erhalten.',
};
} else {
pushNotificationText = {
PushKind.text.name: 'You have received a new message.',
PushKind.twonly.name: 'You have received a new twonly.',
PushKind.video.name: 'You have received a new video.',
PushKind.image.name: 'You have received a new image.',
PushKind.contactRequest.name: 'You have received a new contact request.',
PushKind.acceptRequest.name: 'Your contact request has been accepted.',
PushKind.storedMediaFile.name: 'Your image has been saved.',
PushKind.reaction.name: 'You have received a reaction to your image.',
PushKind.reopenedMedia.name: 'Your image has been reopened.',
PushKind.reactionToVideo.name:
'You have received a reaction to your video.',
PushKind.reactionToText.name:
'You have received a reaction to your text.',
PushKind.reactionToImage.name:
'You have received a reaction to your image.',
PushKind.response.name: 'You have received a response.',
};
}
return pushNotificationText[pushKind.name] ?? '';
} }
String getPushNotificationText(PushNotification pushNotification) { String getPushNotificationText(PushNotification pushNotification) {
final systemLanguage = Platform.localeName; final lang = getLocalizations();
Map<String, String> pushNotificationText; final pushNotificationText = {
PushKind.text.name: lang.notificationText,
PushKind.twonly.name: lang.notificationTwonly,
PushKind.video.name: lang.notificationVideo,
PushKind.image.name: lang.notificationImage,
PushKind.contactRequest.name: lang.notificationContactRequest,
PushKind.acceptRequest.name: lang.notificationAcceptRequest,
PushKind.storedMediaFile.name: lang.notificationStoredMediaFile,
PushKind.reaction.name: lang.notificationReaction,
PushKind.reopenedMedia.name: lang.notificationReopenedMedia,
PushKind.reactionToVideo.name:
lang.notificationReactionToVideo(pushNotification.additionalContent),
PushKind.reactionToText.name:
lang.notificationReactionToText(pushNotification.additionalContent),
PushKind.reactionToImage.name:
lang.notificationReactionToImage(pushNotification.additionalContent),
PushKind.response.name: lang.notificationResponse,
PushKind.addedToGroup.name:
lang.notificationAddedToGroup(pushNotification.additionalContent),
};
if (systemLanguage.contains('de')) { return pushNotificationText[pushNotification.kind.name] ?? '';
pushNotificationText = {
PushKind.text.name: 'hat dir eine Nachricht gesendet.',
PushKind.twonly.name: 'hat dir ein twonly gesendet.',
PushKind.video.name: 'hat dir ein Video gesendet.',
PushKind.image.name: 'hat dir ein Bild gesendet.',
PushKind.contactRequest.name: 'möchte sich mit dir vernetzen.',
PushKind.acceptRequest.name: 'ist jetzt mit dir vernetzt.',
PushKind.storedMediaFile.name: 'hat dein Bild gespeichert.',
PushKind.reaction.name: 'hat auf dein Bild reagiert.',
PushKind.reopenedMedia.name: 'hat dein Bild erneut geöffnet.',
PushKind.reactionToVideo.name:
'hat mit {{reaction}} auf dein Video reagiert.',
PushKind.reactionToText.name:
'hat mit {{reaction}} auf deine Nachricht reagiert.',
PushKind.reactionToImage.name:
'hat mit {{reaction}} auf dein Bild reagiert.',
PushKind.response.name: 'hat dir geantwortet.',
};
} else {
pushNotificationText = {
PushKind.text.name: 'has sent you a message.',
PushKind.twonly.name: 'has sent you a twonly.',
PushKind.video.name: 'has sent you a video.',
PushKind.image.name: 'has sent you an image.',
PushKind.contactRequest.name: 'wants to connect with you.',
PushKind.acceptRequest.name: 'is now connected with you.',
PushKind.storedMediaFile.name: 'has stored your image.',
PushKind.reaction.name: 'has reacted to your image.',
PushKind.reopenedMedia.name: 'has reopened your image.',
PushKind.reactionToVideo.name:
'has reacted with {{reaction}} to your video.',
PushKind.reactionToText.name:
'has reacted with {{reaction}} to your message.',
PushKind.reactionToImage.name:
'has reacted with {{reaction}} to your image.',
PushKind.response.name: 'has responded.',
};
}
var contentText = pushNotificationText[pushNotification.kind.name] ?? '';
if (pushNotification.hasReactionContent()) {
contentText = contentText.replaceAll(
'{{reaction}}',
pushNotification.reactionContent,
);
}
return contentText;
} }

View file

@ -201,7 +201,7 @@ Future<PushNotification?> getPushNotificationFromEncryptedContent(
EncryptedContent content, EncryptedContent content,
) async { ) async {
PushKind? kind; PushKind? kind;
String? reactionContent; String? additionalContent;
if (content.hasReaction()) { if (content.hasReaction()) {
if (content.reaction.remove) return null; if (content.reaction.remove) return null;
@ -209,7 +209,9 @@ Future<PushNotification?> getPushNotificationFromEncryptedContent(
final msg = await twonlyDB.messagesDao final msg = await twonlyDB.messagesDao
.getMessageById(content.reaction.targetMessageId) .getMessageById(content.reaction.targetMessageId)
.getSingleOrNull(); .getSingleOrNull();
if (msg == null) return null; if (msg == null || msg.senderId == null || msg.senderId != toUserId) {
return null;
}
if (msg.content != null) { if (msg.content != null) {
kind = PushKind.reactionToText; kind = PushKind.reactionToText;
} else if (msg.mediaId != null) { } else if (msg.mediaId != null) {
@ -224,7 +226,7 @@ Future<PushNotification?> getPushNotificationFromEncryptedContent(
kind = PushKind.reaction; kind = PushKind.reaction;
} }
} }
reactionContent = content.reaction.emoji; additionalContent = content.reaction.emoji;
} }
if (content.hasTextMessage()) { if (content.hasTextMessage()) {
@ -270,11 +272,17 @@ Future<PushNotification?> getPushNotificationFromEncryptedContent(
} }
} }
if (content.hasGroupCreate()) {
kind = PushKind.addedToGroup;
final group = await twonlyDB.groupsDao.getGroup(content.groupId);
additionalContent = group!.groupName;
}
if (kind == null) return null; if (kind == null) return null;
final pushNotification = PushNotification()..kind = kind; final pushNotification = PushNotification()..kind = kind;
if (reactionContent != null) { if (additionalContent != null) {
pushNotification.reactionContent = reactionContent; pushNotification.additionalContent = additionalContent;
} }
if (messageId != null) { if (messageId != null) {
pushNotification.messageId = messageId; pushNotification.messageId = messageId;

View file

@ -75,7 +75,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
selectedGroupIds.add(widget.sendToGroup!.groupId); selectedGroupIds.add(widget.sendToGroup!.groupId);
} }
if (widget.mediaFileService.mediaFile.type == MediaType.video || if (widget.mediaFileService.mediaFile.type == MediaType.image ||
widget.mediaFileService.mediaFile.type == MediaType.gif) { widget.mediaFileService.mediaFile.type == MediaType.gif) {
if (widget.imageBytesFuture != null) { if (widget.imageBytesFuture != null) {
loadImage(widget.imageBytesFuture!); loadImage(widget.imageBytesFuture!);

View file

@ -77,9 +77,12 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
late StreamSubscription<Group?> userSub; late StreamSubscription<Group?> userSub;
late StreamSubscription<List<Message>> messageSub; late StreamSubscription<List<Message>> messageSub;
late StreamSubscription<List<GroupHistory>>? groupActionsSub; late StreamSubscription<List<GroupHistory>>? groupActionsSub;
late StreamSubscription<List<Contact>>? contactSub;
late StreamSubscription<Future<List<(Message, Contact)>>>? late StreamSubscription<Future<List<(Message, Contact)>>>?
lastOpenedMessageByContactSub; lastOpenedMessageByContactSub;
Map<int, Contact> userIdToContact = {};
List<ChatItem> messages = []; List<ChatItem> messages = [];
List<Message> allMessages = []; List<Message> allMessages = [];
List<(Message, Contact)> lastOpenedMessageByContact = []; List<(Message, Contact)> lastOpenedMessageByContact = [];
@ -110,6 +113,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
void dispose() { void dispose() {
userSub.cancel(); userSub.cancel();
messageSub.cancel(); messageSub.cancel();
contactSub?.cancel();
groupActionsSub?.cancel(); groupActionsSub?.cancel();
lastOpenedMessageByContactSub?.cancel(); lastOpenedMessageByContactSub?.cancel();
tutorial?.cancel(); tutorial?.cancel();
@ -143,6 +147,13 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
groupActions = update; groupActions = update;
await setMessages(allMessages, lastOpenedMessageByContact, update); await setMessages(allMessages, lastOpenedMessageByContact, update);
}); });
final contactsStream = twonlyDB.contactsDao.watchAllContacts();
contactSub = contactsStream.listen((contacts) {
for (final contact in contacts) {
userIdToContact[contact.userId] = contact;
}
});
} }
final msgStream = twonlyDB.messagesDao.watchByGroupId(group.groupId); final msgStream = twonlyDB.messagesDao.watchByGroupId(group.groupId);
@ -408,6 +419,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
: null, : null,
group: group, group: group,
galleryItems: galleryItems, galleryItems: galleryItems,
userIdToContact: userIdToContact,
scrollToMessage: scrollToMessage, scrollToMessage: scrollToMessage,
onResponseTriggered: () { onResponseTriggered: () {
setState(() { setState(() {

View file

@ -14,6 +14,7 @@ import 'package:twonly/src/views/chats/chat_messages_components/message_actions.
import 'package:twonly/src/views/chats/chat_messages_components/message_context_menu.dart'; import 'package:twonly/src/views/chats/chat_messages_components/message_context_menu.dart';
import 'package:twonly/src/views/chats/chat_messages_components/response_container.dart'; import 'package:twonly/src/views/chats/chat_messages_components/response_container.dart';
import 'package:twonly/src/views/components/avatar_icon.component.dart'; import 'package:twonly/src/views/components/avatar_icon.component.dart';
import 'package:twonly/src/views/contact/contact.view.dart';
class ChatListEntry extends StatefulWidget { class ChatListEntry extends StatefulWidget {
const ChatListEntry({ const ChatListEntry({
@ -24,6 +25,7 @@ class ChatListEntry extends StatefulWidget {
this.onResponseTriggered, this.onResponseTriggered,
this.prevMessage, this.prevMessage,
this.nextMessage, this.nextMessage,
this.userIdToContact,
this.hideReactions = false, this.hideReactions = false,
super.key, super.key,
}); });
@ -31,6 +33,7 @@ class ChatListEntry extends StatefulWidget {
final Message? nextMessage; final Message? nextMessage;
final Message message; final Message message;
final Group group; final Group group;
final Map<int, Contact>? userIdToContact;
final bool hideReactions; final bool hideReactions;
final List<MemoryItem> galleryItems; final List<MemoryItem> galleryItems;
final void Function(String)? scrollToMessage; final void Function(String)? scrollToMessage;
@ -108,6 +111,8 @@ class _ChatListEntryState extends State<ChatListEntry> {
ChatTextEntry( ChatTextEntry(
message: widget.message, message: widget.message,
nextMessage: widget.nextMessage, nextMessage: widget.nextMessage,
prevMessage: widget.prevMessage,
userIdToContact: widget.userIdToContact,
borderRadius: borderRadius, borderRadius: borderRadius,
minWidth: reactionsForWidth * 43, minWidth: reactionsForWidth * 43,
) )
@ -124,6 +129,8 @@ class _ChatListEntryState extends State<ChatListEntry> {
? ChatTextEntry( ? ChatTextEntry(
message: widget.message, message: widget.message,
nextMessage: widget.nextMessage, nextMessage: widget.nextMessage,
prevMessage: widget.prevMessage,
userIdToContact: widget.userIdToContact,
borderRadius: borderRadius, borderRadius: borderRadius,
minWidth: reactionsForWidth * 43, minWidth: reactionsForWidth * 43,
) )
@ -182,9 +189,20 @@ class _ChatListEntryState extends State<ChatListEntry> {
if (!right && !widget.group.isDirectChat) if (!right && !widget.group.isDirectChat)
hideContactAvatar hideContactAvatar
? const SizedBox(width: 24) ? const SizedBox(width: 24)
: AvatarIcon( : GestureDetector(
contactId: widget.message.senderId, onTap: () async {
fontSize: 12, await Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ContactView(widget.message.senderId!),
),
);
},
child: AvatarIcon(
contactId: widget.message.senderId,
fontSize: 12,
),
), ),
child, child,
], ],

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart' hide TextDirection; import 'package:intl/intl.dart' hide TextDirection;
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/tables/messages.table.dart'; import 'package:twonly/src/database/tables/messages.table.dart';
import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
@ -12,13 +13,17 @@ class ChatTextEntry extends StatelessWidget {
const ChatTextEntry({ const ChatTextEntry({
required this.message, required this.message,
required this.nextMessage, required this.nextMessage,
required this.prevMessage,
required this.borderRadius, required this.borderRadius,
required this.userIdToContact,
required this.minWidth, required this.minWidth,
super.key, super.key,
}); });
final Message message; final Message message;
final Message? nextMessage; final Message? nextMessage;
final Message? prevMessage;
final Map<int, Contact>? userIdToContact;
final BorderRadius borderRadius; final BorderRadius borderRadius;
final double minWidth; final double minWidth;
@ -41,6 +46,17 @@ class ChatTextEntry extends StatelessWidget {
} }
var displayTime = !combineTextMessageWithNext(message, nextMessage); var displayTime = !combineTextMessageWithNext(message, nextMessage);
var displayUserName = '';
if (message.senderId != null &&
prevMessage != null &&
userIdToContact != null) {
if (!combineTextMessageWithNext(prevMessage!, message)) {
if (userIdToContact![message.senderId] != null) {
displayUserName =
getContactDisplayName(userIdToContact![message.senderId]!);
}
}
}
var spacerWidth = minWidth - measureTextWidth(text) - 53; var spacerWidth = minWidth - measureTextWidth(text) - 53;
if (spacerWidth < 0) spacerWidth = 0; if (spacerWidth < 0) spacerWidth = 0;
@ -75,57 +91,71 @@ class ChatTextEntry extends StatelessWidget {
color: color, color: color,
borderRadius: borderRadius, borderRadius: borderRadius,
), ),
child: Row( child: Column(
mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
if (expanded) if (displayUserName != '')
Expanded( Text(
child: BetterText(text: text, textColor: textColor), displayUserName,
) textAlign: TextAlign.left,
else ...[ style: const TextStyle(
BetterText(text: text, textColor: textColor), color: Colors.white,
SizedBox( fontWeight: FontWeight.bold,
width: spacerWidth,
),
],
if (displayTime || message.modifiedAt != null)
Align(
alignment: AlignmentGeometry.centerRight,
child: Padding(
padding: const EdgeInsets.only(left: 6),
child: Row(
children: [
if (message.modifiedAt != null)
Padding(
padding: const EdgeInsets.only(right: 5),
child: SizedBox(
height: 10,
child: FaIcon(
FontAwesomeIcons.pencil,
color: Colors.white.withAlpha(150),
size: 10,
),
),
),
Text(
friendlyTime(
context,
(message.modifiedAt != null)
? message.modifiedAt!
: message.createdAt,
),
style: TextStyle(
fontSize: 10,
color: Colors.white.withAlpha(150),
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
],
),
), ),
), ),
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (expanded)
Expanded(
child: BetterText(text: text, textColor: textColor),
)
else ...[
BetterText(text: text, textColor: textColor),
SizedBox(
width: spacerWidth,
),
],
if (displayTime || message.modifiedAt != null)
Align(
alignment: AlignmentGeometry.centerRight,
child: Padding(
padding: const EdgeInsets.only(left: 6),
child: Row(
children: [
if (message.modifiedAt != null)
Padding(
padding: const EdgeInsets.only(right: 5),
child: SizedBox(
height: 10,
child: FaIcon(
FontAwesomeIcons.pencil,
color: Colors.white.withAlpha(150),
size: 10,
),
),
),
Text(
friendlyTime(
context,
(message.modifiedAt != null)
? message.modifiedAt!
: message.createdAt,
),
style: TextStyle(
fontSize: 10,
color: Colors.white.withAlpha(150),
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
],
),
),
),
],
),
], ],
), ),
); );

View file

@ -80,9 +80,9 @@ class _ResponseContainerState extends State<ResponseContainer> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Padding( Padding(
key: _preview,
padding: const EdgeInsets.only(top: 4, right: 4, left: 4), padding: const EdgeInsets.only(top: 4, right: 4, left: 4),
child: Container( child: Container(
key: _preview,
width: minWidth, width: minWidth,
decoration: BoxDecoration( decoration: BoxDecoration(
color: context.color.surface.withAlpha(150), color: context.color.surface.withAlpha(150),