Add Message Waiting Indication

This commit is contained in:
Benoit Martins 2025-12-09 10:33:03 +01:00
parent c7ddf2d8d0
commit f16a0f42ae
10 changed files with 204 additions and 31 deletions

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "voicemail.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M200,72a56,56,0,0,0-39.14,96H95.14A56,56,0,1,0,56,184H200a56,56,0,0,0,0-112ZM16,128a40,40,0,1,1,40,40A40,40,0,0,1,16,128Zm184,40a40,40,0,1,1,40-40A40,40,0,0,1,200,168Z"></path></svg>

After

Width:  |  Height:  |  Size: 291 B

View file

@ -437,6 +437,31 @@ class CoreContext: ObservableObject {
DispatchQueue.main.async {
self.accounts = accountModels
}
}, onMessageWaitingIndicationChanged: { (core: Core, event: Event, mwi: MessageWaitingIndication) in
if (mwi.hasMessageWaiting()) {
let summaries = mwi.summaries
Log.info(
"[CoreContext][onMessageWaitingIndicationChanged] MWI NOTIFY received, messages are waiting (\(summaries.count) summaries)"
)
if let defaultAccount = core.defaultAccount?.params?.identityAddress, let mwiAccount = mwi.accountAddress, defaultAccount.weakEqual(address2: mwiAccount){
if !summaries.isEmpty {
let summary = summaries.first
DispatchQueue.main.async {
withAnimation {
SharedMainViewModel.shared.waitingMessageCount = Int(summary?.nbNew ?? 0)
}
}
}
}
} else {
Log.info("[CoreContext][onMessageWaitingIndicationChanged] MWI NOTIFY received, no message is waiting")
DispatchQueue.main.async {
withAnimation {
SharedMainViewModel.shared.waitingMessageCount = 0
}
}
}
})
self.mCore.addDelegate(delegate: self.mCoreDelegate)

View file

@ -1,7 +1,7 @@
import Foundation
public enum AppGitInfo {
public static let branch = "master"
public static let commit = "33b379285"
public static let branch = "feature/mwi"
public static let commit = "623d2e067"
public static let tag = "6.1.0-alpha"
}

View file

@ -419,6 +419,8 @@
"message_meeting_invitation_updated_notification" = "Meeting has been updated";
"message_reaction_click_to_remove_label" = "Click to remove";
"message_reactions_info_all_title" = "Reactions";
"mwi_messages_are_waiting_single" = "1 new voice message";
"mwi_messages_are_waiting_multiple" = "%@ new voice messages";
"network_not_reachable" = "You aren't connected to internet";
"network_reachable_again" = "Network is now reachable again";
"new_conversation_create_group" = "Create a group conversation";

View file

@ -419,6 +419,8 @@
"message_meeting_invitation_updated_notification" = "Réunion mise à jour";
"message_reaction_click_to_remove_label" = "Cliquez pour supprimer";
"message_reactions_info_all_title" = "Réactions";
"mwi_messages_are_waiting_single" = "1 message vocal en attente";
"mwi_messages_are_waiting_multiple" = "%@ messages vocaux en attente";
"network_not_reachable" = "Vous nêtes pas connecté à internet";
"network_reachable_again" = "Vous êtes à nouveau connecté à internet";
"new_conversation_create_group" = "Créer une conversation de groupe";

View file

