Add avatar to group chat

Check first message received per participant
This commit is contained in:
Benoit Martins 2024-05-15 17:26:15 +02:00
parent d7a7615616
commit a011e7643b
14 changed files with 261 additions and 48 deletions

View file

@ -377,7 +377,7 @@ struct CallView: View {
&& $0.friend!.name == addressFriend!.name
&& $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly()
})
: ContactAvatarModel(friend: nil, name: "", withPresence: false)
: ContactAvatarModel(friend: nil, name: "", address: "", withPresence: false)
if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty {
if contactAvatarModel != nil {
@ -732,7 +732,7 @@ struct CallView: View {
&& $0.friend!.name == addressFriend!.name
&& $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly()
})
: ContactAvatarModel(friend: nil, name: "", withPresence: false)
: ContactAvatarModel(friend: nil, name: "", address: "", withPresence: false)
if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty {
if contactAvatarModel != nil {

View file

@ -226,7 +226,7 @@ struct CallsListFragment: View {
&& $0.friend!.name == addressFriend!.name
&& $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly()
})
: ContactAvatarModel(friend: nil, name: "", withPresence: false)
: ContactAvatarModel(friend: nil, name: "", address: "", withPresence: false)
if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty {
if contactAvatarModel != nil {

View file

@ -68,6 +68,8 @@ class MeetingWaitingRoomViewModel: ObservableObject {
? ContactsManager.shared.getFriendWithAddress(address: core.defaultAccount!.contactAddress!)
: nil
let addressTmp = friend?.address?.asStringUriOnly() ?? ""
if friend != nil && friend!.address != nil && friend!.address!.displayName != nil {
userNameTmp = friend!.address!.displayName!
} else {
@ -82,8 +84,8 @@ class MeetingWaitingRoomViewModel: ObservableObject {
? ContactsManager.shared.avatarListModel.first(where: {
$0.friend!.name == friend!.name
&& $0.friend!.address!.asStringUriOnly() == core.defaultAccount!.contactAddress!.asStringUriOnly()
}) ?? ContactAvatarModel(friend: nil, name: userNameTmp, withPresence: false)
: ContactAvatarModel(friend: nil, name: userNameTmp, withPresence: false)
}) ?? ContactAvatarModel(friend: nil, name: userNameTmp, address: addressTmp, withPresence: false)
: ContactAvatarModel(friend: nil, name: userNameTmp, address: addressTmp, withPresence: false)
if core.videoEnabled && !core.videoPreviewEnabled {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {

View file

@ -286,7 +286,7 @@ struct ContactInnerFragment: View {
#Preview {
ContactInnerFragment(
contactAvatarModel: ContactAvatarModel(friend: nil, name: "", withPresence: true),
contactAvatarModel: ContactAvatarModel(friend: nil, name: "", address: "", withPresence: true),
contactViewModel: ContactViewModel(),
editContactViewModel: EditContactViewModel(),
isShowDeletePopup: .constant(false),

View file

@ -155,7 +155,12 @@ struct EditContactFragment: View {
&& !editContactViewModel.selectedEditFriend!.photo!.isEmpty && selectedImage == nil && !removedImage {
Avatar(contactAvatarModel:
ContactAvatarModel(friend: editContactViewModel.selectedEditFriend!, name: editContactViewModel.selectedEditFriend?.name ?? "", withPresence: false), avatarSize: 100
ContactAvatarModel(
friend: editContactViewModel.selectedEditFriend!,
name: editContactViewModel.selectedEditFriend?.name ?? "",
address: editContactViewModel.selectedEditFriend?.address?.asStringUriOnly() ?? "",
withPresence: false
), avatarSize: 100
)
} else if selectedImage == nil {

View file

@ -27,6 +27,8 @@ class ContactAvatarModel: ObservableObject {
let name: String
let address: String
let withPresence: Bool?
@Published var lastPresenceInfo: String
@ -35,9 +37,10 @@ class ContactAvatarModel: ObservableObject {
private var friendSuscription: AnyCancellable?
init(friend: Friend?, name: String, withPresence: Bool?) {
init(friend: Friend?, name: String, address: String, withPresence: Bool?) {
self.friend = friend
self.name = name
self.address = address
self.withPresence = withPresence
if friend != nil &&
withPresence == true {
@ -122,12 +125,12 @@ class ContactAvatarModel: ObservableObject {
})
if avatarModel == nil {
avatarModel = ContactAvatarModel(friend: nil, name: addressFriend.name!, withPresence: false)
avatarModel = ContactAvatarModel(friend: nil, name: addressFriend.name!, address: address.asStringUriOnly(), withPresence: false)
}
return avatarModel!
} else {
let name = address.displayName != nil ? address.displayName! : address.username!
return ContactAvatarModel(friend: nil, name: name, withPresence: false)
return ContactAvatarModel(friend: nil, name: name, address: address.asStringUriOnly(), withPresence: false)
}
}
}

View file

@ -728,7 +728,7 @@ struct ContentView: View {
&& $0.friend!.name == addressFriend!.name
&& $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly()
})
: ContactAvatarModel(friend: nil, name: "", withPresence: false)
: ContactAvatarModel(friend: nil, name: "", address: "", withPresence: false)
if contactAvatarModel != nil {
HistoryContactFragment(

View file

@ -30,32 +30,106 @@ struct ChatBubbleView: View {
var body: some View {
VStack {
HStack {
if message.isOutgoing {
Spacer()
}
VStack(alignment: message.isOutgoing ? .trailing : .leading) {
if !message.attachments.isEmpty {
messageAttachments()
if !message.text.isEmpty || !message.attachments.isEmpty {
HStack {
if message.isOutgoing {
Spacer()
}
if !message.text.isEmpty {
Text(message.text)
.foregroundStyle(Color.grayMain2c700)
.default_text_style(styleSize: 16)
if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && !message.isOutgoing && message.isFirstMessage {
VStack {
Avatar(
contactAvatarModel: conversationViewModel.participantConversationModel.first(where: {$0.address == message.address}) ??
ContactAvatarModel(friend: nil, name: "??", address: "", withPresence: false),
avatarSize: 35
)
.padding(.top, 30)
Spacer()
}
} else if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && !message.isOutgoing {
VStack {
Avatar(
contactAvatarModel: ContactAvatarModel(friend: nil, name: "??", address: "", withPresence: false),
avatarSize: 35
)
Spacer()
}
.hidden()
}
VStack(alignment: .leading, spacing: 0) {
if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && !message.isOutgoing && message.isFirstMessage {
Text(conversationViewModel.participantConversationModel.first(where: {$0.address == message.address})?.name ?? "")
.default_text_style(styleSize: 12)
.padding(.top, 10)
.padding(.bottom, 2)
}
ZStack {
if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && message.isFirstMessage {
VStack {
if message.isOutgoing {
Spacer()
}
HStack {
if message.isOutgoing {
Spacer()
}
VStack {
}
.frame(width: 15, height: 15)
.background(message.isOutgoing ? Color.orangeMain100 : Color.grayMain2c100)
.clipShape(RoundedRectangle(cornerRadius: 2))
if !message.isOutgoing {
Spacer()
}
}
if !message.isOutgoing {
Spacer()
}
}
}
HStack {
if message.isOutgoing {
Spacer()
}
VStack(alignment: message.isOutgoing ? .trailing : .leading) {
if !message.attachments.isEmpty {
messageAttachments()
}
if !message.text.isEmpty {
Text(message.text)
.foregroundStyle(Color.grayMain2c700)
.default_text_style(styleSize: 16)
}
}
.padding(.all, 15)
.background(message.isOutgoing ? Color.orangeMain100 : Color.grayMain2c100)
.clipShape(RoundedRectangle(cornerRadius: 16))
if !message.isOutgoing {
Spacer()
}
}
}
.frame(maxWidth: .infinity)
}
if !message.isOutgoing {
Spacer()
}
}
.padding(.all, 15)
.background(message.isOutgoing ? Color.orangeMain100 : Color.grayMain2c100)
.clipShape(RoundedRectangle(cornerRadius: 16))
if !message.isOutgoing {
Spacer()
}
.padding(.leading, message.isOutgoing ? 40 : 0)
.padding(.trailing, !message.isOutgoing ? 40 : 0)
}
.padding(.leading, message.isOutgoing ? 40 : 0)
.padding(.trailing, !message.isOutgoing ? 40 : 0)
}
}

View file

@ -90,7 +90,7 @@ class ConversationModel: ObservableObject {
self.unreadMessagesCount = 0
self.avatarModel = ContactAvatarModel(friend: nil, name: "", withPresence: false)
self.avatarModel = ContactAvatarModel(friend: nil, name: "", address: "", withPresence: false)
//self.isBeingDeleted = MutableLiveData<Boolean>()
@ -199,13 +199,25 @@ class ConversationModel: ObservableObject {
}
}
let addressTmp = addressFriend?.address?.asStringUriOnly() ?? ""
let avatarModelTmp = addressFriend != nil && !self.isGroup
? ContactsManager.shared.avatarListModel.first(where: {
$0.friend!.name == addressFriend!.name
&& $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly()
})
?? ContactAvatarModel(friend: nil, name: self.subject, withPresence: false)
: ContactAvatarModel(friend: nil, name: self.subject, withPresence: false)
?? ContactAvatarModel(
friend: nil,
name: self.subject,
address: addressTmp,
withPresence: false
)
: ContactAvatarModel(
friend: nil,
name: self.subject,
address: addressTmp,
withPresence: false
)
DispatchQueue.main.async {
self.avatarModel = avatarModelTmp

View file

@ -61,6 +61,8 @@ public struct Message: Identifiable, Hashable {
public var createdAt: Date
public var isOutgoing: Bool
public var address: String
public var isFirstMessage: Bool
public var text: String
public var attachments: [Attachment]
public var recording: Recording?
@ -71,6 +73,8 @@ public struct Message: Identifiable, Hashable {
status: Status? = nil,
createdAt: Date = Date(),
isOutgoing: Bool,
address: String,
isFirstMessage: Bool = false,
text: String = "",
attachments: [Attachment] = [],
recording: Recording? = nil,
@ -80,6 +84,8 @@ public struct Message: Identifiable, Hashable {
self.status = status
self.createdAt = createdAt
self.isOutgoing = isOutgoing
self.isFirstMessage = isFirstMessage
self.address = address
self.text = text
self.attachments = attachments
self.recording = recording
@ -111,6 +117,8 @@ public struct Message: Identifiable, Hashable {
status: status,
createdAt: draft.createdAt,
isOutgoing: draft.isOutgoing,
address: draft.address,
isFirstMessage: draft.isFirstMessage,
text: draft.text,
attachments: attachments,
recording: draft.recording,
@ -127,7 +135,7 @@ extension Message {
extension Message: Equatable {
public static func == (lhs: Message, rhs: Message) -> Bool {
lhs.id == rhs.id && lhs.status == rhs.status
lhs.id == rhs.id && lhs.status == rhs.status && lhs.isFirstMessage == rhs.isFirstMessage
}
}
@ -150,18 +158,24 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
public var id: String
public var address: String
public var isFirstMessage: Bool
public var text: String
public var isOutgoing: Bool
public var attachments: [Attachment]
public var recording: Recording?
public init(id: String,
address: String,
isFirstMessage: Bool = false,
text: String = "",
isOutgoing: Bool,
attachments: [Attachment] = [],
recording: Recording? = nil) {
self.id = id
self.address = address
self.isFirstMessage = isFirstMessage
self.text = text
self.isOutgoing = isOutgoing
self.attachments = attachments
@ -169,20 +183,22 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
}
func toMessage() -> Message {
Message(id: id, isOutgoing: isOutgoing, text: text, attachments: attachments, recording: recording)
Message(id: id, isOutgoing: isOutgoing, address: address, isFirstMessage: isFirstMessage, text: text, attachments: attachments, recording: recording)
}
}
public extension Message {
func toReplyMessage() -> ReplyMessage {
ReplyMessage(id: id, text: text, isOutgoing: isOutgoing, attachments: attachments, recording: recording)
ReplyMessage(id: id, address: address, isFirstMessage: isFirstMessage, text: text, isOutgoing: isOutgoing, attachments: attachments, recording: recording)
}
}
public struct DraftMessage {
public var id: String?
public let isOutgoing: Bool
public let address: String
public let isFirstMessage: Bool
public let text: String
public let medias: [Media]
public let recording: Recording?
@ -191,6 +207,8 @@ public struct DraftMessage {
public init(id: String? = nil,
isOutgoing: Bool,
address: String,
isFirstMessage: Bool,
text: String,
medias: [Media],
recording: Recording?,
@ -198,6 +216,8 @@ public struct DraftMessage {
createdAt: Date) {
self.id = id
self.isOutgoing = isOutgoing
self.address = address
self.isFirstMessage = isFirstMessage
self.text = text
self.medias = medias
self.recording = recording

View file

@ -38,6 +38,7 @@ class ConversationViewModel: ObservableObject {
@Published var conversationMessagesIds: [String] = []
@Published var conversationMessagesSection: [MessagesSection] = []
@Published var participantConversationModel: [ContactAvatarModel] = []
init() {}
@ -91,9 +92,25 @@ class ConversationViewModel: ObservableObject {
}
}
func getParticipantConversationModel() {
coreContext.doOnCoreQueue { _ in
if self.displayedConversation != nil {
self.displayedConversation!.chatRoom.participants.forEach { participant in
if participant.address != nil {
let avatarModelTmp = ContactAvatarModel.getAvatarModelFromAddress(address: participant.address!)
DispatchQueue.main.async {
self.participantConversationModel.append(avatarModelTmp)
}
}
}
}
}
}
func getMessages() {
self.getHistorySize()
self.getUnreadMessagesCount()
self.getParticipantConversationModel()
coreContext.doOnCoreQueue { _ in
if self.displayedConversation != nil {
let historyEvents = self.displayedConversation!.chatRoom.getHistoryRangeEvents(begin: 0, end: 30)
@ -121,11 +138,30 @@ class ConversationViewModel: ObservableObject {
}
}
conversationMessage.append(Message(
id: UUID().uuidString,
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
text: contentText,
attachments: attachmentList))
let addressPrecCleaned = index > 0 ? historyEvents[index - 1].chatMessage?.fromAddress?.clone() : eventLog.chatMessage?.fromAddress?.clone()
addressPrecCleaned?.clean()
let addressNextCleaned = index <= historyEvents.count - 2 ? historyEvents[index + 1].chatMessage?.fromAddress?.clone() : eventLog.chatMessage?.fromAddress?.clone()
addressNextCleaned?.clean()
let addressCleaned = eventLog.chatMessage?.fromAddress?.clone()
addressCleaned?.clean()
let isFirstMessageIncomingTmp = index > 0 ? addressPrecCleaned?.asStringUriOnly() != addressCleaned?.asStringUriOnly() : true
let isFirstMessageOutgoingTmp = index <= historyEvents.count - 2 ? addressNextCleaned?.asStringUriOnly() != addressCleaned?.asStringUriOnly() : true
let isFirstMessageTmp = (eventLog.chatMessage?.isOutgoing ?? false) ? isFirstMessageOutgoingTmp : isFirstMessageIncomingTmp
conversationMessage.append(
Message(
id: UUID().uuidString,
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
address: addressCleaned?.asStringUriOnly() ?? "",
isFirstMessage: isFirstMessageTmp,
text: contentText,
attachments: attachmentList
)
)
}
DispatchQueue.main.async {
@ -143,7 +179,7 @@ class ConversationViewModel: ObservableObject {
let historyEvents = self.displayedConversation!.chatRoom.getHistoryRangeEvents(begin: self.conversationMessagesSection[0].rows.count, end: self.conversationMessagesSection[0].rows.count + 30)
var conversationMessagesTmp: [Message] = []
historyEvents.reversed().forEach { eventLog in
historyEvents.enumerated().reversed().forEach { index, eventLog in
let attachmentList: [Attachment] = []
var contentText = ""
@ -155,10 +191,26 @@ class ConversationViewModel: ObservableObject {
}
}
let addressPrecCleaned = index > 0 ? historyEvents[index - 1].chatMessage?.fromAddress?.clone() : eventLog.chatMessage?.fromAddress?.clone()
addressPrecCleaned?.clean()
let addressNextCleaned = index <= historyEvents.count - 2 ? historyEvents[index + 1].chatMessage?.fromAddress?.clone() : eventLog.chatMessage?.fromAddress?.clone()
addressNextCleaned?.clean()
let addressCleaned = eventLog.chatMessage?.fromAddress?.clone()
addressCleaned?.clean()
let isFirstMessageIncomingTmp = index > 0 ? addressPrecCleaned?.asStringUriOnly() != addressCleaned?.asStringUriOnly() : true
let isFirstMessageOutgoingTmp = index <= historyEvents.count - 2 ? addressNextCleaned?.asStringUriOnly() != addressCleaned?.asStringUriOnly() : true
let isFirstMessageTmp = (eventLog.chatMessage?.isOutgoing ?? false) ? isFirstMessageOutgoingTmp : isFirstMessageIncomingTmp
conversationMessagesTmp.insert(
Message(
id: UUID().uuidString,
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
address: addressCleaned?.asStringUriOnly() ?? "",
isFirstMessage: isFirstMessageTmp,
text: contentText,
attachments: attachmentList
), at: 0
@ -167,6 +219,9 @@ class ConversationViewModel: ObservableObject {
if !conversationMessagesTmp.isEmpty {
DispatchQueue.main.async {
if self.conversationMessagesSection[0].rows.last?.address == conversationMessagesTmp.last?.address {
self.conversationMessagesSection[0].rows[self.conversationMessagesSection[0].rows.count - 1].isFirstMessage = false
}
self.conversationMessagesSection[0].rows.append(contentsOf: conversationMessagesTmp.reversed())
}
}
@ -175,7 +230,6 @@ class ConversationViewModel: ObservableObject {
}
func getNewMessages(eventLogs: [EventLog]) {
var conversationMessage: [Message] = []
eventLogs.enumerated().forEach { index, eventLog in
var attachmentList: [Attachment] = []
var contentText = ""
@ -197,14 +251,50 @@ class ConversationViewModel: ObservableObject {
}
}
let addressPrecCleaned = index > 0 ? eventLogs[index - 1].chatMessage?.fromAddress?.clone() : eventLog.chatMessage?.fromAddress?.clone()
addressPrecCleaned?.clean()
let addressNextCleaned = index <= eventLogs.count - 2 ? eventLogs[index + 1].chatMessage?.fromAddress?.clone() : eventLog.chatMessage?.fromAddress?.clone()
addressNextCleaned?.clean()
let addressCleaned = eventLog.chatMessage?.fromAddress?.clone()
addressCleaned?.clean()
let isFirstMessageIncomingTmp = index > 0
? addressPrecCleaned?.asStringUriOnly() != addressCleaned?.asStringUriOnly()
: (
self.conversationMessagesSection.isEmpty || self.conversationMessagesSection[0].rows.isEmpty
? true
: self.conversationMessagesSection[0].rows[0].address != addressCleaned?.asStringUriOnly()
)
let isFirstMessageOutgoingTmp = index <= eventLogs.count - 2
? addressNextCleaned?.asStringUriOnly() == addressCleaned?.asStringUriOnly()
: (
self.conversationMessagesSection.isEmpty || self.conversationMessagesSection[0].rows.isEmpty
? true
: !self.conversationMessagesSection[0].rows[0].isOutgoing || self.conversationMessagesSection[0].rows[0].address == addressCleaned?.asStringUriOnly()
)
let isFirstMessageTmp = (eventLog.chatMessage?.isOutgoing ?? false) ? isFirstMessageOutgoingTmp : isFirstMessageIncomingTmp
let message = Message(
id: UUID().uuidString,
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
address: addressCleaned?.asStringUriOnly() ?? "",
isFirstMessage: isFirstMessageTmp,
text: contentText,
attachments: attachmentList
)
DispatchQueue.main.async {
if !self.conversationMessagesSection.isEmpty
&& !self.conversationMessagesSection[0].rows.isEmpty
&& self.conversationMessagesSection[0].rows[0].isOutgoing
&& (self.conversationMessagesSection[0].rows[0].address == message.address) {
self.conversationMessagesSection[0].rows[0].isFirstMessage = false
}
if self.conversationMessagesSection.isEmpty {
self.conversationMessagesSection.append(MessagesSection(date: Date(), rows: [message]))
} else {

View file

@ -628,7 +628,7 @@ struct HistoryContactFragment: View {
#Preview {
HistoryContactFragment(
contactAvatarModel: ContactAvatarModel(friend: nil, name: "", withPresence: false),
contactAvatarModel: ContactAvatarModel(friend: nil, name: "", address: "", withPresence: false),
historyViewModel: HistoryViewModel(),
historyListViewModel: HistoryListViewModel(),
contactViewModel: ContactViewModel(),

View file

@ -49,7 +49,7 @@ struct HistoryListFragment: View {
&& $0.friend!.name == addressFriend!.name
&& $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly()
})
: ContactAvatarModel(friend: nil, name: "", withPresence: false)
: ContactAvatarModel(friend: nil, name: "", address: "", withPresence: false)
if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty {
if contactAvatarModel != nil {

View file

@ -86,7 +86,14 @@ final class MagicSearchSingleton: ObservableObject {
self.contactsManager.lastSearch.forEach { searchResult in
if searchResult.friend != nil {
self.contactsManager.avatarListModel.append(ContactAvatarModel(friend: searchResult.friend!, name: searchResult.friend?.name ?? "", withPresence: true))
self.contactsManager.avatarListModel.append(
ContactAvatarModel(
friend: searchResult.friend!,
name: searchResult.friend?.name ?? "",
address: searchResult.friend?.address?.clone()?.asStringUriOnly() ?? "",
withPresence: true
)
)
}
}