diff --git a/Linphone.xcodeproj/project.pbxproj b/Linphone.xcodeproj/project.pbxproj index ef1a0b262..43bab449b 100644 --- a/Linphone.xcodeproj/project.pbxproj +++ b/Linphone.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 66C491FF2B24D4AC00CEA16D /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FE2B24D4AC00CEA16D /* FileUtils.swift */; }; 66C492012B24DB6900CEA16D /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C492002B24DB6900CEA16D /* Log.swift */; }; D706BA822ADD72D100278F45 /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D706BA812ADD72D100278F45 /* DeviceRotationViewModifier.swift */; }; + D70959F12B8DF3EC0014AC0B /* ConversationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D70959F02B8DF3EC0014AC0B /* ConversationModel.swift */; }; D70A26EE2B7CF60B006CC8FC /* ConversationsListBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D70A26ED2B7CF60B006CC8FC /* ConversationsListBottomSheet.swift */; }; D70A26F02B7D02E6006CC8FC /* ConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D70A26EF2B7D02E6006CC8FC /* ConversationViewModel.swift */; }; D70A26F22B7F5D95006CC8FC /* ConversationFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D70A26F12B7F5D95006CC8FC /* ConversationFragment.swift */; }; @@ -118,6 +119,7 @@ 66C491FE2B24D4AC00CEA16D /* FileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = ""; }; 66C492002B24DB6900CEA16D /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = ""; }; D706BA812ADD72D100278F45 /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = ""; }; + D70959F02B8DF3EC0014AC0B /* ConversationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationModel.swift; sourceTree = ""; }; D70A26ED2B7CF60B006CC8FC /* ConversationsListBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationsListBottomSheet.swift; sourceTree = ""; }; D70A26EF2B7D02E6006CC8FC /* ConversationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationViewModel.swift; sourceTree = ""; }; D70A26F12B7F5D95006CC8FC /* ConversationFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationFragment.swift; sourceTree = ""; }; @@ -252,6 +254,14 @@ path = Pods; sourceTree = ""; }; + D70959EF2B8DF33B0014AC0B /* Model */ = { + isa = PBXGroup; + children = ( + D70959F02B8DF3EC0014AC0B /* ConversationModel.swift */, + ); + path = Model; + sourceTree = ""; + }; D717071C2AC591EF0037746F /* Utils */ = { isa = PBXGroup; children = ( @@ -542,6 +552,7 @@ isa = PBXGroup; children = ( D7CEE0392B7A232200FD79B7 /* Fragments */, + D70959EF2B8DF33B0014AC0B /* Model */, D7CEE0362B7A212C00FD79B7 /* ViewModel */, D7CEE0342B7A210300FD79B7 /* ConversationsView.swift */, ); @@ -743,6 +754,7 @@ D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */, D70A26F22B7F5D95006CC8FC /* ConversationFragment.swift in Sources */, 66C491FD2B24D36500CEA16D /* AudioRouteUtils.swift in Sources */, + D70959F12B8DF3EC0014AC0B /* ConversationModel.swift in Sources */, D7EAACCF2AD6ED8000AA6A8A /* PermissionsFragment.swift in Sources */, D777DBB32AE12C5900565A99 /* ContactsManager.swift in Sources */, D796F2002B0BB61A0041115F /* ToastViewModel.swift in Sources */, diff --git a/Linphone/UI/Call/CallView.swift b/Linphone/UI/Call/CallView.swift index 0a587abae..d198a1a7e 100644 --- a/Linphone/UI/Call/CallView.swift +++ b/Linphone/UI/Call/CallView.swift @@ -380,7 +380,7 @@ struct CallView: View { && $0.friend!.name == addressFriend!.name && $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly() }) - : ContactAvatarModel(friend: nil, withPresence: false) + : ContactAvatarModel(friend: nil, name: "", withPresence: false) if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty { if contactAvatarModel != nil { diff --git a/Linphone/UI/Call/Fragments/CallsListFragment.swift b/Linphone/UI/Call/Fragments/CallsListFragment.swift index 1d2d76aea..83abbd81e 100644 --- a/Linphone/UI/Call/Fragments/CallsListFragment.swift +++ b/Linphone/UI/Call/Fragments/CallsListFragment.swift @@ -226,7 +226,7 @@ struct CallsListFragment: View { && $0.friend!.name == addressFriend!.name && $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly() }) - : ContactAvatarModel(friend: nil, withPresence: false) + : ContactAvatarModel(friend: nil, name: "", withPresence: false) if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty { if contactAvatarModel != nil { diff --git a/Linphone/UI/Main/Contacts/Fragments/ContactInnerFragment.swift b/Linphone/UI/Main/Contacts/Fragments/ContactInnerFragment.swift index aa8ac1e27..5dd1b1787 100644 --- a/Linphone/UI/Main/Contacts/Fragments/ContactInnerFragment.swift +++ b/Linphone/UI/Main/Contacts/Fragments/ContactInnerFragment.swift @@ -286,7 +286,7 @@ struct ContactInnerFragment: View { #Preview { ContactInnerFragment( - contactAvatarModel: ContactAvatarModel(friend: nil, withPresence: true), + contactAvatarModel: ContactAvatarModel(friend: nil, name: "", withPresence: true), contactViewModel: ContactViewModel(), editContactViewModel: EditContactViewModel(), isShowDeletePopup: .constant(false), diff --git a/Linphone/UI/Main/Contacts/Fragments/EditContactFragment.swift b/Linphone/UI/Main/Contacts/Fragments/EditContactFragment.swift index 92c4ed7ff..38cc9e87c 100644 --- a/Linphone/UI/Main/Contacts/Fragments/EditContactFragment.swift +++ b/Linphone/UI/Main/Contacts/Fragments/EditContactFragment.swift @@ -154,7 +154,9 @@ struct EditContactFragment: View { && editContactViewModel.selectedEditFriend!.photo != nil && !editContactViewModel.selectedEditFriend!.photo!.isEmpty && selectedImage == nil && !removedImage { - Avatar(contactAvatarModel: ContactAvatarModel(friend: editContactViewModel.selectedEditFriend!, withPresence: false), avatarSize: 100) + Avatar(contactAvatarModel: + ContactAvatarModel(friend: editContactViewModel.selectedEditFriend!, name: editContactViewModel.selectedEditFriend?.name ?? "", withPresence: false), avatarSize: 100 + ) } else if selectedImage == nil { Image("profil-picture-default") diff --git a/Linphone/UI/Main/Contacts/Model/ContactAvatarModel.swift b/Linphone/UI/Main/Contacts/Model/ContactAvatarModel.swift index efd2e1426..950e431c6 100644 --- a/Linphone/UI/Main/Contacts/Model/ContactAvatarModel.swift +++ b/Linphone/UI/Main/Contacts/Model/ContactAvatarModel.swift @@ -25,6 +25,8 @@ class ContactAvatarModel: ObservableObject { let friend: Friend? + let name: String + let withPresence: Bool? @Published var lastPresenceInfo: String @@ -33,8 +35,9 @@ class ContactAvatarModel: ObservableObject { private var friendSuscription: AnyCancellable? - init(friend: Friend?, withPresence: Bool?) { + init(friend: Friend?, name: String, withPresence: Bool?) { self.friend = friend + self.name = name self.withPresence = withPresence if friend != nil && withPresence == true { diff --git a/Linphone/UI/Main/ContentView.swift b/Linphone/UI/Main/ContentView.swift index 195f43d48..eaa9ed2f9 100644 --- a/Linphone/UI/Main/ContentView.swift +++ b/Linphone/UI/Main/ContentView.swift @@ -62,6 +62,9 @@ struct ContentView: View { @State var isShowCallsListFragment = false var body: some View { + let pub = NotificationCenter.default + .publisher(for: NSNotification.Name("ContactLoaded")) + GeometryReader { geometry in VStack(spacing: 0) { if telecomManager.callInProgress && !fullscreenVideo && ((!telecomManager.callDisplayed && callViewModel.calls.count == 1) || callViewModel.calls.count > 1) { @@ -644,7 +647,7 @@ struct ContentView: View { && $0.friend!.name == addressFriend!.name && $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly() }) - : ContactAvatarModel(friend: nil, withPresence: false) + : ContactAvatarModel(friend: nil, name: "", withPresence: false) if contactAvatarModel != nil { HistoryContactFragment( @@ -866,6 +869,12 @@ struct ContentView: View { .zIndex(3) } } + .onAppear { + MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue) + } + .onReceive(pub) { _ in + conversationsListViewModel.refreshContactAvatarModel() + } } .overlay { if isMenuOpen { diff --git a/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift b/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift index 95e1c90b4..05df3a93f 100644 --- a/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift +++ b/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift @@ -60,99 +60,14 @@ struct ConversationFragment: View { } } - let addressFriend = - (conversationViewModel.displayedConversation!.participants.first != nil && conversationViewModel.displayedConversation!.participants.first!.address != nil) - ? contactsManager.getFriendWithAddress(address: conversationViewModel.displayedConversation!.participants.first!.address!) - : nil - - let contactAvatarModel = addressFriend != nil - ? ContactsManager.shared.avatarListModel.first(where: { - ($0.friend!.consolidatedPresence == .Online || $0.friend!.consolidatedPresence == .Busy) - && $0.friend!.name == addressFriend!.name - && $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly() - }) - : ContactAvatarModel(friend: nil, withPresence: false) - - if LinphoneUtils.isChatRoomAGroup(chatRoom: conversationViewModel.displayedConversation!) { - Image(uiImage: contactsManager.textToImage( - firstName: conversationViewModel.displayedConversation!.subject!, - lastName: conversationViewModel.displayedConversation!.subject!.components(separatedBy: " ").count > 1 - ? conversationViewModel.displayedConversation!.subject!.components(separatedBy: " ")[1] - : "")) - .resizable() - .frame(width: 50, height: 50) - .clipShape(Circle()) + Avatar(contactAvatarModel: conversationViewModel.displayedConversation!.avatarModel, avatarSize: 50) .padding(.top, 4) - } else if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty { - if contactAvatarModel != nil { - Avatar(contactAvatarModel: contactAvatarModel!, avatarSize: 50) - .padding(.top, 4) - } else { - Image("profil-picture-default") - .resizable() - .frame(width: 50, height: 50) - .clipShape(Circle()) - .padding(.top, 4) - } - } else { - if conversationViewModel.displayedConversation!.participants.first != nil - && conversationViewModel.displayedConversation!.participants.first!.address != nil { - if conversationViewModel.displayedConversation!.participants.first!.address!.displayName != nil { - Image(uiImage: contactsManager.textToImage( - firstName: conversationViewModel.displayedConversation!.participants.first!.address!.displayName!, - lastName: conversationViewModel.displayedConversation!.participants.first!.address!.displayName!.components(separatedBy: " ").count > 1 - ? conversationViewModel.displayedConversation!.participants.first!.address!.displayName!.components(separatedBy: " ")[1] - : "")) - .resizable() - .frame(width: 50, height: 50) - .clipShape(Circle()) - .padding(.top, 4) - - } else { - Image(uiImage: contactsManager.textToImage( - firstName: conversationViewModel.displayedConversation!.participants.first!.address!.username ?? "Username Error", - lastName: conversationViewModel.displayedConversation!.participants.first!.address!.username!.components(separatedBy: " ").count > 1 - ? conversationViewModel.displayedConversation!.participants.first!.address!.username!.components(separatedBy: " ")[1] - : "")) - .resizable() - .frame(width: 50, height: 50) - .clipShape(Circle()) - .padding(.top, 4) - } - - } else { - Image("profil-picture-default") - .resizable() - .frame(width: 50, height: 50) - .clipShape(Circle()) - .padding(.top, 4) - } - } - if LinphoneUtils.isChatRoomAGroup(chatRoom: conversationViewModel.displayedConversation!) { - Text(conversationViewModel.displayedConversation!.subject ?? "No Subject") - .default_text_style(styleSize: 16) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.top, 4) - .lineLimit(1) - } else if addressFriend != nil { - Text(addressFriend!.name!) - .default_text_style(styleSize: 16) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.top, 4) - .lineLimit(1) - } else { - if conversationViewModel.displayedConversation!.participants.first != nil - && conversationViewModel.displayedConversation!.participants.first!.address != nil { - Text(conversationViewModel.displayedConversation!.participants.first!.address!.displayName != nil - ? conversationViewModel.displayedConversation!.participants.first!.address!.displayName! - : conversationViewModel.displayedConversation!.participants.first!.address!.username!) - .default_text_style(styleSize: 16) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.top, 4) - .lineLimit(1) - } - } + Text(conversationViewModel.displayedConversation!.subject) + .default_text_style(styleSize: 16) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.top, 4) + .lineLimit(1) Spacer() @@ -238,7 +153,7 @@ struct ConversationFragment: View { .scaleEffect(x: 1, y: -1, anchor: .center) .listRowInsets(EdgeInsets(top: 2, leading: 10, bottom: 2, trailing: 10)) .listRowSeparator(.hidden) - .transition(.move(edge: .top)) + .transition(.move(edge: .top)) } } .scaleEffect(x: 1, y: -1, anchor: .center) diff --git a/Linphone/UI/Main/Conversations/Fragments/ConversationsListBottomSheet.swift b/Linphone/UI/Main/Conversations/Fragments/ConversationsListBottomSheet.swift index c51310d32..14a81e55e 100644 --- a/Linphone/UI/Main/Conversations/Fragments/ConversationsListBottomSheet.swift +++ b/Linphone/UI/Main/Conversations/Fragments/ConversationsListBottomSheet.swift @@ -97,7 +97,7 @@ struct ConversationsListBottomSheet: View { Button { if conversationsListViewModel.selectedConversation != nil { conversationsListViewModel.objectWillChange.send() - conversationsListViewModel.selectedConversation!.muted.toggle() + conversationsListViewModel.selectedConversation!.toggleMute() } if #available(iOS 16.0, *) { @@ -113,13 +113,13 @@ struct ConversationsListBottomSheet: View { } } label: { HStack { - Image(conversationsListViewModel.selectedConversation!.muted ? "bell" : "bell-slash") + Image(conversationsListViewModel.selectedConversation!.isMuted ? "bell" : "bell-slash") .renderingMode(.template) .resizable() .foregroundStyle(Color.grayMain2c500) .frame(width: 25, height: 25, alignment: .leading) .padding(.all, 10) - Text(conversationsListViewModel.selectedConversation!.muted ? "Réactiver les notifications" : "Mettre en sourdine") + Text(conversationsListViewModel.selectedConversation!.isMuted ? "Réactiver les notifications" : "Mettre en sourdine") .default_text_style(styleSize: 16) Spacer() } @@ -134,12 +134,10 @@ struct ConversationsListBottomSheet: View { .frame(maxWidth: .infinity) if conversationsListViewModel.selectedConversation != nil - && conversationsListViewModel.selectedConversation!.hasCapability(mask: ChatRoom.Capabilities.OneToOne.rawValue) { + && !conversationsListViewModel.selectedConversation!.isGroup { Button { - if conversationsListViewModel.selectedConversation!.participants.first != nil { - TelecomManager.shared.doCallWithCore( - addr: conversationsListViewModel.selectedConversation!.participants.first!.address!, isVideo: false - ) + if !conversationsListViewModel.selectedConversation!.isGroup { + conversationsListViewModel.selectedConversation!.call() } if #available(iOS 16.0, *) { @@ -178,12 +176,7 @@ struct ConversationsListBottomSheet: View { } Button { - if conversationsListViewModel.selectedConversation != nil { - CoreContext.shared.doOnCoreQueue { core in - core.deleteChatRoom(chatRoom: conversationsListViewModel.selectedConversation!) - //conversationsListViewModel.computeChatRoomsList(filter: "") - } - } + conversationsListViewModel.computeChatRoomsList(filter: "") if #available(iOS 16.0, *) { if idiom != .pad { diff --git a/Linphone/UI/Main/Conversations/Fragments/ConversationsListFragment.swift b/Linphone/UI/Main/Conversations/Fragments/ConversationsListFragment.swift index 6f51b17a4..ee2ba6a2c 100644 --- a/Linphone/UI/Main/Conversations/Fragments/ConversationsListFragment.swift +++ b/Linphone/UI/Main/Conversations/Fragments/ConversationsListFragment.swift @@ -22,8 +22,6 @@ import linphonesw struct ConversationsListFragment: View { - @ObservedObject var contactsManager = ContactsManager.shared - @ObservedObject var conversationViewModel: ConversationViewModel @ObservedObject var conversationsListViewModel: ConversationsListViewModel @@ -34,111 +32,21 @@ struct ConversationsListFragment: View { List { ForEach(0.. 1 - ? conversationsListViewModel.conversationsList[index].subject!.components(separatedBy: " ")[1] - : "")) - .resizable() - .frame(width: 50, height: 50) - .clipShape(Circle()) - } else if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty { - if contactAvatarModel != nil { - Avatar(contactAvatarModel: contactAvatarModel!, avatarSize: 50) - } else { - Image("profil-picture-default") - .resizable() - .frame(width: 50, height: 50) - .clipShape(Circle()) - } - } else { - if conversationsListViewModel.conversationsList[index].participants.first != nil - && conversationsListViewModel.conversationsList[index].participants.first!.address != nil { - if conversationsListViewModel.conversationsList[index].participants.first!.address!.displayName != nil { - Image(uiImage: contactsManager.textToImage( - firstName: conversationsListViewModel.conversationsList[index].participants.first!.address!.displayName!, - lastName: conversationsListViewModel.conversationsList[index].participants.first!.address!.displayName!.components(separatedBy: " ").count > 1 - ? conversationsListViewModel.conversationsList[index].participants.first!.address!.displayName!.components(separatedBy: " ")[1] - : "")) - .resizable() - .frame(width: 50, height: 50) - .clipShape(Circle()) - - } else { - Image(uiImage: contactsManager.textToImage( - firstName: conversationsListViewModel.conversationsList[index].participants.first!.address!.username ?? "Username Error", - lastName: conversationsListViewModel.conversationsList[index].participants.first!.address!.username!.components(separatedBy: " ").count > 1 - ? conversationsListViewModel.conversationsList[index].participants.first!.address!.username!.components(separatedBy: " ")[1] - : "")) - .resizable() - .frame(width: 50, height: 50) - .clipShape(Circle()) - } - - } else { - Image("profil-picture-default") - .resizable() - .frame(width: 50, height: 50) - .clipShape(Circle()) - } - } + Avatar(contactAvatarModel: conversationsListViewModel.conversationsList[index].avatarModel, avatarSize: 50) VStack(spacing: 0) { Spacer() - - if LinphoneUtils.isChatRoomAGroup(chatRoom: conversationsListViewModel.conversationsList[index]) { - Text(conversationsListViewModel.conversationsList[index].subject ?? "No Subject") - .foregroundStyle(Color.grayMain2c800) - .if(conversationsListViewModel.conversationsList[index].unreadMessagesCount > 0) { view in - view.default_text_style_700(styleSize: 14) - } - .default_text_style(styleSize: 14) - .frame(maxWidth: .infinity, alignment: .leading) - .lineLimit(1) - } else if addressFriend != nil { - Text(addressFriend!.name!) - .foregroundStyle(Color.grayMain2c800) - .if(conversationsListViewModel.conversationsList[index].unreadMessagesCount > 0) { view in - view.default_text_style_700(styleSize: 14) - } - .default_text_style(styleSize: 14) - .frame(maxWidth: .infinity, alignment: .leading) - .lineLimit(1) - } else { - if conversationsListViewModel.conversationsList[index].participants.first != nil - && conversationsListViewModel.conversationsList[index].participants.first!.address != nil { - Text(conversationsListViewModel.conversationsList[index].participants.first!.address!.displayName != nil - ? conversationsListViewModel.conversationsList[index].participants.first!.address!.displayName! - : conversationsListViewModel.conversationsList[index].participants.first!.address!.username!) - .foregroundStyle(Color.grayMain2c800) - .if(conversationsListViewModel.conversationsList[index].unreadMessagesCount > 0) { view in - view.default_text_style_700(styleSize: 14) - } - .default_text_style(styleSize: 14) - .frame(maxWidth: .infinity, alignment: .leading) - .lineLimit(1) + + Text(conversationsListViewModel.conversationsList[index].subject) + .foregroundStyle(Color.grayMain2c800) + .if(conversationsListViewModel.conversationsList[index].unreadMessagesCount > 0) { view in + view.default_text_style_700(styleSize: 14) } - } + .default_text_style(styleSize: 14) + .frame(maxWidth: .infinity, alignment: .leading) + .lineLimit(1) - Text( - conversationsListViewModel.conversationsList[index].lastMessageInHistory != nil - ? conversationsListViewModel.getContentTextMessage(message: conversationsListViewModel.conversationsList[index].lastMessageInHistory!) - : "" - ) + Text(conversationsListViewModel.conversationsList[index].lastMessageText) .foregroundStyle(Color.grayMain2c400) .if(conversationsListViewModel.conversationsList[index].unreadMessagesCount > 0) { view in view.default_text_style_700(styleSize: 14) @@ -156,8 +64,7 @@ struct ConversationsListFragment: View { Spacer() HStack { - if conversationsListViewModel.conversationsList[index].currentParams != nil - && !conversationsListViewModel.conversationsList[index].currentParams!.encryptionEnabled { + if !conversationsListViewModel.conversationsList[index].encryptionEnabled { Image("warning-circle") .renderingMode(.template) .resizable() @@ -174,15 +81,15 @@ struct ConversationsListFragment: View { Spacer() HStack { - if conversationsListViewModel.conversationsList[index].muted == false - && !(conversationsListViewModel.conversationsList[index].lastMessageInHistory != nil - && conversationsListViewModel.conversationsList[index].lastMessageInHistory!.isOutgoing == true) + if conversationsListViewModel.conversationsList[index].isMuted == false + && !(!conversationsListViewModel.conversationsList[index].lastMessageText.isEmpty + && conversationsListViewModel.conversationsList[index].lastMessageIsOutgoing == true) && conversationsListViewModel.conversationsList[index].unreadMessagesCount == 0 { Text("") .frame(width: 18, height: 18, alignment: .trailing) } - if conversationsListViewModel.conversationsList[index].muted { + if conversationsListViewModel.conversationsList[index].isMuted { Image("bell-slash") .renderingMode(.template) .resizable() @@ -190,9 +97,9 @@ struct ConversationsListFragment: View { .frame(width: 18, height: 18, alignment: .trailing) } - if conversationsListViewModel.conversationsList[index].lastMessageInHistory != nil - && conversationsListViewModel.conversationsList[index].lastMessageInHistory!.isOutgoing == true { - let imageName = LinphoneUtils.getChatIconState(chatState: conversationsListViewModel.conversationsList[index].lastMessageInHistory!.state) + if !conversationsListViewModel.conversationsList[index].lastMessageText.isEmpty + && conversationsListViewModel.conversationsList[index].lastMessageIsOutgoing == true { + let imageName = LinphoneUtils.getChatIconState(chatState: conversationsListViewModel.conversationsList[index].lastMessageState) Image(imageName) .renderingMode(.template) .resizable() @@ -220,7 +127,6 @@ struct ConversationsListFragment: View { Spacer() } .padding(.trailing, 10) - } } .buttonStyle(.borderless) .listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 20)) @@ -228,7 +134,7 @@ struct ConversationsListFragment: View { .background(.white) .onTapGesture { withAnimation { - conversationViewModel.displayedConversation = conversationsListViewModel.conversationsList[index] + conversationViewModel.changeDisplayedChatRoom(conversationModel: conversationsListViewModel.conversationsList[index]) conversationViewModel.getMessage() } } diff --git a/Linphone/UI/Main/Conversations/Model/ConversationModel.swift b/Linphone/UI/Main/Conversations/Model/ConversationModel.swift new file mode 100644 index 000000000..e04ce5b39 --- /dev/null +++ b/Linphone/UI/Main/Conversations/Model/ConversationModel.swift @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of Linphone + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import Foundation +import linphonesw +import Combine + +class ConversationModel: ObservableObject { + + private var coreContext = CoreContext.shared + private var contactsManager = ContactsManager.shared + + let chatRoom: ChatRoom + let isDisabledBecauseNotSecured: Bool = false + + static let TAG = "[Conversation Model]" + + let id: String + let localSipUri: String + let remoteSipUri: String + let isGroup: Bool + let isReadOnly: Bool + @Published var subject: String + @Published var isComposing: Bool + @Published var lastUpdateTime: time_t + //@Published var composingLabel: String + @Published var isMuted: Bool + @Published var isEphemeral: Bool + @Published var encryptionEnabled: Bool + @Published var lastMessageText: String + @Published var lastMessageIsOutgoing: Bool + @Published var lastMessageState: Int + //@Published var dateTime: String + @Published var unreadMessagesCount: Int + @Published var avatarModel: ContactAvatarModel + //@Published var isBeingDeleted: Bool + + //private let lastMessage: ChatMessage? = nil + + init(chatRoom: ChatRoom) { + self.chatRoom = chatRoom + + self.id = LinphoneUtils.getChatRoomId(room: chatRoom) + + self.localSipUri = chatRoom.localAddress?.asStringUriOnly() ?? "" + + self.remoteSipUri = chatRoom.peerAddress?.asStringUriOnly() ?? "" + + self.isGroup = !chatRoom.hasCapability(mask: ChatRoom.Capabilities.OneToOne.rawValue) && chatRoom.hasCapability(mask: ChatRoom.Capabilities.Conference.rawValue) + + self.isReadOnly = chatRoom.isReadOnly + + self.subject = chatRoom.subject ?? "" + + self.lastUpdateTime = chatRoom.lastUpdateTime + + self.isComposing = chatRoom.isRemoteComposing + + //self.composingLabel = chatRoom.compo + + self.isMuted = chatRoom.muted + + self.isEphemeral = chatRoom.ephemeralEnabled + + self.encryptionEnabled = chatRoom.currentParams != nil && chatRoom.currentParams!.encryptionEnabled + + self.lastMessageText = "" + + self.lastMessageIsOutgoing = false + + self.lastMessageState = 0 + + //self.dateTime = chatRoom.date + + self.unreadMessagesCount = chatRoom.unreadMessagesCount + + self.avatarModel = ContactAvatarModel(friend: nil, name: "", withPresence: false) + + //self.isBeingDeleted = MutableLiveData() + + //self.lastMessage: ChatMessage? = null + + getContentTextMessage() + getChatRoomSubject() + } + + func leave(){ + coreContext.doOnCoreQueue { _ in + self.chatRoom.leave() + } + } + + func markAsRead() { + coreContext.doOnCoreQueue { _ in + self.chatRoom.markAsRead() + } + } + + func toggleMute() { + coreContext.doOnCoreQueue { _ in + self.chatRoom.muted.toggle() + self.isMuted = self.chatRoom.muted + } + } + + func call() { + coreContext.doOnCoreQueue { _ in + if self.chatRoom.peerAddress != nil { + TelecomManager.shared.doCallWithCore( + addr: self.chatRoom.peerAddress!, isVideo: false + ) + } + } + } + + func getContentTextMessage() { + coreContext.doOnCoreQueue { _ in + let lastMessage = self.chatRoom.lastMessageInHistory + if lastMessage != nil { + var fromAddressFriend = lastMessage!.fromAddress != nil + ? self.contactsManager.getFriendWithAddress(address: lastMessage!.fromAddress!)?.name ?? nil + : nil + + if !lastMessage!.isOutgoing && lastMessage!.chatRoom != nil && !lastMessage!.chatRoom!.hasCapability(mask: ChatRoom.Capabilities.OneToOne.rawValue) { + if fromAddressFriend == nil { + if lastMessage!.fromAddress!.displayName != nil { + fromAddressFriend = lastMessage!.fromAddress!.displayName! + ": " + } else if lastMessage!.fromAddress!.username != nil { + fromAddressFriend = lastMessage!.fromAddress!.username! + ": " + } else { + fromAddressFriend = "" + } + } else { + fromAddressFriend! += ": " + } + + } else { + fromAddressFriend = nil + } + + let lastMessageTextTmp = (fromAddressFriend ?? "") + + (lastMessage!.contents.first(where: {$0.isText == true})?.utf8Text ?? (lastMessage!.contents.first(where: {$0.isFile == true || $0.isFileTransfer == true})?.name ?? "")) + + let lastMessageIsOutgoingTmp = lastMessage?.isOutgoing ?? false + + let lastMessageStateTmp = lastMessage?.state.rawValue ?? 0 + + DispatchQueue.main.async { + self.lastMessageText = lastMessageTextTmp + + self.lastMessageIsOutgoing = lastMessageIsOutgoingTmp + + self.lastMessageState = lastMessageStateTmp + } + } + } + } + + func getChatRoomSubject() { + coreContext.doOnCoreQueue { _ in + + let addressFriend = + (self.chatRoom.participants.first != nil && self.chatRoom.participants.first!.address != nil) + ? self.contactsManager.getFriendWithAddress(address: self.chatRoom.participants.first!.address!) + : nil + + if self.isGroup { + self.subject = self.chatRoom.subject! + } else if addressFriend != nil { + self.subject = addressFriend!.name! + } else { + if self.chatRoom.participants.first != nil + && self.chatRoom.participants.first!.address != nil { + + self.subject = self.chatRoom.participants.first!.address!.displayName != nil + ? self.chatRoom.participants.first!.address!.displayName! + : self.chatRoom.participants.first!.address!.username! + + } + } + + 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) + + DispatchQueue.main.async { + self.avatarModel = avatarModelTmp + } + } + } + + func refreshAvatarModel() { + coreContext.doOnCoreQueue { _ in + let addressFriend = + (self.chatRoom.participants.first != nil && self.chatRoom.participants.first!.address != nil) + ? self.contactsManager.getFriendWithAddress(address: self.chatRoom.participants.first!.address!) + : nil + + if addressFriend != nil && !self.isGroup { + let avatarModelTmp = 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) + + DispatchQueue.main.async { + self.avatarModel = avatarModelTmp + } + } + } + } +} diff --git a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift index e34c268e3..40005d702 100644 --- a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift +++ b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift @@ -26,7 +26,7 @@ class ConversationViewModel: ObservableObject { private var coreContext = CoreContext.shared - @Published var displayedConversation: ChatRoom? + @Published var displayedConversation: ConversationModel? @Published var messageText: String = "" @@ -37,14 +37,16 @@ class ConversationViewModel: ObservableObject { init() {} func addConversationDelegate() { - if displayedConversation != nil { - self.chatRoomSuscriptions.insert(displayedConversation!.publisher?.onChatMessageSent?.postOnMainQueue { (cbValue: (chatRoom: ChatRoom, eventLog: EventLog)) in - self.getNewMessages(eventLogs: [cbValue.eventLog]) - }) - - self.chatRoomSuscriptions.insert(displayedConversation!.publisher?.onChatMessagesReceived?.postOnMainQueue { (cbValue: (chatRoom: ChatRoom, eventLogs: [EventLog])) in - self.getNewMessages(eventLogs: cbValue.eventLogs) - }) + coreContext.doOnCoreQueue { _ in + if self.displayedConversation != nil { + self.chatRoomSuscriptions.insert(self.displayedConversation?.chatRoom.publisher?.onChatMessageSent?.postOnCoreQueue { (cbValue: (chatRoom: ChatRoom, eventLog: EventLog)) in + self.getNewMessages(eventLogs: [cbValue.eventLog]) + }) + + self.chatRoomSuscriptions.insert(self.displayedConversation?.chatRoom.publisher?.onChatMessagesReceived?.postOnMainQueue { (cbValue: (chatRoom: ChatRoom, eventLogs: [EventLog])) in + self.getNewMessages(eventLogs: cbValue.eventLogs) + }) + } } } @@ -53,20 +55,36 @@ class ConversationViewModel: ObservableObject { } func getMessage() { - if self.displayedConversation != nil { - let historyEvents = displayedConversation!.getHistoryRangeEvents(begin: conversationMessagesList.count, end: conversationMessagesList.count + 30) - - historyEvents.reversed().forEach { eventLog in - conversationMessagesList.append(LinphoneCustomEventLog(eventLog: eventLog)) + coreContext.doOnCoreQueue { _ in + if self.displayedConversation != nil { + let historyEvents = self.displayedConversation!.chatRoom.getHistoryRangeEvents(begin: self.conversationMessagesList.count, end: self.conversationMessagesList.count + 30) + + historyEvents.reversed().forEach { eventLog in + DispatchQueue.main.async { + self.conversationMessagesList.append(LinphoneCustomEventLog(eventLog: eventLog)) + } + } } } } func getNewMessages(eventLogs: [EventLog]) { - withAnimation { - eventLogs.forEach { eventLog in - conversationMessagesList.insert(LinphoneCustomEventLog(eventLog: eventLog), at: 0) - //conversationMessagesList.append(LinphoneCustomEventLog(eventLog: eventLog)) + + //let conversationMessagesListTmp = self.conversationMessagesList + //self.conversationMessagesList = [] + + eventLogs.forEach { eventLog in + DispatchQueue.main.async { + /* + withAnimation { + self.conversationMessagesList.append(LinphoneCustomEventLog(eventLog: eventLog)) + } + + self.conversationMessagesList.append(contentsOf: conversationMessagesListTmp) + */ + withAnimation(.spring(duration: 2)) { + self.conversationMessagesList.insert(LinphoneCustomEventLog(eventLog: eventLog), at: 0) + } } } } @@ -76,83 +94,93 @@ class ConversationViewModel: ObservableObject { } func sendMessage() { - //val messageToReplyTo = chatMessageToReplyTo - //val message = if (messageToReplyTo != null) { + coreContext.doOnCoreQueue { _ in + //val messageToReplyTo = chatMessageToReplyTo + //val message = if (messageToReplyTo != null) { //Log.i("$TAG Sending message as reply to [${messageToReplyTo.messageId}]") //chatRoom.createReplyMessage(messageToReplyTo) - //} else { - let message = try? self.displayedConversation!.createEmptyMessage() - //} - - let toSend = self.messageText.trimmingCharacters(in: .whitespacesAndNewlines) - if !toSend.isEmpty { - if message != nil { - message!.addUtf8TextContent(text: toSend) - } - } - - /* - if (isVoiceRecording.value == true && voiceMessageRecorder.file != null) { - stopVoiceRecorder() - val content = voiceMessageRecorder.createContent() - if (content != null) { - Log.i( - "$TAG Voice recording content created, file name is ${content.name} and duration is ${content.fileDuration}" - ) - message.addContent(content) - } else { - Log.e("$TAG Voice recording content couldn't be created!") - } - } else { - for (attachment in attachments.value.orEmpty()) { - val content = Factory.instance().createContent() - - content.type = when (attachment.mimeType) { - FileUtils.MimeType.Image -> "image" - FileUtils.MimeType.Audio -> "audio" - FileUtils.MimeType.Video -> "video" - FileUtils.MimeType.Pdf -> "application" - FileUtils.MimeType.PlainText -> "text" - else -> "file" + //} else { + let message = try? self.displayedConversation!.chatRoom.createEmptyMessage() + //} + + let toSend = self.messageText.trimmingCharacters(in: .whitespacesAndNewlines) + if !toSend.isEmpty { + if message != nil { + message!.addUtf8TextContent(text: toSend) } - content.subtype = if (attachment.mimeType == FileUtils.MimeType.PlainText) { - "plain" - } else { - FileUtils.getExtensionFromFileName(attachment.fileName) - } - content.name = attachment.fileName - // Let the file body handler take care of the upload - content.filePath = attachment.file - - message.addFileContent(content) } + + /* + if (isVoiceRecording.value == true && voiceMessageRecorder.file != null) { + stopVoiceRecorder() + val content = voiceMessageRecorder.createContent() + if (content != null) { + Log.i( + "$TAG Voice recording content created, file name is ${content.name} and duration is ${content.fileDuration}" + ) + message.addContent(content) + } else { + Log.e("$TAG Voice recording content couldn't be created!") + } + } else { + for (attachment in attachments.value.orEmpty()) { + val content = Factory.instance().createContent() + + content.type = when (attachment.mimeType) { + FileUtils.MimeType.Image -> "image" + FileUtils.MimeType.Audio -> "audio" + FileUtils.MimeType.Video -> "video" + FileUtils.MimeType.Pdf -> "application" + FileUtils.MimeType.PlainText -> "text" + else -> "file" + } + content.subtype = if (attachment.mimeType == FileUtils.MimeType.PlainText) { + "plain" + } else { + FileUtils.getExtensionFromFileName(attachment.fileName) + } + content.name = attachment.fileName + // Let the file body handler take care of the upload + content.filePath = attachment.file + + message.addFileContent(content) + } + } + */ + + if message != nil && !message!.contents.isEmpty { + Log.info("[ConversationViewModel] Sending message") + message!.send() + } + + Log.info("[ConversationViewModel] Message sent, re-setting defaults") + + DispatchQueue.main.async { + self.messageText = "" + } + + /* + isReplying.postValue(false) + isFileAttachmentsListOpen.postValue(false) + isParticipantsListOpen.postValue(false) + isEmojiPickerOpen.postValue(false) + + if (::voiceMessageRecorder.isInitialized) { + stopVoiceRecorder() + } + isVoiceRecording.postValue(false) + + // Warning: do not delete files + val attachmentsList = arrayListOf() + attachments.postValue(attachmentsList) + + chatMessageToReplyTo = null + */ } - */ - - if message != nil && !message!.contents.isEmpty { - Log.info("[ConversationViewModel] Sending message") - message!.send() - } - - Log.info("[ConversationViewModel] Message sent, re-setting defaults") - self.messageText = "" - /* - isReplying.postValue(false) - isFileAttachmentsListOpen.postValue(false) - isParticipantsListOpen.postValue(false) - isEmojiPickerOpen.postValue(false) - - if (::voiceMessageRecorder.isInitialized) { - stopVoiceRecorder() - } - isVoiceRecording.postValue(false) - - // Warning: do not delete files - val attachmentsList = arrayListOf() - attachments.postValue(attachmentsList) - - chatMessageToReplyTo = null - */ + } + + func changeDisplayedChatRoom(conversationModel: ConversationModel) { + self.displayedConversation = conversationModel } } struct LinphoneCustomEventLog: Hashable { diff --git a/Linphone/UI/Main/Conversations/ViewModel/ConversationsListViewModel.swift b/Linphone/UI/Main/Conversations/ViewModel/ConversationsListViewModel.swift index f3a3c8da7..41553ff9b 100644 --- a/Linphone/UI/Main/Conversations/ViewModel/ConversationsListViewModel.swift +++ b/Linphone/UI/Main/Conversations/ViewModel/ConversationsListViewModel.swift @@ -28,10 +28,10 @@ class ConversationsListViewModel: ObservableObject { private var mCoreSuscriptions = Set() - @Published var conversationsList: [ChatRoom] = [] + @Published var conversationsList: [ConversationModel] = [] @Published var unreadMessages: Int = 0 - var selectedConversation: ChatRoom? + var selectedConversation: ConversationModel? init() { computeChatRoomsList(filter: "") @@ -43,47 +43,64 @@ class ConversationsListViewModel: ObservableObject { let account = core.defaultAccount let chatRooms = account?.chatRooms != nil ? account!.chatRooms : core.chatRooms - DispatchQueue.main.async { - self.conversationsList = [] - chatRooms.forEach { chatRoom in - //let disabledBecauseNotSecured = (account?.isInSecureMode() == true && !chatRoom.hasCapability) ? Capabilities.Encrypted.toInt() : 0 - if chatRoom.hasCapability(mask: ChatRoom.Capabilities.OneToOne.rawValue) { - } - - if filter.isEmpty { - //val model = ConversationModel(chatRoom, disabledBecauseNotSecured) - self.conversationsList.append(chatRoom) - } - /* - else { - val participants = chatRoom.participants - val found = participants.find { - // Search in address but also in contact name if exists - val model = - coreContext.contactsManager.getContactAvatarModelForAddress(it.address) - model.contactName?.contains( - filter, - ignoreCase = true - ) == true || it.address.asStringUriOnly().contains( - filter, - ignoreCase = true - ) - } - if ( - found != null || - chatRoom.peerAddress.asStringUriOnly().contains(filter, ignoreCase = true) || - chatRoom.subject.orEmpty().contains(filter, ignoreCase = true) - ) { - val model = ConversationModel(chatRoom, disabledBecauseNotSecured) - list.add(model) - count += 1 - } - } - */ + var conversationsListTmp: [ConversationModel] = [] + + chatRooms.forEach { chatRoom in + //let disabledBecauseNotSecured = (account?.isInSecureMode() == true && !chatRoom.hasCapability) ? Capabilities.Encrypted.toInt() : 0 + if chatRoom.hasCapability(mask: ChatRoom.Capabilities.OneToOne.rawValue) { } - self.updateUnreadMessagesCount() + if filter.isEmpty { + let model = ConversationModel(chatRoom: chatRoom) + conversationsListTmp.append(model) + } + /* + else { + val participants = chatRoom.participants + val found = participants.find { + // Search in address but also in contact name if exists + val model = + coreContext.contactsManager.getContactAvatarModelForAddress(it.address) + model.contactName?.contains( + filter, + ignoreCase = true + ) == true || it.address.asStringUriOnly().contains( + filter, + ignoreCase = true + ) + } + if ( + found != null || + chatRoom.peerAddress.asStringUriOnly().contains(filter, ignoreCase = true) || + chatRoom.subject.orEmpty().contains(filter, ignoreCase = true) + ) { + val model = ConversationModel(chatRoom, disabledBecauseNotSecured) + list.add(model) + count += 1 + } + } + */ } + + if !self.conversationsList.isEmpty { + for (index, element) in conversationsListTmp.enumerated() { + if index > 0 && element.id != self.conversationsList[index].id { + DispatchQueue.main.async { + self.conversationsList[index] = element + } + } + } + + DispatchQueue.main.async { + self.conversationsList[0] = conversationsListTmp.first! + } + } else { + DispatchQueue.main.async { + self.conversationsList = conversationsListTmp + } + } + + self.updateUnreadMessagesCount() } } @@ -93,7 +110,8 @@ class ConversationsListViewModel: ObservableObject { //Log.info("[ConversationsListViewModel] Conversation [${LinphoneUtils.getChatRoomId(chatRoom)}] state changed [$state]") switch cbValue.state { case ChatRoom.State.Created: - self.addChatRoom(cbChatRoom: cbValue.chatRoom) + let model = ConversationModel(chatRoom: cbValue.chatRoom) + self.addChatRoom(cbChatRoom: model) case ChatRoom.State.Deleted: self.computeChatRoomsList(filter: "") //ToastViewModel.shared.toastMessage = "toast_conversation_deleted" @@ -109,14 +127,13 @@ class ConversationsListViewModel: ObservableObject { self.mCoreSuscriptions.insert(core.publisher?.onMessagesReceived?.postOnMainQueue { _ in self.computeChatRoomsList(filter: "") - }) } } - func addChatRoom(cbChatRoom: ChatRoom) { + func addChatRoom(cbChatRoom: ConversationModel) { Log.info("[ConversationsListViewModel] Re-ordering conversations") - var sortedList: [ChatRoom] = [] + var sortedList: [ConversationModel] = [] sortedList.append(cbChatRoom) sortedList.append(contentsOf: self.conversationsList) @@ -129,7 +146,7 @@ class ConversationsListViewModel: ObservableObject { func reorderChatRooms() { Log.info("[ConversationsListViewModel] Re-ordering conversations") - var sortedList: [ChatRoom] = [] + var sortedList: [ConversationModel] = [] sortedList.append(contentsOf: conversationsList) DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { @@ -204,4 +221,10 @@ class ConversationsListViewModel: ObservableObject { return formatter.string(from: myNSDate) } } + + func refreshContactAvatarModel() { + conversationsList.forEach { conversationModel in + conversationModel.refreshAvatarModel() + } + } } diff --git a/Linphone/UI/Main/History/Fragments/HistoryContactFragment.swift b/Linphone/UI/Main/History/Fragments/HistoryContactFragment.swift index a5021b64a..9c427a317 100644 --- a/Linphone/UI/Main/History/Fragments/HistoryContactFragment.swift +++ b/Linphone/UI/Main/History/Fragments/HistoryContactFragment.swift @@ -563,7 +563,7 @@ struct HistoryContactFragment: View { #Preview { HistoryContactFragment( - contactAvatarModel: ContactAvatarModel(friend: nil, withPresence: false), + contactAvatarModel: ContactAvatarModel(friend: nil, name: "", withPresence: false), historyViewModel: HistoryViewModel(), historyListViewModel: HistoryListViewModel(), contactViewModel: ContactViewModel(), diff --git a/Linphone/UI/Main/History/Fragments/HistoryListFragment.swift b/Linphone/UI/Main/History/Fragments/HistoryListFragment.swift index 9d219ff30..0c4bc0d05 100644 --- a/Linphone/UI/Main/History/Fragments/HistoryListFragment.swift +++ b/Linphone/UI/Main/History/Fragments/HistoryListFragment.swift @@ -48,7 +48,7 @@ struct HistoryListFragment: View { && $0.friend!.name == addressFriend!.name && $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly() }) - : ContactAvatarModel(friend: nil, withPresence: false) + : ContactAvatarModel(friend: nil, name: "", withPresence: false) if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty { if contactAvatarModel != nil { diff --git a/Linphone/Utils/Avatar.swift b/Linphone/Utils/Avatar.swift index 5143790a5..aca33765f 100644 --- a/Linphone/Utils/Avatar.swift +++ b/Linphone/Utils/Avatar.swift @@ -22,6 +22,8 @@ import linphonesw struct Avatar: View { + private var contactsManager = ContactsManager.shared + @ObservedObject var contactAvatarModel: ContactAvatarModel let avatarSize: CGFloat @@ -71,6 +73,15 @@ struct Avatar: View { EmptyView() } } + } else if !contactAvatarModel.name.isEmpty { + Image(uiImage: contactsManager.textToImage( + firstName: contactAvatarModel.name, + lastName: contactAvatarModel.name.components(separatedBy: " ").count > 1 + ? contactAvatarModel.name.components(separatedBy: " ")[1] + : "")) + .resizable() + .frame(width: 50, height: 50) + .clipShape(Circle()) } else { Image("profil-picture-default") .resizable() diff --git a/Linphone/Utils/LinphoneUtils.swift b/Linphone/Utils/LinphoneUtils.swift index 579b4cdde..ded14ef87 100644 --- a/Linphone/Utils/LinphoneUtils.swift +++ b/Linphone/Utils/LinphoneUtils.swift @@ -27,20 +27,36 @@ class LinphoneUtils: NSObject { return !oneToOne && conference } - public class func getChatIconState(chatState: ChatMessage.State) -> String { + public class func getChatIconState(chatState: Int) -> String { return switch chatState { - case ChatMessage.State.Displayed, ChatMessage.State.FileTransferDone: + case ChatMessage.State.Displayed.rawValue, ChatMessage.State.FileTransferDone.rawValue: "checks" - case ChatMessage.State.DeliveredToUser: + case ChatMessage.State.DeliveredToUser.rawValue: "check" - case ChatMessage.State.Delivered: + case ChatMessage.State.Delivered.rawValue: "envelope-simple" - case ChatMessage.State.NotDelivered, ChatMessage.State.FileTransferError: + case ChatMessage.State.NotDelivered.rawValue, ChatMessage.State.FileTransferError.rawValue: "warning-circle" - case ChatMessage.State.InProgress, ChatMessage.State.FileTransferInProgress: + case ChatMessage.State.InProgress.rawValue, ChatMessage.State.FileTransferInProgress.rawValue: "animated-in-progress" default: "animated-in-progress" } } + + public class func getChatRoomId(room: ChatRoom) -> String { + return getChatRoomId(localAddress: room.localAddress!, remoteAddress: room.peerAddress!) + } + + public class func getChatRoomId(localAddress: Address, remoteAddress: Address) -> String { + let localSipUri = localAddress.clone() + localSipUri!.clean() + let remoteSipUri = remoteAddress.clone() + remoteSipUri!.clean() + return getChatRoomId(localSipUri: localSipUri!.asStringUriOnly(), remoteSipUri: remoteSipUri!.asStringUriOnly()) + } + + public class func getChatRoomId(localSipUri: String, remoteSipUri: String) -> String { + return "\(localSipUri)#~#\(remoteSipUri)" + } } diff --git a/Linphone/Utils/MagicSearchSingleton.swift b/Linphone/Utils/MagicSearchSingleton.swift index b65c09bf5..4a58ca4c8 100644 --- a/Linphone/Utils/MagicSearchSingleton.swift +++ b/Linphone/Utils/MagicSearchSingleton.swift @@ -82,9 +82,11 @@ final class MagicSearchSingleton: ObservableObject { self.contactsManager.lastSearch.forEach { searchResult in if searchResult.friend != nil { - self.contactsManager.avatarListModel.append(ContactAvatarModel(friend: searchResult.friend!, withPresence: true)) + self.contactsManager.avatarListModel.append(ContactAvatarModel(friend: searchResult.friend!, name: searchResult.friend?.name ?? "", withPresence: true)) } } + + NotificationCenter.default.post(name: NSNotification.Name("ContactLoaded"), object: nil) } } }