From aa5b0abd6775d2ebbcf75e447e14adedf0a6a275 Mon Sep 17 00:00:00 2001 From: Benoit Martins Date: Wed, 25 Sep 2024 14:30:42 +0200 Subject: [PATCH] Add banner when users are writing (composing) --- Linphone.xcodeproj/project.pbxproj | 4 ++ Linphone/Localizable.xcstrings | 67 +++++++++++++++++- .../Fragments/ConversationFragment.swift | 14 ++++ .../ViewModel/ConversationViewModel.swift | 68 +++++++++++++++++++ 4 files changed, 150 insertions(+), 3 deletions(-) diff --git a/Linphone.xcodeproj/project.pbxproj b/Linphone.xcodeproj/project.pbxproj index 284e9cf84..c365f1847 100644 --- a/Linphone.xcodeproj/project.pbxproj +++ b/Linphone.xcodeproj/project.pbxproj @@ -140,6 +140,7 @@ D7B5678E2B28888F00DE63EB /* CallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B5678D2B28888F00DE63EB /* CallView.swift */; }; D7B99E992B29B39000BE7BF2 /* CallViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B99E982B29B39000BE7BF2 /* CallViewModel.swift */; }; D7B99E9B2B29F7C300BE7BF2 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B99E9A2B29F7C200BE7BF2 /* ActivityIndicator.swift */; }; + D7C2DA1D2CA44DE400A2441B /* EventModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C2DA1C2CA44DE400A2441B /* EventModel.swift */; }; D7C365082AEFAB7F00FE6142 /* ContactListBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C365072AEFAB7F00FE6142 /* ContactListBottomSheet.swift */; }; D7C3650A2AF001C300FE6142 /* EditContactFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C365092AF001C300FE6142 /* EditContactFragment.swift */; }; D7C3650C2AF0084000FE6142 /* EditContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C3650B2AF0084000FE6142 /* EditContactViewModel.swift */; }; @@ -327,6 +328,7 @@ D7B5678D2B28888F00DE63EB /* CallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallView.swift; sourceTree = ""; }; D7B99E982B29B39000BE7BF2 /* CallViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewModel.swift; sourceTree = ""; }; D7B99E9A2B29F7C200BE7BF2 /* ActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; + D7C2DA1C2CA44DE400A2441B /* EventModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventModel.swift; sourceTree = ""; }; D7C365072AEFAB7F00FE6142 /* ContactListBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactListBottomSheet.swift; sourceTree = ""; }; D7C365092AF001C300FE6142 /* EditContactFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditContactFragment.swift; sourceTree = ""; }; D7C3650B2AF0084000FE6142 /* EditContactViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditContactViewModel.swift; sourceTree = ""; }; @@ -482,6 +484,7 @@ children = ( D70959F02B8DF3EC0014AC0B /* ConversationModel.swift */, D7E6ADF22B9875C20009A2BC /* Message.swift */, + D7C2DA1C2CA44DE400A2441B /* EventModel.swift */, D7E6ADF42B9876ED0009A2BC /* Attachment.swift */, ); path = Model; @@ -1098,6 +1101,7 @@ 662B69DB2B25DE25007118BF /* ProviderDelegate.swift in Sources */, D706BA822ADD72D100278F45 /* DeviceRotationViewModifier.swift in Sources */, D732A9132B04C7A300DB42BA /* HistoryListFragment.swift in Sources */, + D7C2DA1D2CA44DE400A2441B /* EventModel.swift in Sources */, D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */, D70A26F22B7F5D95006CC8FC /* ConversationFragment.swift in Sources */, 66C491FD2B24D36500CEA16D /* AudioRouteUtils.swift in Sources */, diff --git a/Linphone/Localizable.xcstrings b/Linphone/Localizable.xcstrings index 797be6b75..8f316799a 100644 --- a/Linphone/Localizable.xcstrings +++ b/Linphone/Localizable.xcstrings @@ -180,9 +180,15 @@ }, "9" : { + }, + "A subject and at least one participant is required to create a meeting" : { + }, "Accept all" : { + }, + "Account successfully logged out" : { + }, "Active" : { @@ -913,6 +919,40 @@ } } }, + "conversation_composing_label_multiple" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ are composing…" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ sont en train d'écrire…" + } + } + } + }, + "conversation_composing_label_single" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ is composing…" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ est en train d'écrire…" + } + } + } + }, "conversation_dialog_set_subject" : { "extractionState" : "manual", "localizations" : { @@ -946,6 +986,24 @@ } } } + }, + "conversation_ephemeral_messages_duration_disabled" : { + + }, + "conversation_ephemeral_messages_duration_one_day" : { + + }, + "conversation_ephemeral_messages_duration_one_hour" : { + + }, + "conversation_ephemeral_messages_duration_one_minute" : { + + }, + "conversation_ephemeral_messages_duration_one_week" : { + + }, + "conversation_ephemeral_messages_duration_three_days" : { + }, "conversation_event_admin_set" : { "extractionState" : "manual", @@ -1281,6 +1339,12 @@ }, "Copy SIP address" : { + }, + "Could not reach network" : { + + }, + "Could not send ICS invitations to meeting to any participant" : { + }, "D'accord" : { @@ -1677,9 +1741,6 @@ } } } - }, - "Incoming call" : { - }, "Information" : { diff --git a/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift b/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift index d1376d0de..e029d1ef1 100644 --- a/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift +++ b/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift @@ -379,6 +379,20 @@ struct ConversationFragment: View { } } + if !conversationViewModel.composingLabel.isEmpty { + HStack { + Text(conversationViewModel.composingLabel) + .lineLimit(1) + .default_text_style_300(styleSize: 15) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 10) + } + .onDisappear { + conversationViewModel.composingLabel = "" + } + .transition(.move(edge: .bottom)) + } + if conversationViewModel.messageToReply != nil { ZStack(alignment: .top) { HStack { diff --git a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift index c06f29420..28379afba 100644 --- a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift +++ b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift @@ -35,6 +35,7 @@ class ConversationViewModel: ObservableObject { @Published var displayedConversationUnreadMessagesCount: Int = 0 @Published var messageText: String = "" + @Published var composingLabel: String = "" private var chatRoomDelegate: ChatRoomDelegate? @@ -99,6 +100,8 @@ class ConversationViewModel: ObservableObject { self.getNewMessages(eventLogs: eventLogs) }, onChatMessageSending: { (_: ChatRoom, eventLog: EventLog) in self.getNewMessages(eventLogs: [eventLog]) + }, onIsComposingReceived: { (_: ChatRoom, _: Address, _: Bool) in + self.computeComposingLabel() }) self.displayedConversation?.chatRoom.addDelegate(delegate: self.chatRoomDelegate!) } @@ -282,6 +285,7 @@ class ConversationViewModel: ObservableObject { self.getHistorySize() self.getUnreadMessagesCount() self.getParticipantConversationModel() + self.computeComposingLabel() self.mediasToSend.removeAll() self.messageToReply = nil @@ -1375,10 +1379,26 @@ class ConversationViewModel: ObservableObject { conversationsList.forEach { conversation in if conversation.id == self.displayedConversation!.id { self.displayedConversation = conversation +<<<<<<< HEAD self.chatRoomDelegate = ChatRoomDelegateStub(onChatMessagesReceived: { (_: ChatRoom, eventLogs: [EventLog]) in self.getNewMessages(eventLogs: eventLogs) }, onChatMessageSending: { (_: ChatRoom, eventLog: EventLog) in self.getNewMessages(eventLogs: [eventLog]) +======= + + let messageID = self.displayedConversation!.chatRoom.getHistoryRangeEvents(begin: 0, end: 1).first?.chatMessage?.messageId + if self.conversationMessagesSection[0].rows.first?.message.id != messageID { + self.resetMessage() + self.getMessages() + } + + self.chatRoomSuscriptions.insert(conversation.chatRoom.publisher?.onChatMessageSending?.postOnCoreQueue { (cbValue: (chatRoom: ChatRoom, eventLog: EventLog)) in + self.getNewMessages(eventLogs: [cbValue.eventLog]) + }) + + self.chatRoomSuscriptions.insert(conversation.chatRoom.publisher?.onChatMessagesReceived?.postOnCoreQueue { (cbValue: (chatRoom: ChatRoom, eventLogs: [EventLog])) in + self.getNewMessages(eventLogs: cbValue.eventLogs) +>>>>>>> aa3a58b (Add banner when users are writing (composing)) }) self.displayedConversation?.chatRoom.addDelegate(delegate: self.chatRoomDelegate!) } @@ -1709,6 +1729,54 @@ class ConversationViewModel: ObservableObject { } } } + + func computeComposingLabel() { + let composing = self.displayedConversation!.chatRoom.isRemoteComposing + + if !composing { + DispatchQueue.main.async { + withAnimation { + self.composingLabel = "" + } + } + return + } + + var composingFriends: [String] = [] + var label = "" + + for address in self.displayedConversation!.chatRoom.composingAddresses { + if let addressCleaned = address.clone() { + addressCleaned.clean() + + if let avatar = self.participantConversationModel.first(where: {$0.address == addressCleaned.asStringUriOnly()}) { + let name = avatar.name + composingFriends.append(name) + label += "\(name), " + } + } + } + + if !composingFriends.isEmpty { + label = String(label.dropLast(2)) + + let format = composingFriends.count > 1 + ? String(format: NSLocalizedString("conversation_composing_label_multiple", comment: ""), label) + : String(format: NSLocalizedString("conversation_composing_label_single", comment: ""), label) + + DispatchQueue.main.async { + withAnimation { + self.composingLabel = format + } + } + } else { + DispatchQueue.main.async { + withAnimation { + self.composingLabel = "" + } + } + } + } } // swiftlint:enable line_length // swiftlint:enable type_body_length