@ -140,6 +140,54 @@ struct ContentView: View {
.background(Color.redDanger500)
}
if sharedMainViewModel.waitingMessageCount > 0 && (!telecomManager.callInProgress || (telecomManager.callInProgress && !telecomManager.callDisplayed)) {
HStack {
Image("voicemail")
.renderingMode(.template)
.resizable()
.foregroundStyle(.white)
.frame(width: 26, height: 26)
.padding(.leading, 10)
if sharedMainViewModel.waitingMessageCount > 1 {
Text(String(format: String(localized: "mwi_messages_are_waiting_multiple"), sharedMainViewModel.waitingMessageCount.description))
.default_text_style_white(styleSize: 16)
} else {
Text(String(localized: "mwi_messages_are_waiting_single"))
.default_text_style_white(styleSize: 16)
}
Spacer()
Button(
action: {
withAnimation {
sharedMainViewModel.waitingMessageCount = 0
}
}, label: {
Image("x")
.renderingMode(.template)
.resizable()
.foregroundStyle(.white)
.frame(width: 26, height: 26)
.padding(.trailing, 10)
}
)
}
.frame(maxWidth: .infinity)
.frame(height: 40)
.padding(.horizontal, 10)
.background(Color.gray)
.onTapGesture {
if let index = accountProfileViewModel.defaultAccountModelIndex,
index < coreContext.accounts.count {
sharedMainViewModel.waitingMessageCount = 0
coreContext.accounts[index].callVoicemailUri()
}
}
}
if !sharedMainViewModel.fileUrlsToShare.isEmpty && (!telecomManager.callInProgress || (telecomManager.callInProgress && !telecomManager.callDisplayed)) {
HStack {
Image("share-network")

View file

@ -90,15 +90,51 @@ struct SideMenuAccountRow: View {
Spacer()
HStack {
if model.voicemailCount > 0 {
Button {
model.callVoicemailUri()
} label: {
ZStack(alignment: .top) {
VStack {
Spacer()
Image("voicemail")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c500)
.frame(width: 22, height: 22)
Spacer()
}
Text(String(model.voicemailCount))
.foregroundStyle(Color.redDanger500)
.default_text_style_600(styleSize: 12)
.lineLimit(1)
.frame(maxWidth: .infinity, alignment: .trailing)
.padding(.top, 1)
}
.frame(width: 30, height: 30)
}
.highPriorityGesture(
TapGesture().onEnded {
model.callVoicemailUri()
}
)
}
if model.notificationsCount > 0 && !CorePreferences.disableChatFeature {
Text(String(model.notificationsCount))
.foregroundStyle(.white)
.default_text_style(styleSize: 12)
.lineLimit(1)
.frame(width: 20, height: 20)
.background(Color.redDanger500)
.cornerRadius(50)
.frame(maxWidth: .infinity, alignment: .leading)
VStack {
Text(String(model.notificationsCount))
.foregroundStyle(.white)
.default_text_style(styleSize: 12)
.lineLimit(1)
.frame(width: 20, height: 20)
.background(Color.redDanger500)
.cornerRadius(50)
}
.frame(width: 30, height: 30)
.padding(.trailing, -8)
}
Menu {
@ -112,15 +148,18 @@ struct SideMenuAccountRow: View {
Label("drawer_menu_manage_account", systemImage: "arrow.right.circle")
}
} label: {
Image("dots-three-vertical")
.renderingMode(.template)
.resizable()
.foregroundColor(Color.gray)
.scaledToFit()
.frame(height: 30)
VStack {
Image("dots-three-vertical")
.renderingMode(.template)
.resizable()
.foregroundColor(Color.grayMain2c500)
.scaledToFit()
.frame(height: 25)
}
.frame(width: 30, height: 30)
}
}
.frame(width: 64, alignment: .trailing)
.frame(alignment: .trailing)
.padding(.top, 12)
.padding(.bottom, 12)
}

View file

@ -32,6 +32,8 @@ class AccountModel: ObservableObject {
@Published var registrationStateAssociatedUIColor: Color = .clear
@Published var isRegistrered: Bool = false
@Published var notificationsCount: Int = 0
@Published var showMwi: Bool = false
@Published var voicemailCount: Int = 0
@Published var isDefaultAccount: Bool = false
@Published var displayName: String = ""
@Published var address: String = ""
@ -52,22 +54,45 @@ class AccountModel: ObservableObject {
init(account: Account, core: Core) {
self.account = account
self.computeNotificationsCount()
self.computeNotificationsCount()
accountDelegate = AccountDelegateStub(onRegistrationStateChanged: { (_: Account, _: RegistrationState, _: String) in
self.update()
})
accountDelegate = AccountDelegateStub(
onRegistrationStateChanged: { (_: Account, _: RegistrationState, _: String) in
self.update()
}, onMessageWaitingIndicationChanged: { (account: Account, mwi: MessageWaitingIndication) in
Log.info("\(AccountModel.TAG) Account \(account.params?.identityAddress?.asStringUriOnly() ?? "Error") has received a MWI NOTIFY. \(mwi.hasMessageWaiting() ? "Message(s) are waiting." : "No message is waiting.")")
let showMwiTmp = mwi.hasMessageWaiting()
var voicemailCountTmp = 0
for summary in mwi.summaries {
let contextClass = summary.contextClass
let nbNew = summary.nbNew
let nbNewUrgent = summary.nbNewUrgent
let nbOld = summary.nbOld
let nbOldUrgent = summary.nbOldUrgent
Log.info("\(AccountModel.TAG) [MWI] \(contextClass): new \(nbNew) urgent \(nbNewUrgent), old \(nbOld) urgent \(nbOldUrgent)")
voicemailCountTmp = Int(nbNew)
}
DispatchQueue.main.async {
self.showMwi = showMwiTmp
self.voicemailCount = voicemailCountTmp
}
}
)
account.addDelegate(delegate: accountDelegate!)
coreDelegate = CoreDelegateStub(onCallStateChanged: { (_: Core, _: Call, _: Call.State, _: String) in
self.computeNotificationsCount()
}, onMessagesReceived: { (_: Core, _: ChatRoom, _: [ChatMessage]) in
self.computeNotificationsCount()
}, onChatRoomRead: { (_: Core, _: ChatRoom) in
self.computeNotificationsCount()
}, onMessageRetracted: { (_: Core, _: ChatRoom, _: ChatMessage) in
self.computeNotificationsCount()
})
coreDelegate = CoreDelegateStub(
onCallStateChanged: { (_: Core, _: Call, _: Call.State, _: String) in
self.computeNotificationsCount()
}, onMessagesReceived: { (_: Core, _: ChatRoom, _: [ChatMessage]) in
self.computeNotificationsCount()
}, onChatRoomRead: { (_: Core, _: ChatRoom) in
self.computeNotificationsCount()
}, onMessageRetracted: { (_: Core, _: ChatRoom, _: ChatMessage) in
self.computeNotificationsCount()
}
)
core.addDelegate(delegate: coreDelegate!)
CoreContext.shared.doOnCoreQueue { _ in
@ -311,6 +336,15 @@ class AccountModel: ObservableObject {
self.isDefaultAccount = true
}
func callVoicemailUri() {
CoreContext.shared.doOnCoreQueue { core in
if let voicemail = self.account.params?.voicemailAddress {
Log.info("\(AccountModel.TAG) Calling voicemail address \(voicemail.asStringUriOnly())")
TelecomManager.shared.doCallOrJoinConf(address: voicemail)
}
}
}
}
class AccountDeviceModel: ObservableObject {

View file

@ -40,6 +40,7 @@ class SharedMainViewModel: ObservableObject {
@Published var dialPlansShortLabelList: [String] = []
@Published var fileUrlsToShare: [String] = []
@Published var waitingMessageCount: Int = 0
@Published var operationInProgress = false