diff --git a/Linphone/Assets.xcassets/calendar.imageset/calendar.svg b/Linphone/Assets.xcassets/calendar.imageset/calendar.svg
index 5caacdbef..c066f4a4b 100644
--- a/Linphone/Assets.xcassets/calendar.imageset/calendar.svg
+++ b/Linphone/Assets.xcassets/calendar.imageset/calendar.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/Linphone/Assets.xcassets/trash.imageset/Contents.json b/Linphone/Assets.xcassets/trash.imageset/Contents.json
new file mode 100644
index 000000000..4c3681644
--- /dev/null
+++ b/Linphone/Assets.xcassets/trash.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "trash.svg",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Linphone/Assets.xcassets/trash.imageset/trash.svg b/Linphone/Assets.xcassets/trash.imageset/trash.svg
new file mode 100644
index 000000000..b2d5dfe18
--- /dev/null
+++ b/Linphone/Assets.xcassets/trash.imageset/trash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Linphone/Contacts/ContactsManager.swift b/Linphone/Contacts/ContactsManager.swift
index 194e7a556..1338effcc 100644
--- a/Linphone/Contacts/ContactsManager.swift
+++ b/Linphone/Contacts/ContactsManager.swift
@@ -318,7 +318,7 @@ final class ContactsManager: ObservableObject {
// Clear existing addresses and add new ones
friend.addresses.forEach { friend.removeAddress(address: $0) }
for sipAddress in contact.sipAddresses where !sipAddress.isEmpty {
- if let address = core.interpretUrl(url: sipAddress, applyInternationalPrefix: true),
+ if let address = core.interpretUrl(url: sipAddress, applyInternationalPrefix: LinphoneUtils.applyInternationalPrefix(core: core)),
!friend.addresses.contains(where: { $0.asString() == address.asString() }) {
friend.addAddress(address: address)
}
diff --git a/Linphone/Core/CoreContext.swift b/Linphone/Core/CoreContext.swift
index 023e4680b..f027a909c 100644
--- a/Linphone/Core/CoreContext.swift
+++ b/Linphone/Core/CoreContext.swift
@@ -148,11 +148,21 @@ class CoreContext: ObservableObject {
self.mCore.callkitEnabled = true
self.mCore.pushNotificationEnabled = true
- let appName = Bundle.main.infoDictionary?["CFBundleName"] as? String
- let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
+ let appGitVersion = APP_GIT_COMMIT
+ let appGitBranch = APP_GIT_BRANCH
+ let appGitTag = APP_GIT_TAG
+ let sdkGitVersion = linphonesw.sdkVersion
+ var sdkGitBranch = linphonesw.sdkBranch
- let userAgent = "LinphoneiOS/\(version ?? "6.0.0") (\(UIDevice.current.localizedModel.replacingOccurrences(of: "'", with: ""))) LinphoneSDK"
+ if sdkGitBranch.hasPrefix("remotes/origin/") {
+ sdkGitBranch = String(sdkGitBranch.dropFirst("remotes/origin/".count))
+ }
+
+ Log.info("Git Info — App: \(appGitTag + "-" + appGitVersion) [\(appGitBranch)] | SDK: \(sdkGitVersion) [\(sdkGitBranch)]")
+
+ let userAgent = "LinphoneiOS/\(appGitTag) (\(UIDevice.current.localizedModel.replacingOccurrences(of: "'", with: ""))) LinphoneSDK"
self.mCore.setUserAgent(name: userAgent, version: self.coreVersion)
+
self.mCore.videoCaptureEnabled = true
self.mCore.videoDisplayEnabled = true
self.mCore.videoPreviewEnabled = false
diff --git a/Linphone/GeneratedGit.swift b/Linphone/GeneratedGit.swift
new file mode 100644
index 000000000..e6941c350
--- /dev/null
+++ b/Linphone/GeneratedGit.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+public let APP_GIT_BRANCH = "master"
+public let APP_GIT_COMMIT = "5492a3e3a"
+public let APP_GIT_TAG = "6.1.0-alpha"
diff --git a/Linphone/Localizable/cs.lproj/Localizable.strings b/Linphone/Localizable/cs.lproj/Localizable.strings
index 27e239ef7..7f6df198e 100644
--- a/Linphone/Localizable/cs.lproj/Localizable.strings
+++ b/Linphone/Localizable/cs.lproj/Localizable.strings
@@ -2,7 +2,7 @@
"assistant_sip_account_transport_protocol" = "Transport";
"contact_call_action" = "Volat";
"conversation_action_call" = "Volat";
-"dialog_ok" = "OK";
+"dialog_confirm" = "Confirm";
"drawer_menu_manage_account" = "Spravovat profil";
"help_error_checking_version_toast_message" = "Během kontroly aktualizací nastala chyba";
"settings_contacts_carddav_name_title" = "Zobrazené jméno";
@@ -474,9 +474,9 @@
"message_copied_to_clipboard_toast" = "Zpráva zkopírována do schránky";
"message_delivery_info_read_title" = "Přečteno";
"message_delivery_info_received_title" = "Přijato";
-"message_meeting_invitation_cancelled_notification" = "📅 Schůzka byla zrušena";
-"message_meeting_invitation_notification" = "📅 Jste pozváni na schůzku";
-"message_meeting_invitation_updated_notification" = "📅 Schůzka byla aktualizována";
+"message_meeting_invitation_cancelled_notification" = "Schůzka byla zrušena";
+"message_meeting_invitation_notification" = "Jste pozváni na schůzku";
+"message_meeting_invitation_updated_notification" = "Schůzka byla aktualizována";
"message_reactions_info_all_title" = "Reakce";
"network_reachable_again" = "Síť je znovu dostupná";
"menu_block_number" = "Blokovat číslo";
diff --git a/Linphone/Localizable/de.lproj/Localizable.strings b/Linphone/Localizable/de.lproj/Localizable.strings
index 97d4d7e1c..158351512 100644
--- a/Linphone/Localizable/de.lproj/Localizable.strings
+++ b/Linphone/Localizable/de.lproj/Localizable.strings
@@ -192,7 +192,7 @@
"dialog_deny" = "Ablehnen";
"dialog_install" = "Installieren";
"dialog_no" = "Nein";
-"dialog_ok" = "OK";
+"dialog_confirm" = "Confirm";
"dialog_yes" = "Ja";
"drawer_menu_account_connection_status_cleared" = "Deaktiviert";
"drawer_menu_account_connection_status_connected" = "Verbunden";
@@ -467,9 +467,9 @@
"message_delivery_info_read_title" = "Gelesen";
"message_delivery_info_received_title" = "Empfangen";
"message_delivery_info_sent_title" = "Gesendet";
-"message_meeting_invitation_cancelled_notification" = "📅 Die Besprechung wurde abgesagt";
-"message_meeting_invitation_notification" = "📅 Sie sind zu einer Besprechung eingeladen";
-"message_meeting_invitation_updated_notification" = "📅 Besprechung wurde aktualisiert";
+"message_meeting_invitation_cancelled_notification" = "Die Besprechung wurde abgesagt";
+"message_meeting_invitation_notification" = "Sie sind zu einer Besprechung eingeladen";
+"message_meeting_invitation_updated_notification" = "Besprechung wurde aktualisiert";
"message_reactions_info_all_title" = "Reaktionen";
"network_reachable_again" = "Netzwerk ist nun wieder erreichbar";
"None" = "Kein";
diff --git a/Linphone/Localizable/en.lproj/Localizable.strings b/Linphone/Localizable/en.lproj/Localizable.strings
index 78479300d..cf1b49e09 100644
--- a/Linphone/Localizable/en.lproj/Localizable.strings
+++ b/Linphone/Localizable/en.lproj/Localizable.strings
@@ -47,6 +47,8 @@
"account_settings_im_encryption_mandatory_title" = "IM encryption mandatory";
"account_settings_lime_server_url_title" = "E2E encryption keys server URL";
"account_settings_mwi_uri_title" = "MWI server URI (Message Waiting Indicator)";
+"account_settings_apply_international_prefix_title" = "Format phone numbers using international prefix";
+"account_settings_replace_plus_by_00_title" = "Replace + by 00 when formatting phone numbers";
"account_settings_nat_policy_title" = "NAT policy settings";
"account_settings_outbound_proxy_title" = "Outbound proxy";
"account_settings_push_notification_not_available_title" = "Push notifications aren't available!";
@@ -188,6 +190,7 @@
"contacts_list_favourites_title" = "Favourites";
"contacts_list_filter_popup_see_all" = "See all";
"contacts_list_filter_popup_see_linphone_only" = "See %@ contacts";
+"contacts_list_filter_popup_see_sip_only" = "See SIP contacts";
"conversation_action_call" = "Call";
"conversation_action_configure_ephemeral_messages" = "Configure ephemeral messages";
"conversation_action_delete" = "Delete conversation";
@@ -201,6 +204,13 @@
"conversation_dialog_edit_subject" = "Edit conversation subject";
"conversation_dialog_set_subject" = "Set conversation subject";
"conversation_dialog_subject_hint" = "Conversation subject";
+"conversation_editing_message_title" = "Message being edited";
+"conversation_message_edited_label" = "Edited";
+"conversation_dialog_delete_chat_message_title" = "Delete this message?";
+"conversation_dialog_delete_locally_label" = "For me";
+"conversation_dialog_delete_for_everyone_label" = "For everyone";
+"conversation_message_content_deleted_label" = "This message has been deleted";
+"conversation_message_content_deleted_by_us_label" = "You have deleted this message";
"conversation_end_to_end_encrypted_bottom_sheet_link" = "https://linphone.org/en/features/#security";
"conversation_end_to_end_encrypted_event_title" = "End-to-end encrypted conversation";
"conversation_end_to_end_encrypted_event_subtitle" = "Messages in this conversation are e2e encrypted. Only your correspondent can decrypt them.";
@@ -267,7 +277,7 @@
"dialog_deny" = "Deny";
"dialog_install" = "Install";
"dialog_no" = "No";
-"dialog_ok" = "OK";
+"dialog_confirm" = "Confirm";
"dialog_yes" = "Yes";
"drawer_menu_account_connection_status_cleared" = "Disabled";
"drawer_menu_account_connection_status_connected" = "Connected";
@@ -391,6 +401,7 @@
"menu_delete_selected_item" = "Delete";
"menu_forward_chat_message" = "Forward";
"menu_invite" = "Invite";
+"menu_edit_chat_message" = "Edit";
"menu_reply_to_chat_message" = "Reply";
"menu_resend_chat_message" = "Re-send";
"menu_see_existing_contact" = "See contact";
@@ -401,9 +412,9 @@
"message_delivery_info_received_title" = "Received";
"message_delivery_info_sent_title" = "Sent";
"message_forwarded_label" = "Forwarded";
-"message_meeting_invitation_cancelled_notification" = "📅 Meeting has been cancelled";
-"message_meeting_invitation_notification" = "📅 You are invited to a meeting";
-"message_meeting_invitation_updated_notification" = "📅 Meeting has been updated";
+"message_meeting_invitation_cancelled_notification" = "Meeting has been cancelled";
+"message_meeting_invitation_notification" = "You are invited to a meeting";
+"message_meeting_invitation_updated_notification" = "Meeting has been updated";
"message_reaction_click_to_remove_label" = "Click to remove";
"message_reactions_info_all_title" = "Reactions";
"network_not_reachable" = "You aren't connected to internet";
diff --git a/Linphone/Localizable/fr.lproj/Localizable.strings b/Linphone/Localizable/fr.lproj/Localizable.strings
index 58961a458..d9b064b5d 100644
--- a/Linphone/Localizable/fr.lproj/Localizable.strings
+++ b/Linphone/Localizable/fr.lproj/Localizable.strings
@@ -47,6 +47,8 @@
"account_settings_im_encryption_mandatory_title" = "Chiffrement obligatoire des conversations";
"account_settings_lime_server_url_title" = "URL du serveur d'échange de clés de chiffrement";
"account_settings_mwi_uri_title" = "URI du serveur MWI (Message Waiting Indicator)";
+"account_settings_apply_international_prefix_title" = "Formater les numéros en utilisant l'indicatif international";
+"account_settings_replace_plus_by_00_title" = "Remplacer + par 00 lors du formatage des numéros de téléphone";
"account_settings_nat_policy_title" = "Paramètres de politique NAT";
"account_settings_outbound_proxy_title" = "Serveur mandataire sortant";
"account_settings_push_notification_not_available_title" = "Notifications push non disponibles";
@@ -188,6 +190,7 @@
"contacts_list_favourites_title" = "Favoris";
"contacts_list_filter_popup_see_all" = "Tous les contacts";
"contacts_list_filter_popup_see_linphone_only" = "Contacts %@";
+"contacts_list_filter_popup_see_sip_only" = "Contacts SIP";
"conversation_action_call" = "Appeler";
"conversation_action_configure_ephemeral_messages" = "Configurer les messages éphémères";
"conversation_action_delete" = "Supprimer la conversation";
@@ -201,6 +204,13 @@
"conversation_dialog_edit_subject" = "Renommer la conversation";
"conversation_dialog_set_subject" = "Nommer la conversation";
"conversation_dialog_subject_hint" = "Nom de la conversation";
+"conversation_editing_message_title" = "Modification du message";
+"conversation_message_edited_label" = "Modifié";
+"conversation_dialog_delete_chat_message_title" = "Supprimer le message ?";
+"conversation_dialog_delete_locally_label" = "Pour moi";
+"conversation_dialog_delete_for_everyone_label" = "Pour tout le monde";
+"conversation_message_content_deleted_label" = "Le message a été supprimé";
+"conversation_message_content_deleted_by_us_label" = "Vous avez supprimé le message";
"conversation_end_to_end_encrypted_bottom_sheet_link" = "https://linphone.org/en/features/#security";
"conversation_end_to_end_encrypted_event_title" = "Conversation chiffrée de bout en bout";
"conversation_end_to_end_encrypted_event_subtitle" = "Les messages de cette conversation sont chiffrés de bout en bout. Seul votre correspondant peut les déchiffrer.";
@@ -267,7 +277,7 @@
"dialog_deny" = "Refuser";
"dialog_install" = "Installer";
"dialog_no" = "Non";
-"dialog_ok" = "OK";
+"dialog_confirm" = "Confirmer";
"dialog_yes" = "Oui";
"drawer_menu_account_connection_status_cleared" = "Désactivé";
"drawer_menu_account_connection_status_connected" = "Connecté";
@@ -391,6 +401,7 @@
"menu_delete_selected_item" = "Supprimer";
"menu_forward_chat_message" = "Transférer";
"menu_invite" = "Inviter";
+"menu_edit_chat_message" = "Modifier";
"menu_reply_to_chat_message" = "Répondre";
"menu_resend_chat_message" = "Ré-envoyer";
"menu_see_existing_contact" = "Voir le contact";
@@ -401,9 +412,9 @@
"message_delivery_info_received_title" = "Reçu";
"message_delivery_info_sent_title" = "Envoyé";
"message_forwarded_label" = "Transféré";
-"message_meeting_invitation_cancelled_notification" = "📅 Réunion annulée";
-"message_meeting_invitation_notification" = "📅 Invitation à une réunion";
-"message_meeting_invitation_updated_notification" = "📅 Réunion mise à jour";
+"message_meeting_invitation_cancelled_notification" = "Réunion annulée";
+"message_meeting_invitation_notification" = "Invitation à une réunion";
+"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";
"network_not_reachable" = "Vous n’êtes pas connecté à internet";
diff --git a/Linphone/Localizable/ru.lproj/Localizable.strings b/Linphone/Localizable/ru.lproj/Localizable.strings
index 0f6b7d6f9..bd4edb2a2 100644
--- a/Linphone/Localizable/ru.lproj/Localizable.strings
+++ b/Linphone/Localizable/ru.lproj/Localizable.strings
@@ -187,7 +187,7 @@
"dialog_deny" = "Отказать";
"dialog_install" = "Установить";
"dialog_no" = "Нет";
-"dialog_ok" = "ОК";
+"dialog_confirm" = "ОК";
"dialog_yes" = "Да";
"drawer_menu_account_connection_status_cleared" = "Отключить";
"drawer_menu_account_connection_status_connected" = "Подключен";
diff --git a/Linphone/Localizable/sk.lproj/Localizable.strings b/Linphone/Localizable/sk.lproj/Localizable.strings
index 7a04eba8f..582bba3a4 100644
--- a/Linphone/Localizable/sk.lproj/Localizable.strings
+++ b/Linphone/Localizable/sk.lproj/Localizable.strings
@@ -171,7 +171,7 @@
"dialog_deny" = "Odmietnuť";
"dialog_install" = "Inštalovať";
"dialog_no" = "Nie";
-"dialog_ok" = "OK";
+"dialog_confirm" = "Confirm";
"dialog_yes" = "Áno";
"meeting_waiting_room_cancel" = "Zrušiť";
"menu_delete_selected_item" = "Vymazať";
diff --git a/Linphone/Localizable/uk.lproj/Localizable.strings b/Linphone/Localizable/uk.lproj/Localizable.strings
index b965bbb6f..0bfb10b18 100644
--- a/Linphone/Localizable/uk.lproj/Localizable.strings
+++ b/Linphone/Localizable/uk.lproj/Localizable.strings
@@ -377,7 +377,7 @@
"conversation_ephemeral_messages_duration_one_week" = "1 тиждень";
"conversation_ephemeral_messages_duration_three_days" = "3 доби";
"conversation_ephemeral_messages_duration_one_day" = "1 доба";
-"dialog_ok" = "ОК";
+"dialog_confirm" = "ОК";
"welcome_page_title" = "Ласкаво просимо";
": %@" = ": %@";
"[https://sip.linphone.org](https://sip.linphone.org)" = "[https://sip.linphone.org](https://sip.linphone.org)";
@@ -456,9 +456,9 @@
"message_delivery_info_read_title" = "Читати";
"message_delivery_info_received_title" = "Отримано";
"message_delivery_info_sent_title" = "Відправлено";
-"message_meeting_invitation_cancelled_notification" = "📅 Нараду скасовано";
-"message_meeting_invitation_notification" = "📅 Вас запрошено на нараду";
-"message_meeting_invitation_updated_notification" = "📅 Нараду оновлено";
+"message_meeting_invitation_cancelled_notification" = "Нараду скасовано";
+"message_meeting_invitation_notification" = "Вас запрошено на нараду";
+"message_meeting_invitation_updated_notification" = "Нараду оновлено";
"message_reactions_info_all_title" = "Реакції";
"network_reachable_again" = "Мережа знову доступна";
"None" = "Жоден";
diff --git a/Linphone/Localizable/zh-Hans.lproj/Localizable.strings b/Linphone/Localizable/zh-Hans.lproj/Localizable.strings
index 853d1469c..b3b295824 100644
--- a/Linphone/Localizable/zh-Hans.lproj/Localizable.strings
+++ b/Linphone/Localizable/zh-Hans.lproj/Localizable.strings
@@ -181,7 +181,7 @@
"dialog_deny" = "拒绝";
"dialog_install" = "安装";
"dialog_no" = "否";
-"dialog_ok" = "OK";
+"dialog_confirm" = "Confirm";
"dialog_yes" = "是";
"drawer_menu_account_connection_status_cleared" = "禁用";
"drawer_menu_account_connection_status_connected" = "已连接";
diff --git a/Linphone/UI/Assistant/Fragments/LoginFragment.swift b/Linphone/UI/Assistant/Fragments/LoginFragment.swift
index 3fde7171e..655dd638d 100644
--- a/Linphone/UI/Assistant/Fragments/LoginFragment.swift
+++ b/Linphone/UI/Assistant/Fragments/LoginFragment.swift
@@ -70,25 +70,33 @@ struct LoginFragment: View {
let contentPopup3 = Text(.init(splitMsg[1]))
let contentPopup4 = Text(.init(privacyPolicy)).underline()
let contentPopup5 = Text(.init(splitMsg[2]))
- PopupView(isShowPopup: $isShowPopup,
- title: Text("assistant_dialog_general_terms_and_privacy_policy_title"),
- content: contentPopup1 + contentPopup2 + contentPopup3 + contentPopup4 + contentPopup5,
- titleFirstButton: Text("dialog_deny"),
- actionFirstButton: {self.isShowPopup.toggle()},
- titleSecondButton: Text("dialog_accept"),
- actionSecondButton: {acceptGeneralTerms()})
+ PopupView(
+ isShowPopup: $isShowPopup,
+ title: Text("assistant_dialog_general_terms_and_privacy_policy_title"),
+ content: contentPopup1 + contentPopup2 + contentPopup3 + contentPopup4 + contentPopup5,
+ titleFirstButton: nil,
+ actionFirstButton: {},
+ titleSecondButton: Text("dialog_accept"),
+ actionSecondButton: { acceptGeneralTerms() },
+ titleThirdButton: Text("dialog_deny"),
+ actionThirdButton: { self.isShowPopup.toggle() }
+ )
.background(.black.opacity(0.65))
.onTapGesture {
self.isShowPopup.toggle()
}
} else { // backup just in case
- PopupView(isShowPopup: $isShowPopup,
- title: Text("assistant_dialog_general_terms_and_privacy_policy_title"),
- content: Text(.init(String(format: String(localized: "assistant_dialog_general_terms_and_privacy_policy_message"), generalTerms, privacyPolicy))),
- titleFirstButton: Text("dialog_deny"),
- actionFirstButton: {self.isShowPopup.toggle()},
- titleSecondButton: Text("dialog_accept"),
- actionSecondButton: {acceptGeneralTerms()})
+ PopupView(
+ isShowPopup: $isShowPopup,
+ title: Text("assistant_dialog_general_terms_and_privacy_policy_title"),
+ content: Text(.init(String(format: String(localized: "assistant_dialog_general_terms_and_privacy_policy_message"), generalTerms, privacyPolicy))),
+ titleFirstButton: nil,
+ actionFirstButton: {},
+ titleSecondButton: Text("dialog_accept"),
+ actionSecondButton: { acceptGeneralTerms() },
+ titleThirdButton: Text("dialog_deny"),
+ actionThirdButton: { self.isShowPopup.toggle() }
+ )
.background(.black.opacity(0.65))
.onTapGesture {
self.isShowPopup.toggle()
diff --git a/Linphone/UI/Assistant/Fragments/ProfileModeFragment.swift b/Linphone/UI/Assistant/Fragments/ProfileModeFragment.swift
index b5aad06a6..bfde7c666 100644
--- a/Linphone/UI/Assistant/Fragments/ProfileModeFragment.swift
+++ b/Linphone/UI/Assistant/Fragments/ProfileModeFragment.swift
@@ -145,20 +145,21 @@ struct ProfileModeFragment: View {
}
if self.isShowPopup {
- PopupView(isShowPopup: $isShowPopup,
- title: Text(isShowPopupForDefault ? "Default mode" : "Interoperable mode"),
- content: Text(
- isShowPopupForDefault
- ? "Texte explicatif du default mode : lorem ipsum dolor sit amet, consectetur adipiscing elit."
- + "Etiam velit sapien, egestas sit amet dictum eget, condimentum a ligula."
- : "Texte explicatif du interoperable mode : lorem ipsum dolor sit amet, consectetur adipiscing elit."
- + " Etiam velit sapien, egestas sit amet dictum eget, condimentum a ligula."),
- titleFirstButton: nil,
- actionFirstButton: {},
- titleSecondButton: Text("dialog_close"),
- actionSecondButton: {
- self.isShowPopup.toggle()
- }
+ PopupView(
+ isShowPopup: $isShowPopup,
+ title: Text(isShowPopupForDefault ? "Default mode" : "Interoperable mode"),
+ content: Text(
+ isShowPopupForDefault
+ ? "Texte explicatif du default mode : lorem ipsum dolor sit amet, consectetur adipiscing elit."
+ + "Etiam velit sapien, egestas sit amet dictum eget, condimentum a ligula."
+ : "Texte explicatif du interoperable mode : lorem ipsum dolor sit amet, consectetur adipiscing elit."
+ + " Etiam velit sapien, egestas sit amet dictum eget, condimentum a ligula."),
+ titleFirstButton: nil,
+ actionFirstButton: {},
+ titleSecondButton: nil,
+ actionSecondButton: {},
+ titleThirdButton: Text("dialog_close"),
+ actionThirdButton: { self.isShowPopup.toggle() }
)
.background(.black.opacity(0.65))
.onTapGesture {
diff --git a/Linphone/UI/Assistant/Fragments/RegisterFragment.swift b/Linphone/UI/Assistant/Fragments/RegisterFragment.swift
index 69f42192e..2d5179eb7 100644
--- a/Linphone/UI/Assistant/Fragments/RegisterFragment.swift
+++ b/Linphone/UI/Assistant/Fragments/RegisterFragment.swift
@@ -57,22 +57,21 @@ struct RegisterFragment: View {
let titlePopup = Text("assistant_dialog_confirm_phone_number_title")
let contentPopup = Text(String(format: String(localized: "assistant_dialog_confirm_phone_number_message"), registerViewModel.phoneNumber))
-
PopupView(
isShowPopup: $isShowPopup,
title: titlePopup,
content: contentPopup,
- titleFirstButton: Text("dialog_cancel"),
- actionFirstButton: {
- self.isShowPopup = false
- },
+ titleFirstButton: nil,
+ actionFirstButton: {},
titleSecondButton: Text("dialog_continue"),
actionSecondButton: {
self.isShowPopup = false
registerViewModel.createInProgress = true
registerViewModel.startAccountCreation()
registerViewModel.phoneNumberConfirmedByUser()
- }
+ },
+ titleThirdButton: Text("dialog_cancel"),
+ actionThirdButton: { self.isShowPopup = false },
)
.background(.black.opacity(0.65))
.onTapGesture {
diff --git a/Linphone/UI/Call/Fragments/CallsListFragment.swift b/Linphone/UI/Call/Fragments/CallsListFragment.swift
index 2c670cb87..d6211a6a4 100644
--- a/Linphone/UI/Call/Fragments/CallsListFragment.swift
+++ b/Linphone/UI/Call/Fragments/CallsListFragment.swift
@@ -103,17 +103,21 @@ struct CallsListFragment: View {
.background(.white)
if self.isShowPopup {
- PopupView(isShowPopup: $isShowPopup,
- title: Text("calls_list_dialog_merge_into_conference_title"),
- content: nil,
- titleFirstButton: Text("dialog_cancel"),
- actionFirstButton: {self.isShowPopup.toggle()},
- titleSecondButton: Text("calls_list_dialog_merge_into_conference_label"),
- actionSecondButton: {
- callViewModel.mergeCallsIntoConference()
- self.isShowPopup.toggle()
- isShowCallsListFragment.toggle()
- })
+ PopupView(
+ isShowPopup: $isShowPopup,
+ title: Text("calls_list_dialog_merge_into_conference_title"),
+ content: nil,
+ titleFirstButton: nil,
+ actionFirstButton: {},
+ titleSecondButton: Text("calls_list_dialog_merge_into_conference_label"),
+ actionSecondButton: {
+ callViewModel.mergeCallsIntoConference()
+ self.isShowPopup.toggle()
+ isShowCallsListFragment.toggle()
+ },
+ titleThirdButton: Text("dialog_cancel"),
+ actionThirdButton: { self.isShowPopup.toggle() },
+ )
.background(.black.opacity(0.65))
.onTapGesture {
self.isShowPopup.toggle()
diff --git a/Linphone/UI/Call/Fragments/ParticipantsListFragment.swift b/Linphone/UI/Call/Fragments/ParticipantsListFragment.swift
index 756670ee5..5478a6533 100644
--- a/Linphone/UI/Call/Fragments/ParticipantsListFragment.swift
+++ b/Linphone/UI/Call/Fragments/ParticipantsListFragment.swift
@@ -108,17 +108,21 @@ struct ParticipantsListFragment: View {
if self.isShowPopup {
let contentPopup = Text(String(format: String(localized: "meeting_call_remove_participant_confirmation_message"), callViewModel.participantList[indexToRemove].name))
- PopupView(isShowPopup: $isShowPopup,
- title: Text("meeting_call_remove_participant_confirmation_title"),
- content: contentPopup,
- titleFirstButton: Text("dialog_no"),
- actionFirstButton: {self.isShowPopup.toggle()},
- titleSecondButton: Text("dialog_yes"),
- actionSecondButton: {
- callViewModel.removeParticipant(index: indexToRemove)
- self.isShowPopup.toggle()
- indexToRemove = -1
- })
+ PopupView(
+ isShowPopup: $isShowPopup,
+ title: Text("meeting_call_remove_participant_confirmation_title"),
+ content: contentPopup,
+ titleFirstButton: nil,
+ actionFirstButton: {},
+ titleSecondButton: Text("dialog_yes"),
+ actionSecondButton: {
+ callViewModel.removeParticipant(index: indexToRemove)
+ self.isShowPopup.toggle()
+ indexToRemove = -1
+ },
+ titleThirdButton: Text("dialog_no"),
+ actionThirdButton: { self.isShowPopup.toggle() }
+ )
.background(.black.opacity(0.65))
.onTapGesture {
self.isShowPopup.toggle()
diff --git a/Linphone/UI/Main/Contacts/Fragments/ContactInnerActionsFragment.swift b/Linphone/UI/Main/Contacts/Fragments/ContactInnerActionsFragment.swift
index cdd0f4b98..fe1c0c62d 100644
--- a/Linphone/UI/Main/Contacts/Fragments/ContactInnerActionsFragment.swift
+++ b/Linphone/UI/Main/Contacts/Fragments/ContactInnerActionsFragment.swift
@@ -156,7 +156,7 @@ struct ContactInnerActionsFragment: View {
.background(.white)
.onTapGesture {
CoreContext.shared.doOnCoreQueue { core in
- let address = core.interpretUrl(url: contactAvatarModel.phoneNumbersWithLabel[index].phoneNumber, applyInternationalPrefix: true)
+ let address = core.interpretUrl(url: contactAvatarModel.phoneNumbersWithLabel[index].phoneNumber, applyInternationalPrefix: LinphoneUtils.applyInternationalPrefix(core: core))
if address != nil {
TelecomManager.shared.doCallOrJoinConf(address: address!)
}
diff --git a/Linphone/UI/Main/Contacts/Fragments/ContactInnerFragment.swift b/Linphone/UI/Main/Contacts/Fragments/ContactInnerFragment.swift
index c920aba7b..bece283f3 100644
--- a/Linphone/UI/Main/Contacts/Fragments/ContactInnerFragment.swift
+++ b/Linphone/UI/Main/Contacts/Fragments/ContactInnerFragment.swift
@@ -153,7 +153,7 @@ struct ContactInnerFragment: View {
Log.error("[ContactInnerFragment] unable to create address for a new outgoing call : \(contactAvatarModel.address) \(error) ")
}
} else if contactAvatarModel.addresses.count < 1 && contactAvatarModel.phoneNumbersWithLabel.count == 1 {
- if let firstPhoneNumbersWithLabel = contactAvatarModel.phoneNumbersWithLabel.first, let address = core.interpretUrl(url: firstPhoneNumbersWithLabel.phoneNumber, applyInternationalPrefix: true) {
+ if let firstPhoneNumbersWithLabel = contactAvatarModel.phoneNumbersWithLabel.first, let address = core.interpretUrl(url: firstPhoneNumbersWithLabel.phoneNumber, applyInternationalPrefix: LinphoneUtils.applyInternationalPrefix(core: core)) {
telecomManager.doCallOrJoinConf(address: address, isVideo: false)
}
} else {
@@ -194,7 +194,7 @@ struct ContactInnerFragment: View {
Log.error("[ContactInnerFragment] unable to create address for a new outgoing call : \(contactAvatarModel.address) \(error) ")
}
} else if contactAvatarModel.addresses.count < 1 && contactAvatarModel.phoneNumbersWithLabel.count == 1 {
- if let firstPhoneNumbersWithLabel = contactAvatarModel.phoneNumbersWithLabel.first, let address = core.interpretUrl(url: firstPhoneNumbersWithLabel.phoneNumber, applyInternationalPrefix: true) {
+ if let firstPhoneNumbersWithLabel = contactAvatarModel.phoneNumbersWithLabel.first, let address = core.interpretUrl(url: firstPhoneNumbersWithLabel.phoneNumber, applyInternationalPrefix: LinphoneUtils.applyInternationalPrefix(core: core)) {
contactsListViewModel.createOneToOneChatRoomWith(remote: address)
}
} else {
@@ -235,7 +235,7 @@ struct ContactInnerFragment: View {
Log.error("[ContactInnerFragment] unable to create address for a new outgoing call : \(contactAvatarModel.address) \(error) ")
}
} else if contactAvatarModel.addresses.count < 1 && contactAvatarModel.phoneNumbersWithLabel.count == 1 {
- if let firstPhoneNumbersWithLabel = contactAvatarModel.phoneNumbersWithLabel.first, let address = core.interpretUrl(url: firstPhoneNumbersWithLabel.phoneNumber, applyInternationalPrefix: true) {
+ if let firstPhoneNumbersWithLabel = contactAvatarModel.phoneNumbersWithLabel.first, let address = core.interpretUrl(url: firstPhoneNumbersWithLabel.phoneNumber, applyInternationalPrefix: LinphoneUtils.applyInternationalPrefix(core: core)) {
telecomManager.doCallOrJoinConf(address: address, isVideo: true)
}
} else {
diff --git a/Linphone/UI/Main/Contacts/Fragments/ContactsInnerFragment.swift b/Linphone/UI/Main/Contacts/Fragments/ContactsInnerFragment.swift
index 8d6c362d9..dc52c0c62 100644
--- a/Linphone/UI/Main/Contacts/Fragments/ContactsInnerFragment.swift
+++ b/Linphone/UI/Main/Contacts/Fragments/ContactsInnerFragment.swift
@@ -24,6 +24,7 @@ struct ContactsInnerFragment: View {
@ObservedObject var sharedMainViewModel = SharedMainViewModel.shared
@ObservedObject var contactsManager = ContactsManager.shared
+ @ObservedObject var magicSearch = MagicSearchSingleton.shared
@EnvironmentObject var contactsListViewModel: ContactsListViewModel
@@ -33,76 +34,84 @@ struct ContactsInnerFragment: View {
@Binding var text: String
var body: some View {
- VStack(alignment: .leading) {
- if contactsManager.avatarListModel.contains(where: { $0.starred }) {
- HStack(alignment: .center) {
- Text("contacts_list_favourites_title")
- .default_text_style_800(styleSize: 16)
-
- Spacer()
-
- Image(isFavoriteOpen ? "caret-up" : "caret-down")
- .renderingMode(.template)
- .resizable()
- .foregroundStyle(Color.grayMain2c600)
- .frame(width: 25, height: 25, alignment: .leading)
- .padding(.all, 10)
- }
- .padding(.top, 10)
- .padding(.horizontal, 16)
- .background(.white)
- .onTapGesture {
- withAnimation {
- isFavoriteOpen.toggle()
+ ZStack {
+ VStack(alignment: .leading) {
+ if contactsManager.avatarListModel.contains(where: { $0.starred }) {
+ HStack(alignment: .center) {
+ Text("contacts_list_favourites_title")
+ .default_text_style_800(styleSize: 16)
+
+ Spacer()
+
+ Image(isFavoriteOpen ? "caret-up" : "caret-down")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.grayMain2c600)
+ .frame(width: 25, height: 25, alignment: .leading)
+ .padding(.all, 10)
}
- }
-
- if isFavoriteOpen {
- FavoriteContactsListFragment(showingSheet: $showingSheet)
- .zIndex(-1)
- .transition(.move(edge: .top))
- }
-
- HStack(alignment: .center) {
- Text("contacts_list_all_contacts_title")
- .default_text_style_800(styleSize: 16)
-
- Spacer()
- }
- .padding(.top, 10)
- .padding(.horizontal, 16)
- }
-
- VStack {
- List {
- ContactsListFragment(showingSheet: $showingSheet, startCallFunc: {_ in })}
- .safeAreaInset(edge: .top, content: {
- Spacer()
- .frame(height: 12)
- })
- .listStyle(.plain)
- .if(sharedMainViewModel.cardDavFriendsListsCount > 0) { view in
- view.refreshable {
- contactsManager.refreshCardDavContacts()
- }
- }
- .overlay(
- VStack {
- if contactsManager.avatarListModel.isEmpty {
- Spacer()
- Image("illus-belledonne")
- .resizable()
- .scaledToFit()
- .clipped()
- .padding(.all)
- Text(!text.isEmpty ? "list_filter_no_result_found" : "contacts_list_empty")
- .default_text_style_800(styleSize: 16)
- Spacer()
- Spacer()
+ .padding(.top, 10)
+ .padding(.horizontal, 16)
+ .background(.white)
+ .onTapGesture {
+ withAnimation {
+ isFavoriteOpen.toggle()
}
}
- .padding(.all)
- )
+
+ if isFavoriteOpen {
+ FavoriteContactsListFragment(showingSheet: $showingSheet)
+ .zIndex(-1)
+ .transition(.move(edge: .top))
+ }
+
+ HStack(alignment: .center) {
+ Text("contacts_list_all_contacts_title")
+ .default_text_style_800(styleSize: 16)
+
+ Spacer()
+ }
+ .padding(.top, 10)
+ .padding(.horizontal, 16)
+ }
+
+ VStack {
+ List {
+ ContactsListFragment(showingSheet: $showingSheet, startCallFunc: {_ in })}
+ .safeAreaInset(edge: .top, content: {
+ Spacer()
+ .frame(height: 12)
+ })
+ .listStyle(.plain)
+ .if(sharedMainViewModel.cardDavFriendsListsCount > 0) { view in
+ view.refreshable {
+ contactsManager.refreshCardDavContacts()
+ }
+ }
+ .overlay(
+ VStack {
+ if contactsManager.avatarListModel.isEmpty {
+ Spacer()
+ Image("illus-belledonne")
+ .resizable()
+ .scaledToFit()
+ .clipped()
+ .padding(.all)
+ Text(!text.isEmpty ? "list_filter_no_result_found" : "contacts_list_empty")
+ .default_text_style_800(styleSize: 16)
+ Spacer()
+ Spacer()
+ }
+ }
+ .padding(.all)
+ )
+ }
+ }
+
+ if magicSearch.isLoading {
+ ProgressView()
+ .controlSize(.large)
+ .progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500))
}
}
.navigationBarHidden(true)
diff --git a/Linphone/UI/Main/ContentView.swift b/Linphone/UI/Main/ContentView.swift
index 271716a7e..f72ffe2dd 100644
--- a/Linphone/UI/Main/ContentView.swift
+++ b/Linphone/UI/Main/ContentView.swift
@@ -60,6 +60,7 @@ struct ContentView: View {
@State var isShowDismissPopup = false
@State var isShowSendCancelMeetingNotificationPopup = false
@State var isShowStartCallGroupPopup = false
+ @State var isShowDeleteMessagePopup = false
@State var isShowSipAddressesPopup = false
@State var isShowSipAddressesPopupType = 0 // 0 to call, 1 to message, 2 to video call
@State var isShowConversationFragment = false
@@ -545,7 +546,7 @@ struct ContentView: View {
magicSearch.searchForContacts()
} label: {
HStack {
- Text(String(format: String(localized: "contacts_list_filter_popup_see_linphone_only"), Bundle.main.displayName))
+ Text(magicSearch.domainDefaultAccount == "*" ? String(localized: "contacts_list_filter_popup_see_sip_only") : String(format: String(localized: "contacts_list_filter_popup_see_linphone_only"), Bundle.main.displayName))
Spacer()
if !magicSearch.allContact {
Image("green-check")
@@ -988,6 +989,7 @@ struct ContentView: View {
ConversationFragment(
isShowConversationFragment: $isShowConversationFragment,
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
+ isShowDeleteMessagePopup: $isShowDeleteMessagePopup,
isShowEditContactFragment: $isShowEditContactFragment,
isShowEditContactFragmentAddress: $isShowEditContactFragmentAddress,
isShowScheduleMeetingFragment: $isShowScheduleMeetingFragment,
@@ -1113,14 +1115,16 @@ struct ContentView: View {
)
),
content: Text("contact_dialog_delete_message"),
- titleFirstButton: Text("dialog_cancel"),
- actionFirstButton: {
- self.isShowDeleteContactPopup.toggle()},
- titleSecondButton: Text("dialog_ok"),
+ titleFirstButton: nil,
+ actionFirstButton: {},
+ titleSecondButton: Text("dialog_confirm"),
actionSecondButton: {
contactsListVM.deleteSelectedContact()
self.isShowDeleteContactPopup.toggle()
- })
+ },
+ titleThirdButton: Text("dialog_cancel"),
+ actionThirdButton: { self.isShowDeleteContactPopup.toggle() }
+ )
.background(.black.opacity(0.65))
.zIndex(3)
.onTapGesture {
@@ -1132,27 +1136,31 @@ struct ContentView: View {
}
if isShowDeleteAllHistoryPopup {
- PopupView(isShowPopup: $isShowDeleteContactPopup,
- title: Text("history_dialog_delete_all_call_logs_title"),
- content: Text("history_dialog_delete_all_call_logs_message"),
- titleFirstButton: Text("dialog_cancel"),
- actionFirstButton: {
- self.isShowDeleteAllHistoryPopup.toggle()
- if let historyListVM = historyListViewModel {
- historyListVM.callLogsAddressToDelete = ""
+ PopupView(
+ isShowPopup: $isShowDeleteContactPopup,
+ title: Text("history_dialog_delete_all_call_logs_title"),
+ content: Text("history_dialog_delete_all_call_logs_message"),
+ titleFirstButton: nil,
+ actionFirstButton: {},
+ titleSecondButton: Text("dialog_confirm"),
+ actionSecondButton: {
+ if let historyListVM = historyListViewModel {
+ historyListVM.removeCallLogs()
+ }
+ self.isShowDeleteAllHistoryPopup.toggle()
+ sharedMainViewModel.displayedCall = nil
+
+ ToastViewModel.shared.toastMessage = "Success_remove_call_logs"
+ ToastViewModel.shared.displayToast.toggle()
+ },
+ titleThirdButton: Text("dialog_cancel"),
+ actionThirdButton: {
+ self.isShowDeleteAllHistoryPopup.toggle()
+ if let historyListVM = historyListViewModel {
+ historyListVM.callLogsAddressToDelete = ""
+ }
}
- },
- titleSecondButton: Text("dialog_ok"),
- actionSecondButton: {
- if let historyListVM = historyListViewModel {
- historyListVM.removeCallLogs()
- }
- self.isShowDeleteAllHistoryPopup.toggle()
- sharedMainViewModel.displayedCall = nil
-
- ToastViewModel.shared.toastMessage = "Success_remove_call_logs"
- ToastViewModel.shared.displayToast.toggle()
- })
+ )
.background(.black.opacity(0.65))
.zIndex(3)
.onTapGesture {
@@ -1161,20 +1169,24 @@ struct ContentView: View {
}
if isShowDismissPopup {
- PopupView(isShowPopup: $isShowDismissPopup,
- title: Text("contact_editor_dialog_abort_confirmation_title"),
- content: Text("contact_editor_dialog_abort_confirmation_message"),
- titleFirstButton: Text("dialog_cancel"),
- actionFirstButton: {self.isShowDismissPopup.toggle()},
- titleSecondButton: Text("dialog_ok"),
- actionSecondButton: {
- self.isShowDismissPopup.toggle()
- if isShowEditContactFragment {
- isShowEditContactFragment = false
- } else {
- isShowEditContactFragmentInContactDetails = false
- }
- })
+ PopupView(
+ isShowPopup: $isShowDismissPopup,
+ title: Text("contact_editor_dialog_abort_confirmation_title"),
+ content: Text("contact_editor_dialog_abort_confirmation_message"),
+ titleFirstButton: nil,
+ actionFirstButton: {},
+ titleSecondButton: Text("dialog_confirm"),
+ actionSecondButton: {
+ self.isShowDismissPopup.toggle()
+ if isShowEditContactFragment {
+ isShowEditContactFragment = false
+ } else {
+ isShowEditContactFragmentInContactDetails = false
+ }
+ },
+ titleThirdButton: Text("dialog_cancel"),
+ actionThirdButton: { self.isShowDismissPopup.toggle() }
+ )
.background(.black.opacity(0.65))
.zIndex(3)
.onTapGesture {
@@ -1304,21 +1316,25 @@ struct ContentView: View {
}
if let meetingsListVM = meetingsListViewModel, isShowSendCancelMeetingNotificationPopup {
- PopupView(isShowPopup: $isShowSendCancelMeetingNotificationPopup,
- title: Text("meeting_schedule_cancel_dialog_title"),
- content: !sharedMainViewModel.disableChatFeature ? Text("meeting_schedule_cancel_dialog_message") : Text(""),
- titleFirstButton: Text("dialog_cancel"),
- actionFirstButton: {
- sharedMainViewModel.displayedMeeting = nil
- meetingsListVM.deleteSelectedMeeting()
- self.isShowSendCancelMeetingNotificationPopup.toggle(
- ) },
- titleSecondButton: Text("dialog_ok"),
- actionSecondButton: {
- sharedMainViewModel.displayedMeeting = nil
- meetingsListVM.cancelMeetingWithNotifications()
- self.isShowSendCancelMeetingNotificationPopup.toggle()
- })
+ PopupView(
+ isShowPopup: $isShowSendCancelMeetingNotificationPopup,
+ title: Text("meeting_schedule_cancel_dialog_title"),
+ content: !sharedMainViewModel.disableChatFeature ? Text("meeting_schedule_cancel_dialog_message") : Text(""),
+ titleFirstButton: nil,
+ actionFirstButton: {},
+ titleSecondButton: Text("dialog_confirm"),
+ actionSecondButton: {
+ sharedMainViewModel.displayedMeeting = nil
+ meetingsListVM.cancelMeetingWithNotifications()
+ self.isShowSendCancelMeetingNotificationPopup.toggle()
+ },
+ titleThirdButton: Text("dialog_cancel"),
+ actionThirdButton: {
+ sharedMainViewModel.displayedMeeting = nil
+ meetingsListVM.deleteSelectedMeeting()
+ self.isShowSendCancelMeetingNotificationPopup.toggle()
+ }
+ )
.background(.black.opacity(0.65))
.zIndex(3)
.onTapGesture {
@@ -1331,17 +1347,17 @@ struct ContentView: View {
isShowPopup: $isShowStartCallGroupPopup,
title: Text("conversation_info_confirm_start_group_call_dialog_title"),
content: Text("conversation_info_confirm_start_group_call_dialog_message"),
- titleFirstButton: Text("dialog_cancel"),
- actionFirstButton: {
- self.isShowStartCallGroupPopup.toggle()
- },
- titleSecondButton: Text("dialog_ok"),
+ titleFirstButton: nil,
+ actionFirstButton: {},
+ titleSecondButton: Text("dialog_confirm"),
actionSecondButton: {
if sharedMainViewModel.displayedConversation != nil {
sharedMainViewModel.displayedConversation!.createGroupCall()
}
self.isShowStartCallGroupPopup.toggle()
- }
+ },
+ titleThirdButton: Text("dialog_cancel"),
+ actionThirdButton: { self.isShowStartCallGroupPopup.toggle() }
)
.background(.black.opacity(0.65))
.zIndex(3)
@@ -1350,6 +1366,31 @@ struct ContentView: View {
}
}
+ if isShowDeleteMessagePopup {
+ PopupView(
+ isShowPopup: $isShowDeleteMessagePopup,
+ title: Text("conversation_dialog_delete_chat_message_title"),
+ content: nil,
+ titleFirstButton: Text("conversation_dialog_delete_for_everyone_label"),
+ actionFirstButton: {
+ NotificationCenter.default.post(name: NSNotification.Name("DeleteMessageForEveryone"), object: nil)
+ self.isShowDeleteMessagePopup.toggle()
+ },
+ titleSecondButton: Text("conversation_dialog_delete_locally_label"),
+ actionSecondButton: {
+ NotificationCenter.default.post(name: NSNotification.Name("DeleteMessageForMe"), object: nil)
+ self.isShowDeleteMessagePopup.toggle()
+ },
+ titleThirdButton: Text("dialog_cancel"),
+ actionThirdButton: { self.isShowDeleteMessagePopup.toggle() }
+ )
+ .background(.black.opacity(0.65))
+ .zIndex(3)
+ .onTapGesture {
+ self.isShowDeleteMessagePopup.toggle()
+ }
+ }
+
if isShowConversationInfoPopup {
PopupViewWithTextField(
isShowConversationInfoPopup: $isShowConversationInfoPopup,
diff --git a/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift b/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift
index 368817e12..5c489df4a 100644
--- a/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift
+++ b/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift
@@ -52,7 +52,7 @@ struct ChatBubbleView: View {
HStack {
if eventLogMessage.eventModel.eventLogType == .ConferenceChatMessage {
VStack {
- if !eventLogMessage.message.text.isEmpty || !eventLogMessage.message.attachments.isEmpty || eventLogMessage.message.isIcalendar {
+ if !eventLogMessage.message.text.isEmpty || !eventLogMessage.message.attachments.isEmpty || eventLogMessage.message.isIcalendar || eventLogMessage.message.isRetracted {
HStack(alignment: .top, content: {
if eventLogMessage.message.isOutgoing {
Spacer()
@@ -137,6 +137,12 @@ struct ChatBubbleView: View {
.foregroundStyle(Color.grayMain2c700)
.default_text_style(styleSize: 14)
.lineLimit(/*@START_MENU_TOKEN@*/2/*@END_MENU_TOKEN@*/)
+ } else if eventLogMessage.message.replyMessage!.isRetracted {
+ Text(eventLogMessage.message.replyMessage!.isOutgoing ? "conversation_message_content_deleted_by_us_label" : "conversation_message_content_deleted_label")
+ .italic()
+ .foregroundStyle(Color.grayMain2c500)
+ .font(.system(size: 14))
+ .lineLimit(1)
}
}
.padding(.all, 15)
@@ -174,6 +180,12 @@ struct ChatBubbleView: View {
if !eventLogMessage.message.text.isEmpty {
DynamicLinkText(text: eventLogMessage.message.text)
+ } else if eventLogMessage.message.isRetracted {
+ Text(eventLogMessage.message.isOutgoing ? "conversation_message_content_deleted_by_us_label" : "conversation_message_content_deleted_label")
+ .italic()
+ .foregroundStyle(Color.grayMain2c500)
+ .font(.system(size: 14))
+ .lineLimit(1)
}
if eventLogMessage.message.isIcalendar && eventLogMessage.message.messageConferenceInfo != nil {
@@ -325,6 +337,14 @@ struct ChatBubbleView: View {
.padding(.top, 1)
}
+ if eventLogMessage.message.isEdited && eventLogMessage.message.isOutgoing {
+ Text("conversation_message_edited_label")
+ .foregroundStyle(Color.grayMain2c500)
+ .default_text_style_300(styleSize: 12)
+ .padding(.top, 1)
+ .padding(.trailing, -4)
+ }
+
Text(conversationViewModel.getMessageTime(startDate: eventLogMessage.message.dateReceived))
.foregroundStyle(Color.grayMain2c500)
.default_text_style_300(styleSize: 12)
@@ -349,6 +369,14 @@ struct ChatBubbleView: View {
}
}
+ if eventLogMessage.message.isEdited && !eventLogMessage.message.isOutgoing {
+ Text("conversation_message_edited_label")
+ .foregroundStyle(Color.grayMain2c500)
+ .default_text_style_300(styleSize: 12)
+ .padding(.top, 1)
+ .padding(.trailing, -4)
+ }
+
if eventLogMessage.message.isEphemeral && !eventLogMessage.message.isOutgoing {
Image("clock-countdown")
.renderingMode(.template)
diff --git a/Linphone/UI/Main/Conversations/Fragments/ConversationForwardMessageFragment.swift b/Linphone/UI/Main/Conversations/Fragments/ConversationForwardMessageFragment.swift
index 5a41b038d..403f0a14b 100644
--- a/Linphone/UI/Main/Conversations/Fragments/ConversationForwardMessageFragment.swift
+++ b/Linphone/UI/Main/Conversations/Fragments/ConversationForwardMessageFragment.swift
@@ -141,50 +141,58 @@ struct ConversationForwardMessageFragment: View {
.padding(.vertical)
.padding(.horizontal)
- ScrollView {
- if !conversationForwardMessageViewModel.conversationsList.isEmpty {
- HStack(alignment: .center) {
- Text("bottom_navigation_conversations_label")
- .default_text_style_800(styleSize: 16)
+ ZStack {
+ ScrollView {
+ if !conversationForwardMessageViewModel.conversationsList.isEmpty {
+ HStack(alignment: .center) {
+ Text("bottom_navigation_conversations_label")
+ .default_text_style_800(styleSize: 16)
+
+ Spacer()
+ }
+ .padding(.vertical, 10)
+ .padding(.horizontal, 16)
- Spacer()
+ conversationsList
}
- .padding(.vertical, 10)
+
+ if !ContactsManager.shared.lastSearch.isEmpty {
+ HStack(alignment: .center) {
+ Text("contacts_list_all_contacts_title")
+ .default_text_style_800(styleSize: 16)
+
+ Spacer()
+ }
+ .padding(.vertical, 10)
+ .padding(.horizontal, 16)
+ }
+
+ ContactsListFragment(showingSheet: .constant(false), startCallFunc: { addr in
+ withAnimation {
+ conversationForwardMessageViewModel.createOneToOneChatRoomWith(remote: addr)
+ }
+
+ })
.padding(.horizontal, 16)
- conversationsList
+ if !contactsManager.lastSearchSuggestions.isEmpty {
+ HStack(alignment: .center) {
+ Text("generic_address_picker_suggestions_list_title")
+ .default_text_style_800(styleSize: 16)
+
+ Spacer()
+ }
+ .padding(.vertical, 10)
+ .padding(.horizontal, 16)
+
+ suggestionsList
+ }
}
- if !ContactsManager.shared.lastSearch.isEmpty {
- HStack(alignment: .center) {
- Text("contacts_list_all_contacts_title")
- .default_text_style_800(styleSize: 16)
-
- Spacer()
- }
- .padding(.vertical, 10)
- .padding(.horizontal, 16)
- }
-
- ContactsListFragment(showingSheet: .constant(false), startCallFunc: { addr in
- withAnimation {
- conversationForwardMessageViewModel.createOneToOneChatRoomWith(remote: addr)
- }
-
- })
- .padding(.horizontal, 16)
-
- if !contactsManager.lastSearchSuggestions.isEmpty {
- HStack(alignment: .center) {
- Text("generic_address_picker_suggestions_list_title")
- .default_text_style_800(styleSize: 16)
-
- Spacer()
- }
- .padding(.vertical, 10)
- .padding(.horizontal, 16)
-
- suggestionsList
+ if magicSearch.isLoading {
+ ProgressView()
+ .controlSize(.large)
+ .progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500))
}
}
}
diff --git a/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift b/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift
index 25b67299a..8faf53e6f 100644
--- a/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift
+++ b/Linphone/UI/Main/Conversations/Fragments/ConversationFragment.swift
@@ -64,6 +64,7 @@ struct ConversationFragment: View {
@Binding var isShowConversationFragment: Bool
@Binding var isShowStartCallGroupPopup: Bool
+ @Binding var isShowDeleteMessagePopup: Bool
@State private var selectedCategoryIndex = 0
@@ -233,6 +234,8 @@ struct ConversationFragment: View {
if SharedMainViewModel.shared.displayedConversation != nil && (navigationManager.peerAddr == nil || navigationManager.peerAddr!.contains(SharedMainViewModel.shared.displayedConversation!.remoteSipUri)) {
conversationViewModel.resetDisplayedChatRoom()
}
+ } else {
+ conversationViewModel.compose(stop: true, cachedConversation: cachedConversation)
}
}
}
@@ -460,6 +463,7 @@ struct ConversationFragment: View {
}
}
.onDisappear {
+ conversationViewModel.compose(stop: true, cachedConversation: cachedConversation)
conversationViewModel.resetMessage()
}
} else {
@@ -555,6 +559,7 @@ struct ConversationFragment: View {
conversationViewModel.getMessages()
}
.onDisappear {
+ conversationViewModel.compose(stop: true, cachedConversation: cachedConversation)
conversationViewModel.resetMessage()
}
}
@@ -620,6 +625,43 @@ struct ConversationFragment: View {
}
}
.transition(.move(edge: .bottom))
+ } else if conversationViewModel.messageToEdit != nil {
+ ZStack(alignment: .top) {
+ HStack {
+ VStack {
+ Text("conversation_editing_message_title")
+ .default_text_style_300(styleSize: 15)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .padding(.bottom, 1)
+ .lineLimit(1)
+
+ Text("\(conversationViewModel.messageToEdit!.message.text)")
+ .default_text_style_300(styleSize: 15)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .lineLimit(1)
+ }
+ }
+ .frame(maxWidth: .infinity)
+ .padding(.all, 20)
+ .background(Color.gray100)
+
+ HStack {
+ Spacer()
+
+ Button(action: {
+ messageText = ""
+ withAnimation {
+ conversationViewModel.messageToEdit = nil
+ }
+ }, label: {
+ Image("x")
+ .resizable()
+ .frame(width: 30, height: 30, alignment: .leading)
+ .padding(.all, 10)
+ })
+ }
+ }
+ .transition(.move(edge: .bottom))
}
if !conversationViewModel.mediasToSend.isEmpty || mediasIsLoading {
@@ -847,9 +889,7 @@ struct ConversationFragment: View {
.focused($isMessageTextFocused)
.padding(.vertical, 5)
.onChange(of: messageText) { text in
- if !text.isEmpty {
- conversationViewModel.compose()
- }
+ conversationViewModel.compose(stop: text.isEmpty)
}
} else {
ZStack(alignment: .leading) {
@@ -860,9 +900,7 @@ struct ConversationFragment: View {
.default_text_style(styleSize: 15)
.focused($isMessageTextFocused)
.onChange(of: messageText) { text in
- if !text.isEmpty {
- conversationViewModel.compose()
- }
+ conversationViewModel.compose(stop: text.isEmpty)
}
if messageText.isEmpty {
@@ -879,43 +917,66 @@ struct ConversationFragment: View {
}
}
- if messageText.isEmpty && conversationViewModel.mediasToSend.isEmpty {
- Button {
- voiceRecordingInProgress = true
- } label: {
- Image("microphone")
- .renderingMode(.template)
- .resizable()
- .foregroundStyle(Color.grayMain2c500)
- .frame(width: 28, height: 28, alignment: .leading)
- .padding(.all, 6)
- .padding(.top, 4)
+ if conversationViewModel.messageToEdit == nil {
+ if messageText.isEmpty && conversationViewModel.mediasToSend.isEmpty {
+ Button {
+ voiceRecordingInProgress = true
+ } label: {
+ Image("microphone")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.grayMain2c500)
+ .frame(width: 28, height: 28, alignment: .leading)
+ .padding(.all, 6)
+ .padding(.top, 4)
+ }
+ } else {
+ Button {
+ if conversationViewModel.displayedConversationHistorySize > 1 {
+ NotificationCenter.default.post(name: .onScrollToBottom, object: nil)
+ }
+
+ let messageTextTmp = self.messageText
+ messageText = " "
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
+ messageText = ""
+ isMessageTextFocused = true
+
+ conversationViewModel.sendMessage(messageText: messageTextTmp)
+ }
+ } label: {
+ Image("paper-plane-tilt")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.orangeMain500)
+ .frame(width: 28, height: 28, alignment: .leading)
+ .padding(.all, 6)
+ .padding(.top, 4)
+ .rotationEffect(.degrees(45))
+ }
+ .padding(.trailing, 4)
}
} else {
Button {
- if conversationViewModel.displayedConversationHistorySize > 1 {
- NotificationCenter.default.post(name: .onScrollToBottom, object: nil)
- }
-
let messageTextTmp = self.messageText
- messageText = " "
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
- messageText = ""
- isMessageTextFocused = true
-
- conversationViewModel.sendMessage(messageText: messageTextTmp)
- }
+ messageText = " "
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
+ messageText = ""
+ isMessageTextFocused = true
+
+ conversationViewModel.sendMessage(messageText: messageTextTmp)
+ }
} label: {
- Image("paper-plane-tilt")
+ Image("pencil-simple")
.renderingMode(.template)
.resizable()
- .foregroundStyle(Color.orangeMain500)
+ .foregroundStyle(messageText.isEmpty ? Color.gray300 : Color.orangeMain500)
.frame(width: 28, height: 28, alignment: .leading)
.padding(.all, 6)
.padding(.top, 4)
- .rotationEffect(.degrees(45))
}
.padding(.trailing, 4)
+ .disabled(messageText.isEmpty)
}
}
.padding(.leading, 15)
@@ -1096,28 +1157,67 @@ struct ConversationFragment: View {
Divider()
}
-
- Button {
- let indexMessage = conversationViewModel.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.id == conversationViewModel.selectedMessage!.message.id})
- conversationViewModel.selectedMessage = nil
- conversationViewModel.replyToMessage(index: indexMessage ?? 0, isMessageTextFocused: Binding(
- get: { isMessageTextFocused },
- set: { isMessageTextFocused = $0 }
- ))
- } label: {
- HStack {
- Text("menu_reply_to_chat_message")
- .default_text_style(styleSize: 15)
- Spacer()
- Image("reply")
- .resizable()
- .frame(width: 20, height: 20, alignment: .leading)
- }
- .padding(.vertical, 5)
- .padding(.horizontal, 20)
- }
- Divider()
+ if conversationViewModel.selectedMessage!.message.isOutgoing
+ && !(SharedMainViewModel.shared.displayedConversation?.isReadOnly ?? cachedConversation!.isReadOnly)
+ && conversationViewModel.selectedMessage!.message.isEditable {
+ Button {
+ if let chatMessage = conversationViewModel.selectedMessage {
+ if voiceRecordingInProgress {
+ voiceRecordingInProgress = false
+ }
+
+ messageText = chatMessage.message.text
+ conversationViewModel.selectedMessage = nil
+ conversationViewModel.editMessage(
+ chatMessage: chatMessage,
+ isMessageTextFocused: Binding(
+ get: { isMessageTextFocused },
+ set: { isMessageTextFocused = $0 }
+ )
+ )
+ }
+ } label: {
+ HStack {
+ Text("menu_edit_chat_message")
+ .default_text_style(styleSize: 15)
+ Spacer()
+ Image("pencil-simple")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.grayMain2c600)
+ .frame(width: 20, height: 20, alignment: .leading)
+ }
+ .padding(.vertical, 5)
+ .padding(.horizontal, 20)
+ }
+
+ Divider()
+ }
+
+ if !conversationViewModel.selectedMessage!.message.isRetracted {
+ Button {
+ let indexMessage = conversationViewModel.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.id == conversationViewModel.selectedMessage!.message.id})
+ conversationViewModel.selectedMessage = nil
+ conversationViewModel.replyToMessage(index: indexMessage ?? 0, isMessageTextFocused: Binding(
+ get: { isMessageTextFocused },
+ set: { isMessageTextFocused = $0 }
+ ))
+ } label: {
+ HStack {
+ Text("menu_reply_to_chat_message")
+ .default_text_style(styleSize: 15)
+ Spacer()
+ Image("reply")
+ .resizable()
+ .frame(width: 20, height: 20, alignment: .leading)
+ }
+ .padding(.vertical, 5)
+ .padding(.horizontal, 20)
+ }
+
+ Divider()
+ }
if !conversationViewModel.selectedMessage!.message.text.isEmpty {
Button {
@@ -1146,27 +1246,35 @@ struct ConversationFragment: View {
Divider()
}
- Button {
- withAnimation {
- isShowConversationForwardMessageFragment = true
+ if !conversationViewModel.selectedMessage!.message.isRetracted {
+ Button {
+ withAnimation {
+ isShowConversationForwardMessageFragment = true
+ }
+ } label: {
+ HStack {
+ Text("menu_forward_chat_message")
+ .default_text_style(styleSize: 15)
+ Spacer()
+ Image("forward")
+ .resizable()
+ .frame(width: 20, height: 20, alignment: .leading)
+ }
+ .padding(.vertical, 5)
+ .padding(.horizontal, 20)
}
- } label: {
- HStack {
- Text("menu_forward_chat_message")
- .default_text_style(styleSize: 15)
- Spacer()
- Image("forward")
- .resizable()
- .frame(width: 20, height: 20, alignment: .leading)
- }
- .padding(.vertical, 5)
- .padding(.horizontal, 20)
+
+ Divider()
}
- Divider()
-
Button {
- conversationViewModel.deleteMessage()
+ if conversationViewModel.selectedMessage!.message.isOutgoing
+ && !(SharedMainViewModel.shared.displayedConversation?.isReadOnly ?? cachedConversation!.isReadOnly)
+ && conversationViewModel.selectedMessage!.message.isRetractable && !conversationViewModel.selectedMessage!.message.isRetracted {
+ isShowDeleteMessagePopup = true
+ } else {
+ conversationViewModel.deleteMessage()
+ }
} label: {
HStack {
Text("menu_delete_selected_item")
@@ -1211,11 +1319,18 @@ struct ConversationFragment: View {
}
.onAppear {
touchFeedback()
+ if isMessageTextFocused {
+ isMessageTextFocused = false
+ }
}
.onDisappear {
if conversationViewModel.selectedMessage != nil {
conversationViewModel.selectedMessage = nil
}
+ }.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("DeleteMessageForMe"))) { _ in
+ conversationViewModel.deleteMessage()
+ }.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("DeleteMessageForEveryone"))) { _ in
+ conversationViewModel.deleteMessageForEveryone()
}
}
diff --git a/Linphone/UI/Main/Conversations/Fragments/ConversationsListFragment.swift b/Linphone/UI/Main/Conversations/Fragments/ConversationsListFragment.swift
index e9c40e871..d39a0835d 100644
--- a/Linphone/UI/Main/Conversations/Fragments/ConversationsListFragment.swift
+++ b/Linphone/UI/Main/Conversations/Fragments/ConversationsListFragment.swift
@@ -105,14 +105,46 @@ struct ConversationRow: View {
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
- Text(conversation.lastMessageText)
- .foregroundStyle(Color.grayMain2c400)
- .if(conversation.unreadMessagesCount > 0) { view in
- view.default_text_style_700(styleSize: 14)
+ HStack(spacing: 0) {
+ Text(conversation.lastMessagePrefixText)
+ .foregroundStyle(Color.grayMain2c400)
+ .if(conversation.unreadMessagesCount > 0) { view in
+ view.default_text_style_700(styleSize: 14)
+ }
+ .default_text_style(styleSize: 14)
+ .lineLimit(1)
+ .layoutPriority(1)
+
+ if !conversation.lastMessageIcon.isEmpty {
+ Image(conversation.lastMessageIcon)
+ .resizable()
+ .frame(width: 16, height: 16)
+ .layoutPriority(0)
+ .padding(.trailing, 2)
}
- .default_text_style(styleSize: 14)
- .frame(maxWidth: .infinity, alignment: .leading)
- .lineLimit(1)
+
+ if conversation.lastMessageInItalic {
+ Text(conversation.lastMessageText)
+ .italic()
+ .if(conversation.unreadMessagesCount > 0) { view in
+ view.bold()
+ }
+ .foregroundStyle(Color.grayMain2c400)
+ .font(.system(size: 14))
+ .lineLimit(1)
+ .layoutPriority(-1)
+ } else {
+ Text(conversation.lastMessageText)
+ .foregroundStyle(Color.grayMain2c400)
+ .if(conversation.unreadMessagesCount > 0) { view in
+ view.default_text_style_700(styleSize: 14)
+ }
+ .default_text_style(styleSize: 14)
+ .lineLimit(1)
+ .layoutPriority(-1)
+ }
+ }
+ .frame(maxWidth: .infinity, alignment: .leading)
Spacer()
}
diff --git a/Linphone/UI/Main/Conversations/Fragments/StartConversationFragment.swift b/Linphone/UI/Main/Conversations/Fragments/StartConversationFragment.swift
index 55d5dc64b..2a81a536f 100644
--- a/Linphone/UI/Main/Conversations/Fragments/StartConversationFragment.swift
+++ b/Linphone/UI/Main/Conversations/Fragments/StartConversationFragment.swift
@@ -170,34 +170,42 @@ struct StartConversationFragment: View {
)
}
- ScrollView {
- if !ContactsManager.shared.lastSearch.isEmpty {
- HStack(alignment: .center) {
- Text("contacts_list_all_contacts_title")
- .default_text_style_800(styleSize: 16)
-
- Spacer()
+ ZStack {
+ ScrollView {
+ if !ContactsManager.shared.lastSearch.isEmpty {
+ HStack(alignment: .center) {
+ Text("contacts_list_all_contacts_title")
+ .default_text_style_800(styleSize: 16)
+
+ Spacer()
+ }
+ .padding(.vertical, 10)
+ .padding(.horizontal, 16)
}
- .padding(.vertical, 10)
- .padding(.horizontal, 16)
- }
-
- ContactsListFragment(showingSheet: .constant(false), startCallFunc: { addr in
+
+ ContactsListFragment(showingSheet: .constant(false), startCallFunc: { addr in
startConversationViewModel.createOneToOneChatRoomWith(remote: addr)
- })
- .padding(.horizontal, 16)
-
- if !contactsManager.lastSearchSuggestions.isEmpty {
- HStack(alignment: .center) {
- Text("generic_address_picker_suggestions_list_title")
- .default_text_style_800(styleSize: 16)
-
- Spacer()
- }
- .padding(.vertical, 10)
+ })
.padding(.horizontal, 16)
- suggestionsList
+ if !contactsManager.lastSearchSuggestions.isEmpty {
+ HStack(alignment: .center) {
+ Text("generic_address_picker_suggestions_list_title")
+ .default_text_style_800(styleSize: 16)
+
+ Spacer()
+ }
+ .padding(.vertical, 10)
+ .padding(.horizontal, 16)
+
+ suggestionsList
+ }
+ }
+
+ if magicSearch.isLoading {
+ ProgressView()
+ .controlSize(.large)
+ .progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500))
}
}
}
diff --git a/Linphone/UI/Main/Conversations/Fragments/StartGroupConversationFragment.swift b/Linphone/UI/Main/Conversations/Fragments/StartGroupConversationFragment.swift
index 1edbaf478..6963e60c3 100644
--- a/Linphone/UI/Main/Conversations/Fragments/StartGroupConversationFragment.swift
+++ b/Linphone/UI/Main/Conversations/Fragments/StartGroupConversationFragment.swift
@@ -111,7 +111,7 @@ struct StartGroupConversationFragment: View {
Button(action: {
startConversationViewModel.createGroupChatRoom()
}, label: {
- Text("dialog_ok")
+ Text("dialog_confirm")
.default_text_style_white_600(styleSize: 20)
.frame(height: 35)
.frame(maxWidth: .infinity)
diff --git a/Linphone/UI/Main/Conversations/Fragments/UIList.swift b/Linphone/UI/Main/Conversations/Fragments/UIList.swift
index 59622cec2..2b116e379 100644
--- a/Linphone/UI/Main/Conversations/Fragments/UIList.swift
+++ b/Linphone/UI/Main/Conversations/Fragments/UIList.swift
@@ -551,6 +551,12 @@ struct UIList: UIViewRepresentable {
}
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
+ let eventLogMessage = parent.conversationViewModel.conversationMessagesSection[0].rows[indexPath.row]
+
+ guard !eventLogMessage.message.isRetracted && eventLogMessage.eventModel.eventLogType == .ConferenceChatMessage else {
+ return nil
+ }
+
let archiveAction = UIContextualAction(style: .normal, title: "") { _, _, completionHandler in
self.parent.conversationViewModel.replyToMessage(index: indexPath.row, isMessageTextFocused: Binding(
get: { self.parent.isMessageTextFocused },
diff --git a/Linphone/UI/Main/Conversations/Model/ConversationModel.swift b/Linphone/UI/Main/Conversations/Model/ConversationModel.swift
index 399fbede4..584e1c7d9 100644
--- a/Linphone/UI/Main/Conversations/Model/ConversationModel.swift
+++ b/Linphone/UI/Main/Conversations/Model/ConversationModel.swift
@@ -46,9 +46,12 @@ class ConversationModel: ObservableObject, Identifiable {
@Published var isMuted: Bool
@Published var isEphemeral: Bool
@Published var encryptionEnabled: Bool
+ @Published var lastMessagePrefixText: String
@Published var lastMessageText: String
+ @Published var lastMessageIcon: String
@Published var lastMessageIsOutgoing: Bool
@Published var lastMessageState: Int
+ @Published var lastMessageInItalic: Bool
@Published var unreadMessagesCount: Int
@Published var avatarModel: ContactAvatarModel
@@ -138,11 +141,17 @@ class ConversationModel: ObservableObject, Identifiable {
self.lastMessage = nil
+ self.lastMessagePrefixText = ""
+
self.lastMessageText = ""
+ self.lastMessageIcon = ""
+
self.lastMessageIsOutgoing = false
self.lastMessageState = 0
+
+ self.lastMessageInItalic = false
self.unreadMessagesCount = chatRoom.unreadMessagesCount
@@ -294,8 +303,10 @@ class ConversationModel: ObservableObject, Identifiable {
fromAddressFriend = nil
}
- var lastMessageTextTmp = (fromAddressFriend ?? "")
- + (lastMessage!.contents.first(where: {$0.isText == true})?.utf8Text ?? (lastMessage!.contents.first(where: {$0.isFile == true || $0.isFileTransfer == true})?.name ?? ""))
+ let lastMessagePrefixTextTmp = (fromAddressFriend ?? "")
+ var lastMessageTextTmp = (lastMessage!.contents.first(where: {$0.isText == true})?.utf8Text ?? (lastMessage!.contents.first(where: {$0.isFile == true || $0.isFileTransfer == true})?.name ?? ""))
+ var lastMessageIconTmp = ""
+ var lastMessageInItalicTmp = false
if lastMessage!.contents.first != nil && lastMessage!.contents.first!.isIcalendar == true {
if let conferenceInfo = try? Factory.Instance.createConferenceInfoFromIcalendarContent(content: lastMessage!.contents.first!) {
@@ -308,10 +319,30 @@ class ConversationModel: ObservableObject, Identifiable {
} else if conferenceInfo.state == .Cancelled {
lastMessageTextTmp = String(localized: "message_meeting_invitation_cancelled_notification")
}
+
+ lastMessageIconTmp = "calendar"
+
+ lastMessageInItalicTmp = true
}
}
}
+ if (lastMessage!.contents.first(where: {$0.isFile == true || $0.isFileTransfer == true})?.name != nil) {
+ lastMessageIconTmp = "file"
+ } else if lastMessage!.isReply {
+ lastMessageIconTmp = "reply"
+ } else if lastMessage!.isForward {
+ lastMessageIconTmp = "forward"
+ }
+
+ if lastMessage!.isRetracted {
+ lastMessageTextTmp += lastMessage!.isOutgoing ? String(localized: "conversation_message_content_deleted_by_us_label") : String(localized: "conversation_message_content_deleted_label")
+
+ lastMessageIconTmp = "trash"
+
+ lastMessageInItalicTmp = true
+ }
+
let lastMessageIsOutgoingTmp = lastMessage?.isOutgoing ?? false
let lastUpdateTimeTmp = lastMessage?.time ?? chatRoom.lastUpdateTime
@@ -319,13 +350,19 @@ class ConversationModel: ObservableObject, Identifiable {
let lastMessageStateTmp = lastMessage?.state.rawValue ?? 0
DispatchQueue.main.async {
+ self.lastMessagePrefixText = lastMessagePrefixTextTmp
+
self.lastMessageText = lastMessageTextTmp
+
+ self.lastMessageIcon = lastMessageIconTmp
self.lastMessageIsOutgoing = lastMessageIsOutgoingTmp
self.lastUpdateTime = lastUpdateTimeTmp
self.lastMessageState = lastMessageStateTmp
+
+ self.lastMessageInItalic = lastMessageInItalicTmp
}
getUnreadMessagesCount()
diff --git a/Linphone/UI/Main/Conversations/Model/Message.swift b/Linphone/UI/Main/Conversations/Model/Message.swift
index 48a36e7a1..c0096e42e 100644
--- a/Linphone/UI/Main/Conversations/Model/Message.swift
+++ b/Linphone/UI/Main/Conversations/Model/Message.swift
@@ -68,6 +68,10 @@ public struct Message: Identifiable, Hashable {
public var status: Status?
public var createdAt: Date
public var isOutgoing: Bool
+ public var isEditable: Bool
+ public var isRetractable: Bool
+ public var isEdited: Bool
+ public var isRetracted: Bool
public var dateReceived: time_t
public var address: String
@@ -94,6 +98,10 @@ public struct Message: Identifiable, Hashable {
status: Status? = nil,
createdAt: Date = Date(),
isOutgoing: Bool,
+ isEditable: Bool,
+ isRetractable: Bool,
+ isEdited: Bool,
+ isRetracted: Bool,
dateReceived: time_t,
address: String,
isFirstMessage: Bool = false,
@@ -116,6 +124,10 @@ public struct Message: Identifiable, Hashable {
self.status = status
self.createdAt = createdAt
self.isOutgoing = isOutgoing
+ self.isEditable = isEditable
+ self.isRetractable = isRetractable
+ self.isEdited = isEdited
+ self.isRetracted = isRetracted
self.dateReceived = dateReceived
self.isFirstMessage = isFirstMessage
self.address = address
@@ -163,6 +175,10 @@ public struct Message: Identifiable, Hashable {
status: status,
createdAt: draft.createdAt,
isOutgoing: draft.isOutgoing,
+ isEditable: draft.isEditable,
+ isRetractable: draft.isRetractable,
+ isEdited: draft.isEdited,
+ isRetracted: draft.isRetracted,
dateReceived: draft.dateReceived,
address: draft.address,
isFirstMessage: draft.isFirstMessage,
@@ -184,7 +200,7 @@ extension Message {
extension Message: Equatable {
public static func == (lhs: Message, rhs: Message) -> Bool {
- lhs.id == rhs.id && lhs.status == rhs.status && lhs.isFirstMessage == rhs.isFirstMessage && lhs.ownReaction == rhs.ownReaction && lhs.reactions == rhs.reactions && lhs.ephemeralExpireTime == rhs.ephemeralExpireTime && lhs.attachments == rhs.attachments
+ lhs.id == rhs.id && lhs.status == rhs.status && lhs.isEdited == rhs.isEdited && lhs.isRetracted == rhs.isRetracted && lhs.isFirstMessage == rhs.isFirstMessage && lhs.text == rhs.text && lhs.attachments == rhs.attachments && lhs.replyMessage?.text == rhs.replyMessage?.text && lhs.replyMessage?.isRetracted == rhs.replyMessage?.isRetracted && lhs.ownReaction == rhs.ownReaction && lhs.reactions == rhs.reactions && lhs.ephemeralExpireTime == rhs.ephemeralExpireTime
}
}
@@ -211,6 +227,10 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
public var isFirstMessage: Bool
public var text: String
public var isOutgoing: Bool
+ public var isEditable: Bool
+ public var isRetractable: Bool
+ public var isEdited: Bool
+ public var isRetracted: Bool
public var dateReceived: time_t
public var attachmentsNames: String
public var attachments: [Attachment]
@@ -221,6 +241,10 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
isFirstMessage: Bool = false,
text: String = "",
isOutgoing: Bool,
+ isEditable: Bool,
+ isRetractable: Bool,
+ isEdited: Bool,
+ isRetracted: Bool,
dateReceived: time_t,
attachmentsNames: String = "",
attachments: [Attachment] = [],
@@ -231,6 +255,10 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
self.isFirstMessage = isFirstMessage
self.text = text
self.isOutgoing = isOutgoing
+ self.isEditable = isEditable
+ self.isRetractable = isRetractable
+ self.isEdited = isEdited
+ self.isRetracted = isRetracted
self.dateReceived = dateReceived
self.attachmentsNames = attachmentsNames
self.attachments = attachments
@@ -238,20 +266,24 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
}
func toMessage() -> Message {
- Message(id: id, isOutgoing: isOutgoing, dateReceived: dateReceived, address: address, isFirstMessage: isFirstMessage, text: text, attachments: attachments, recording: recording)
+ Message(id: id, isOutgoing: isOutgoing, isEditable: isEditable, isRetractable: isRetractable, isEdited: isEdited, isRetracted: isRetracted, dateReceived: dateReceived, address: address, isFirstMessage: isFirstMessage, text: text, attachments: attachments, recording: recording)
}
}
public extension Message {
func toReplyMessage() -> ReplyMessage {
- ReplyMessage(id: id, address: address, isFirstMessage: isFirstMessage, text: text, isOutgoing: isOutgoing, dateReceived: dateReceived, attachments: attachments, recording: recording)
+ ReplyMessage(id: id, address: address, isFirstMessage: isFirstMessage, text: text, isOutgoing: isOutgoing, isEditable: isEditable, isRetractable: isRetractable, isEdited: isEdited, isRetracted: isRetracted, dateReceived: dateReceived, attachments: attachments, recording: recording)
}
}
public struct DraftMessage {
public var id: String?
public let isOutgoing: Bool
+ public let isEditable: Bool
+ public let isRetractable: Bool
+ public let isEdited: Bool
+ public let isRetracted: Bool
public var dateReceived: time_t
public let address: String
public let isFirstMessage: Bool
@@ -265,6 +297,10 @@ public struct DraftMessage {
public init(id: String? = nil,
isOutgoing: Bool,
+ isEditable: Bool,
+ isRetractable: Bool,
+ isEdited: Bool,
+ isRetracted: Bool,
dateReceived: time_t,
address: String,
isFirstMessage: Bool,
@@ -278,6 +314,10 @@ public struct DraftMessage {
) {
self.id = id
self.isOutgoing = isOutgoing
+ self.isEditable = isEditable
+ self.isRetractable = isRetractable
+ self.isEdited = isEdited
+ self.isRetracted = isRetracted
self.dateReceived = dateReceived
self.address = address
self.isFirstMessage = isFirstMessage
diff --git a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift
index 50eab7080..d944cadc2 100644
--- a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift
+++ b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift
@@ -93,6 +93,7 @@ class ConversationViewModel: ObservableObject {
@Published var selectedMessageToPlayVoiceRecording: EventLogMessage?
@Published var selectedMessage: EventLogMessage?
@Published var messageToReply: EventLogMessage?
+ @Published var messageToEdit: EventLogMessage?
@Published var sheetCategories: [SheetCategory] = []
@@ -171,7 +172,152 @@ class ConversationViewModel: ObservableObject {
self.getEventMessage(eventLog: eventLog)
}, onEphemeralMessageDeleted: {(_: ChatRoom, eventLog: EventLog) in
self.removeMessage(eventLog)
+ }, onMessageContentEdited: {(chatRoom: ChatRoom, message: ChatMessage) in
+ let indexMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.eventModel.eventLogId == message.messageId})
+
+ if let displayedConversation = self.sharedMainViewModel.displayedConversation {
+ displayedConversation.getContentTextMessage(chatRoom: displayedConversation.chatRoom)
+ }
+
+ var attachmentNameList: String = ""
+ var attachmentList: [Attachment] = []
+ var contentText = ""
+
+ if !message.contents.isEmpty {
+ message.contents.forEach { content in
+ if content.isText && content.name == nil {
+ contentText = content.utf8Text ?? ""
+ } else if content.name != nil && !content.name!.isEmpty {
+ if content.filePath == nil || content.filePath!.isEmpty {
+ let path = URL(string: self.getNewFilePath(name: content.name ?? ""))
+
+ if path != nil {
+ let attachment =
+ Attachment(
+ id: UUID().uuidString,
+ name: content.name!,
+ url: path!,
+ type: .fileTransfer,
+ size: content.fileSize,
+ transferProgressIndication: content.filePath != nil && !content.filePath!.isEmpty ? 100 : -1
+ )
+ attachmentNameList += ", \(content.name!)"
+ attachmentList.append(attachment)
+ }
+ } else {
+ if content.type != "video" {
+ let filePathSep = content.filePath!.components(separatedBy: "/Library/Images/")
+ let path = URL(string: self.getNewFilePath(name: filePathSep[1]))
+
+ var typeTmp: AttachmentType = .other
+
+ switch content.type {
+ case "image":
+ typeTmp = (content.name?.lowercased().hasSuffix("gif"))! ? .gif : .image
+ case "audio":
+ typeTmp = content.isVoiceRecording ? .voiceRecording : .audio
+ case "application":
+ typeTmp = content.subtype.lowercased() == "pdf" ? .pdf : .other
+ case "text":
+ typeTmp = .text
+ default:
+ typeTmp = .other
+ }
+
+ if path != nil {
+ let attachment =
+ Attachment(
+ id: UUID().uuidString,
+ name: content.name!,
+ url: path!,
+ type: typeTmp,
+ duration: typeTmp == .voiceRecording ? content.fileDuration : 0,
+ size: content.fileSize,
+ transferProgressIndication: content.filePath != nil && !content.filePath!.isEmpty ? 100 : -1
+ )
+ attachmentNameList += ", \(content.name!)"
+ attachmentList.append(attachment)
+ if typeTmp != .voiceRecording {
+ DispatchQueue.main.async {
+ if !attachment.full.pathExtension.isEmpty {
+ self.attachments.append(attachment)
+ }
+ }
+ }
+ }
+ } else if content.type == "video" {
+ let filePathSep = content.filePath!.components(separatedBy: "/Library/Images/")
+ let path = URL(string: self.getNewFilePath(name: filePathSep[1]))
+ let pathThumbnail = URL(string: self.generateThumbnail(name: filePathSep[1]))
+
+ if path != nil && pathThumbnail != nil {
+ let attachment =
+ Attachment(
+ id: UUID().uuidString,
+ name: content.name!,
+ thumbnail: pathThumbnail!,
+ full: path!,
+ type: .video,
+ size: content.fileSize,
+ transferProgressIndication: content.filePath != nil && !content.filePath!.isEmpty ? 100 : -1
+ )
+ attachmentNameList += ", \(content.name!)"
+ attachmentList.append(attachment)
+ DispatchQueue.main.async {
+ if !attachment.full.pathExtension.isEmpty {
+ self.attachments.append(attachment)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if !attachmentNameList.isEmpty {
+ attachmentNameList = String(attachmentNameList.dropFirst(2))
+ }
+
+ let indexReplyMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.replyMessage?.id == message.messageId})
+
+ DispatchQueue.main.async {
+ if indexMessage != nil {
+ self.conversationMessagesSection[0].rows[indexMessage!].message.text = contentText
+ self.conversationMessagesSection[0].rows[indexMessage!].message.isEdited = true
+ self.conversationMessagesSection[0].rows[indexMessage!].message.attachments = attachmentList
+ self.conversationMessagesSection[0].rows[indexMessage!].message.attachmentsNames = attachmentNameList
+ }
+
+ if indexReplyMessage != nil {
+ self.conversationMessagesSection[0].rows[indexReplyMessage!].message.replyMessage?.text = contentText
+ }
+ }
+ }, onMessageRetracted: {(chatRoom: ChatRoom, message: ChatMessage) in
+ let indexMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.eventModel.eventLogId == message.messageId})
+ let indexReplyMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.replyMessage?.id == message.messageId})
+
+ if let displayedConversation = self.sharedMainViewModel.displayedConversation {
+ displayedConversation.getContentTextMessage(chatRoom: displayedConversation.chatRoom)
+ }
+
+ DispatchQueue.main.async {
+ if indexMessage != nil {
+ self.conversationMessagesSection[0].rows[indexMessage!].message.text = ""
+ self.conversationMessagesSection[0].rows[indexMessage!].message.isRetracted = true
+ self.conversationMessagesSection[0].rows[indexMessage!].message.attachments = []
+ self.conversationMessagesSection[0].rows[indexMessage!].message.attachmentsNames = ""
+ }
+
+ if indexReplyMessage != nil {
+ self.conversationMessagesSection[0].rows[indexReplyMessage!].message.replyMessage?.text = ""
+ self.conversationMessagesSection[0].rows[indexReplyMessage!].message.replyMessage?.isRetracted = true
+ self.conversationMessagesSection[0].rows[indexReplyMessage!].message.replyMessage?.attachments = []
+ self.conversationMessagesSection[0].rows[indexReplyMessage!].message.replyMessage?.attachmentsNames = ""
+ }
+ }
})
+
self.chatRoomDelegateHolder = ChatRoomDelegateHolder(chatroom: chatRoom, delegate: chatRoomDelegate)
}
@@ -544,6 +690,10 @@ class ConversationViewModel: ObservableObject {
id: UUID().uuidString,
status: nil,
isOutgoing: false,
+ isEditable: false,
+ isRetractable: false,
+ isEdited: false,
+ isRetracted: false,
dateReceived: 0,
address: "",
isFirstMessage: false,
@@ -704,6 +854,8 @@ class ConversationViewModel: ObservableObject {
let contentReplyText = chatMessage.replyMessage?.utf8Text ?? ""
+ let isReplyRetracted = chatMessage.replyMessage?.isRetracted ?? false
+
var attachmentNameReplyList: String = ""
chatMessage.replyMessage?.contents.forEach { content in
@@ -721,7 +873,11 @@ class ConversationViewModel: ObservableObject {
address: addressReplyCleaned?.asStringUriOnly() ?? "",
isFirstMessage: false,
text: contentReplyText,
- isOutgoing: false,
+ isOutgoing: chatMessage.replyMessage!.isOutgoing,
+ isEditable: false,
+ isRetractable: false,
+ isEdited: false,
+ isRetracted: isReplyRetracted,
dateReceived: 0,
attachmentsNames: attachmentNameReplyList,
attachments: []
@@ -735,6 +891,10 @@ class ConversationViewModel: ObservableObject {
id: !chatMessage.messageId.isEmpty ? chatMessage.messageId : UUID().uuidString,
status: statusTmp,
isOutgoing: chatMessage.isOutgoing,
+ isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
+ isRetractable: chatMessage.isOutgoing ? chatMessage.isRetractable : false,
+ isEdited: chatMessage.isEdited,
+ isRetracted: chatMessage.isRetracted,
dateReceived: chatMessage.time,
address: addressCleaned?.asStringUriOnly() ?? "",
isFirstMessage: isFirstMessageTmp,
@@ -788,6 +948,10 @@ class ConversationViewModel: ObservableObject {
id: UUID().uuidString,
status: nil,
isOutgoing: false,
+ isEditable: false,
+ isRetractable: false,
+ isEdited: false,
+ isRetracted: false,
dateReceived: 0,
address: "",
isFirstMessage: false,
@@ -947,6 +1111,8 @@ class ConversationViewModel: ObservableObject {
let contentReplyText = chatMessage.replyMessage?.utf8Text ?? ""
+ let isReplyRetracted = chatMessage.replyMessage?.isRetracted ?? false
+
var attachmentNameReplyList: String = ""
chatMessage.replyMessage?.contents.forEach { content in
@@ -964,7 +1130,11 @@ class ConversationViewModel: ObservableObject {
address: addressReplyCleaned?.asStringUriOnly() ?? "",
isFirstMessage: false,
text: contentReplyText,
- isOutgoing: false,
+ isOutgoing: chatMessage.replyMessage!.isOutgoing,
+ isEditable: false,
+ isRetractable: false,
+ isEdited: false,
+ isRetracted: isReplyRetracted,
dateReceived: 0,
attachmentsNames: attachmentNameReplyList,
attachments: []
@@ -978,6 +1148,10 @@ class ConversationViewModel: ObservableObject {
id: !chatMessage.messageId.isEmpty ? chatMessage.messageId : UUID().uuidString,
status: statusTmp,
isOutgoing: chatMessage.isOutgoing,
+ isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
+ isRetractable: chatMessage.isOutgoing ? chatMessage.isRetractable : false,
+ isEdited: chatMessage.isEdited,
+ isRetracted: chatMessage.isRetracted,
dateReceived: chatMessage.time,
address: addressCleaned?.asStringUriOnly() ?? "",
isFirstMessage: isFirstMessageTmp,
@@ -1048,6 +1222,10 @@ class ConversationViewModel: ObservableObject {
id: UUID().uuidString,
status: nil,
isOutgoing: false,
+ isEditable: false,
+ isRetractable: false,
+ isEdited: false,
+ isRetracted: false,
dateReceived: 0,
address: "",
isFirstMessage: false,
@@ -1221,6 +1399,8 @@ class ConversationViewModel: ObservableObject {
let contentReplyText = chatMessage.replyMessage?.utf8Text ?? ""
+ let isReplyRetracted = chatMessage.replyMessage?.isRetracted ?? false
+
var attachmentNameReplyList: String = ""
chatMessage.replyMessage?.contents.forEach { content in
@@ -1238,7 +1418,11 @@ class ConversationViewModel: ObservableObject {
address: addressReplyCleaned != nil ? addressReplyCleaned!.asStringUriOnly() : "",
isFirstMessage: false,
text: contentReplyText,
- isOutgoing: false,
+ isOutgoing: chatMessage.replyMessage!.isOutgoing,
+ isEditable: false,
+ isRetractable: false,
+ isEdited: false,
+ isRetracted: isReplyRetracted,
dateReceived: 0,
attachmentsNames: attachmentNameReplyList,
attachments: []
@@ -1253,6 +1437,10 @@ class ConversationViewModel: ObservableObject {
appData: chatMessage.appdata ?? "",
status: statusTmp,
isOutgoing: chatMessage.isOutgoing,
+ isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
+ isRetractable: chatMessage.isOutgoing ? chatMessage.isRetractable : false,
+ isEdited: chatMessage.isEdited,
+ isRetracted: chatMessage.isRetracted,
dateReceived: chatMessage.time,
address: addressCleaned != nil ? addressCleaned!.asStringUriOnly() : "",
isFirstMessage: isFirstMessageTmp,
@@ -1453,6 +1641,8 @@ class ConversationViewModel: ObservableObject {
let contentReplyText = chatMessage.replyMessage?.utf8Text ?? ""
+ let isReplyRetracted = chatMessage.replyMessage?.isRetracted ?? false
+
var attachmentNameReplyList: String = ""
chatMessage.replyMessage?.contents.forEach { content in
@@ -1470,7 +1660,11 @@ class ConversationViewModel: ObservableObject {
address: addressReplyCleaned != nil ? addressReplyCleaned!.asStringUriOnly() : "",
isFirstMessage: false,
text: contentReplyText,
- isOutgoing: false,
+ isOutgoing: chatMessage.replyMessage!.isOutgoing,
+ isEditable: false,
+ isRetractable: false,
+ isEdited: false,
+ isRetracted: isReplyRetracted,
dateReceived: 0,
attachmentsNames: attachmentNameReplyList,
attachments: []
@@ -1485,6 +1679,10 @@ class ConversationViewModel: ObservableObject {
appData: chatMessage.appdata ?? "",
status: statusTmp,
isOutgoing: chatMessage.isOutgoing,
+ isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
+ isRetractable: chatMessage.isOutgoing ? chatMessage.isRetractable : false,
+ isEdited: chatMessage.isEdited,
+ isRetracted: chatMessage.isRetracted,
dateReceived: chatMessage.time,
address: addressCleaned != nil ? addressCleaned!.asStringUriOnly() : "",
isFirstMessage: isFirstMessageTmp,
@@ -1526,6 +1724,10 @@ class ConversationViewModel: ObservableObject {
id: UUID().uuidString,
status: nil,
isOutgoing: false,
+ isEditable: false,
+ isRetractable: false,
+ isEdited: false,
+ isRetracted: false,
dateReceived: 0,
address: "",
isFirstMessage: false,
@@ -1553,6 +1755,9 @@ class ConversationViewModel: ObservableObject {
}
func replyToMessage(index: Int, isMessageTextFocused: Binding) {
+ if self.messageToEdit != nil {
+ self.messageToEdit = nil
+ }
coreContext.doOnCoreQueue { _ in
let messageToReplyTmp = self.conversationMessagesSection[0].rows[index]
DispatchQueue.main.async {
@@ -1564,6 +1769,21 @@ class ConversationViewModel: ObservableObject {
}
}
+ func editMessage(chatMessage: EventLogMessage, isMessageTextFocused: Binding) {
+ if self.messageToReply != nil {
+ self.messageToReply = nil
+ }
+ coreContext.doOnCoreQueue { _ in
+ let messageToEditTmp = chatMessage
+ DispatchQueue.main.async {
+ withAnimation(.linear(duration: 0.15)) {
+ self.messageToEdit = messageToEditTmp
+ }
+ isMessageTextFocused.wrappedValue = true
+ }
+ }
+ }
+
func resendMessage(chatMessage: EventLogMessage) {
coreContext.doOnCoreQueue { _ in
if let message = chatMessage.eventModel.eventLog.chatMessage {
@@ -1619,6 +1839,10 @@ class ConversationViewModel: ObservableObject {
id: UUID().uuidString,
status: nil,
isOutgoing: false,
+ isEditable: false,
+ isRetractable: false,
+ isEdited: false,
+ isRetracted: false,
dateReceived: 0,
address: "",
isFirstMessage: false,
@@ -1778,6 +2002,8 @@ class ConversationViewModel: ObservableObject {
let contentReplyText = chatMessage.replyMessage?.utf8Text ?? ""
+ let isReplyRetracted = chatMessage.replyMessage?.isRetracted ?? false
+
var attachmentNameReplyList: String = ""
chatMessage.replyMessage?.contents.forEach { content in
@@ -1795,7 +2021,11 @@ class ConversationViewModel: ObservableObject {
address: addressReplyCleaned?.asStringUriOnly() ?? "",
isFirstMessage: false,
text: contentReplyText,
- isOutgoing: false,
+ isOutgoing: chatMessage.replyMessage!.isOutgoing,
+ isEditable: false,
+ isRetractable: false,
+ isEdited: false,
+ isRetracted: isReplyRetracted,
dateReceived: 0,
attachmentsNames: attachmentNameReplyList,
attachments: []
@@ -1809,6 +2039,10 @@ class ConversationViewModel: ObservableObject {
id: !chatMessage.messageId.isEmpty ? chatMessage.messageId : UUID().uuidString,
status: statusTmp,
isOutgoing: chatMessage.isOutgoing,
+ isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
+ isRetractable: chatMessage.isOutgoing ? chatMessage.isRetractable : false,
+ isEdited: chatMessage.isEdited,
+ isRetracted: chatMessage.isRetracted,
dateReceived: chatMessage.time,
address: addressCleaned?.asStringUriOnly() ?? "",
isFirstMessage: isFirstMessageTmp,
@@ -1873,6 +2107,8 @@ class ConversationViewModel: ObservableObject {
if chatMessageToReply != nil {
message = try self.sharedMainViewModel.displayedConversation!.chatRoom.createReplyMessage(message: chatMessageToReply!)
}
+ } else if let chatMessage = self.messageToEdit?.eventModel.eventLog.chatMessage {
+ message = try self.sharedMainViewModel.displayedConversation!.chatRoom.createReplacesMessage(message: chatMessage)
} else {
message = try self.sharedMainViewModel.displayedConversation!.chatRoom.createEmptyMessage()
}
@@ -1948,12 +2184,14 @@ class ConversationViewModel: ObservableObject {
if message != nil && !message!.contents.isEmpty {
Log.info("[ConversationViewModel] Sending message")
message!.send()
+ self.sharedMainViewModel.displayedConversation!.chatRoom.stopComposing()
}
Log.info("[ConversationViewModel] Message sent, re-setting defaults")
DispatchQueue.main.async {
self.messageToReply = nil
+ self.messageToEdit = nil
withAnimation {
self.mediasToSend.removeAll()
}
@@ -2370,10 +2608,20 @@ class ConversationViewModel: ObservableObject {
}
}
- func compose() {
+ func compose(stop: Bool, cachedConversation: ConversationModel? = nil) {
coreContext.doOnCoreQueue { _ in
- if self.sharedMainViewModel.displayedConversation != nil {
- self.sharedMainViewModel.displayedConversation!.chatRoom.compose()
+ if let displayedConversation = self.sharedMainViewModel.displayedConversation {
+ if stop {
+ displayedConversation.chatRoom.stopComposing()
+ } else {
+ displayedConversation.chatRoom.composeTextMessage()
+ }
+ } else if let displayedConversation = cachedConversation {
+ if stop {
+ displayedConversation.chatRoom.stopComposing()
+ } else {
+ displayedConversation.chatRoom.composeTextMessage()
+ }
}
}
}
@@ -2672,17 +2920,39 @@ class ConversationViewModel: ObservableObject {
if let displayedConversation = self.sharedMainViewModel.displayedConversation,
let selectedMessage = self.selectedMessage,
let chatMessage = selectedMessage.eventModel.eventLog.chatMessage {
+
+ let indexReplyMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.replyMessage?.id == chatMessage.messageId})
displayedConversation.chatRoom.deleteMessage(message: chatMessage)
+
+ displayedConversation.getContentTextMessage(chatRoom: displayedConversation.chatRoom)
+
DispatchQueue.main.async {
if let sectionIndex = self.conversationMessagesSection.firstIndex(where: { $0.chatRoomID == displayedConversation.id }),
let rowIndex = self.conversationMessagesSection[sectionIndex].rows.firstIndex(of: selectedMessage) {
self.conversationMessagesSection[sectionIndex].rows.remove(at: rowIndex)
+
+ if indexReplyMessage != nil {
+ self.conversationMessagesSection[0].rows[indexReplyMessage!].message.replyMessage = nil
+ }
}
self.selectedMessage = nil
}
}
}
}
+
+ func deleteMessageForEveryone(){
+ coreContext.doOnCoreQueue { _ in
+ if let displayedConversation = self.sharedMainViewModel.displayedConversation,
+ let selectedMessage = self.selectedMessage,
+ let chatMessage = selectedMessage.eventModel.eventLog.chatMessage {
+ displayedConversation.chatRoom.retractMessage(message: chatMessage)
+ DispatchQueue.main.async {
+ self.selectedMessage = nil
+ }
+ }
+ }
+ }
}
// swiftlint:enable line_length
// swiftlint:enable type_body_length
diff --git a/Linphone/UI/Main/Conversations/ViewModel/ConversationsListViewModel.swift b/Linphone/UI/Main/Conversations/ViewModel/ConversationsListViewModel.swift
index f6c6b5bad..41e30f53e 100644
--- a/Linphone/UI/Main/Conversations/ViewModel/ConversationsListViewModel.swift
+++ b/Linphone/UI/Main/Conversations/ViewModel/ConversationsListViewModel.swift
@@ -89,16 +89,16 @@ class ConversationsListViewModel: ObservableObject {
fromAddressFriend = nil
}
- let lastMessageTextTmp = (fromAddressFriend ?? "") + (lastMessage.contents.first(where: { $0.isText })?.utf8Text ?? (lastMessage.contents.first(where: { $0.isFile || $0.isFileTransfer })?.name ?? ""))
+ let lastMessagePrefixTextTmp = (fromAddressFriend ?? "")
if let index = self.conversationsList.firstIndex(where: { $0.chatRoom === conversationModel.chatRoom }) {
DispatchQueue.main.async {
- conversationModel.lastMessageText = lastMessageTextTmp
- self.conversationsList[index].lastMessageText = lastMessageTextTmp
+ conversationModel.lastMessagePrefixText = lastMessagePrefixTextTmp
+ self.conversationsList[index].lastMessagePrefixText = lastMessagePrefixTextTmp
}
} else {
DispatchQueue.main.async {
- conversationModel.lastMessageText = lastMessageTextTmp
+ conversationModel.lastMessagePrefixText = lastMessagePrefixTextTmp
}
}
}
@@ -148,16 +148,16 @@ class ConversationsListViewModel: ObservableObject {
fromAddressFriend = nil
}
- let lastMessageTextTmp = (fromAddressFriend ?? "") + (lastMessage.contents.first(where: { $0.isText })?.utf8Text ?? (lastMessage.contents.first(where: { $0.isFile || $0.isFileTransfer })?.name ?? ""))
+ let lastMessagePrefixTextTmp = (fromAddressFriend ?? "")
if let index = self.conversationsList.firstIndex(where: { $0.chatRoom === conversationModel.chatRoom }) {
DispatchQueue.main.async {
- conversationModel.lastMessageText = lastMessageTextTmp
- self.conversationsList[index].lastMessageText = lastMessageTextTmp
+ conversationModel.lastMessagePrefixText = lastMessagePrefixTextTmp
+ self.conversationsList[index].lastMessagePrefixText = lastMessagePrefixTextTmp
}
} else {
DispatchQueue.main.async {
- conversationModel.lastMessageText = lastMessageTextTmp
+ conversationModel.lastMessagePrefixText = lastMessagePrefixTextTmp
}
}
}
@@ -183,69 +183,76 @@ class ConversationsListViewModel: ObservableObject {
func addConversationDelegate() {
coreContext.doOnCoreQueue { core in
- self.coreConversationDelegate = CoreDelegateStub(onMessagesReceived: { (core: Core, chatRoom: ChatRoom, _: [ChatMessage]) in
- if let defaultAddress = core.defaultAccount?.contactAddress,
- let localAddress = chatRoom.localAddress,
- defaultAddress.weakEqual(address2: localAddress) {
- let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
- let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
- model.getContentTextMessage(chatRoom: chatRoom)
- let index = self.conversationsList.firstIndex(where: { $0.id == idTmp })
- DispatchQueue.main.async {
- if index != nil {
- self.conversationsList.remove(at: index!)
- }
- self.conversationsList.insert(model, at: 0)
- }
- SharedMainViewModel.shared.updateUnreadMessagesCount()
- }
- }, onMessageSent: { (_: Core, chatRoom: ChatRoom, _: ChatMessage) in
- if let defaultAddress = core.defaultAccount?.contactAddress,
- let localAddress = chatRoom.localAddress,
- defaultAddress.weakEqual(address2: localAddress) {
- let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
- let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
- model.getContentTextMessage(chatRoom: chatRoom)
- let index = self.conversationsList.firstIndex(where: { $0.id == idTmp })
- if index != nil {
- self.conversationsList[index!].chatMessageRemoveDelegate()
- }
- DispatchQueue.main.async {
- if index != nil {
- self.conversationsList.remove(at: index!)
- }
- self.conversationsList.insert(model, at: 0)
- }
- SharedMainViewModel.shared.updateUnreadMessagesCount()
- }
- }, onChatRoomRead: { (_: Core, chatRoom: ChatRoom) in
- let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
- let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
- model.getContentTextMessage(chatRoom: chatRoom)
- if let index = self.conversationsList.firstIndex(where: { $0.id == idTmp }) {
- DispatchQueue.main.async {
- self.conversationsList.remove(at: index)
- self.conversationsList.insert(model, at: index)
+ self.coreConversationDelegate = CoreDelegateStub(
+ onMessagesReceived: { (core: Core, chatRoom: ChatRoom, _: [ChatMessage]) in
+ if let defaultAddress = core.defaultAccount?.contactAddress,
+ let localAddress = chatRoom.localAddress,
+ defaultAddress.weakEqual(address2: localAddress) {
+ let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
+ let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
+ model.getContentTextMessage(chatRoom: chatRoom)
+ let index = self.conversationsList.firstIndex(where: { $0.id == idTmp })
+ DispatchQueue.main.async {
+ if index != nil {
+ self.conversationsList.remove(at: index!)
+ }
+ self.conversationsList.insert(model, at: 0)
+ }
+ SharedMainViewModel.shared.updateUnreadMessagesCount()
}
+ }, onMessageSent: { (_: Core, chatRoom: ChatRoom, _: ChatMessage) in
+ if let defaultAddress = core.defaultAccount?.contactAddress,
+ let localAddress = chatRoom.localAddress,
+ defaultAddress.weakEqual(address2: localAddress) {
+ let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
+ let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
+ model.getContentTextMessage(chatRoom: chatRoom)
+ let index = self.conversationsList.firstIndex(where: { $0.id == idTmp })
+ if index != nil {
+ self.conversationsList[index!].chatMessageRemoveDelegate()
+ }
+ DispatchQueue.main.async {
+ if index != nil {
+ self.conversationsList.remove(at: index!)
+ }
+ self.conversationsList.insert(model, at: 0)
+ }
+ SharedMainViewModel.shared.updateUnreadMessagesCount()
+ }
+ }, onChatRoomRead: { (_: Core, chatRoom: ChatRoom) in
+ let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
+ let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
+ model.getContentTextMessage(chatRoom: chatRoom)
+ if let index = self.conversationsList.firstIndex(where: { $0.id == idTmp }) {
+ DispatchQueue.main.async {
+ self.conversationsList.remove(at: index)
+ self.conversationsList.insert(model, at: index)
+ }
+ }
+ SharedMainViewModel.shared.updateUnreadMessagesCount()
+ }, onChatRoomStateChanged: { (core: Core, chatroom: ChatRoom, state: ChatRoom.State) in
+ // Log.info("[ConversationsListViewModel] Conversation [${LinphoneUtils.getChatRoomId(chatRoom)}] state changed [$state]")
+ if let defaultAddress = core.defaultAccount?.contactAddress,
+ let localAddress = chatroom.localAddress,
+ defaultAddress.weakEqual(address2: localAddress) {
+ if core.globalState == .On {
+ switch state {
+ case .Created:
+ self.addChatRoom(chatRoom: chatroom)
+ case .Deleted:
+ self.removeChatRoom(chatRoom: chatroom)
+ default:
+ break
+ }
+ }
+ }
+ }, onMessageRetracted: { (core: Core, chatRoom: ChatRoom, message: ChatMessage) in
+ let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
+ let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
+ model.getContentTextMessage(chatRoom: chatRoom)
+ SharedMainViewModel.shared.updateUnreadMessagesCount()
}
- SharedMainViewModel.shared.updateUnreadMessagesCount()
- }, onChatRoomStateChanged: { (core: Core, chatroom: ChatRoom, state: ChatRoom.State) in
- // Log.info("[ConversationsListViewModel] Conversation [${LinphoneUtils.getChatRoomId(chatRoom)}] state changed [$state]")
- if let defaultAddress = core.defaultAccount?.contactAddress,
- let localAddress = chatroom.localAddress,
- defaultAddress.weakEqual(address2: localAddress) {
- if core.globalState == .On {
- switch state {
- case .Created:
- self.addChatRoom(chatRoom: chatroom)
- case .Deleted:
- self.removeChatRoom(chatRoom: chatroom)
- default:
- break
- }
- }
- }
- })
+ )
core.addDelegate(delegate: self.coreConversationDelegate!)
}
}
diff --git a/Linphone/UI/Main/Fragments/PopupUpdatePassword.swift b/Linphone/UI/Main/Fragments/PopupUpdatePassword.swift
index d7ca7d013..d61e153d6 100644
--- a/Linphone/UI/Main/Fragments/PopupUpdatePassword.swift
+++ b/Linphone/UI/Main/Fragments/PopupUpdatePassword.swift
@@ -105,7 +105,7 @@ struct PopupUpdatePassword: View {
updateAuthInfo()
isShowUpdatePasswordPopup = false
}, label: {
- Text("dialog_ok")
+ Text("dialog_confirm")
.default_text_style_white_600(styleSize: 20)
.frame(height: 35)
.frame(maxWidth: .infinity)
diff --git a/Linphone/UI/Main/Fragments/PopupView.swift b/Linphone/UI/Main/Fragments/PopupView.swift
index a1bd4ac5a..784ed58ef 100644
--- a/Linphone/UI/Main/Fragments/PopupView.swift
+++ b/Linphone/UI/Main/Fragments/PopupView.swift
@@ -34,6 +34,9 @@ struct PopupView: View {
var titleSecondButton: Text?
var actionSecondButton: () -> Void
+ var titleThirdButton: Text?
+ var actionThirdButton: () -> Void
+
var body: some View {
GeometryReader { geometry in
VStack(alignment: .leading) {
@@ -49,40 +52,57 @@ struct PopupView: View {
.padding(.bottom, 20)
}
- if titleFirstButton != nil {
- Button(action: {
- actionFirstButton()
- }, label: {
- titleFirstButton
- .default_text_style_orange_600(styleSize: 20)
- .frame(height: 35)
- .frame(maxWidth: .infinity)
- })
- .padding(.horizontal, 20)
- .padding(.vertical, 10)
- .cornerRadius(60)
- .overlay(
- RoundedRectangle(cornerRadius: 60)
- .inset(by: 0.5)
- .stroke(Color.orangeMain500, lineWidth: 1)
- )
- .padding(.bottom, 10)
- }
-
- if titleSecondButton != nil {
- Button(action: {
- actionSecondButton()
- }, label: {
- titleSecondButton
- .default_text_style_white_600(styleSize: 20)
- .frame(height: 35)
- .frame(maxWidth: .infinity)
- })
- .padding(.horizontal, 20)
- .padding(.vertical, 10)
- .background(Color.orangeMain500)
- .cornerRadius(60)
+ HStack {
+ if titleFirstButton != nil {
+ Button(action: {
+ actionFirstButton()
+ }, label: {
+ titleFirstButton
+ .default_text_style_white_600(styleSize: 14)
+ .frame(height: 30)
+ })
+ .padding(.horizontal, 20)
+ .padding(.vertical, 10)
+ .background(Color.orangeMain500)
+ .cornerRadius(60)
+ .padding(.horizontal, 2)
+ }
+
+ if titleSecondButton != nil {
+ Button(action: {
+ actionSecondButton()
+ }, label: {
+ titleSecondButton
+ .default_text_style_white_600(styleSize: 14)
+ .frame(height: 30)
+ })
+ .padding(.horizontal, 20)
+ .padding(.vertical, 10)
+ .background(Color.orangeMain500)
+ .cornerRadius(60)
+ .padding(.horizontal, 2)
+ }
+
+ if titleThirdButton != nil {
+ Button(action: {
+ actionThirdButton()
+ }, label: {
+ titleThirdButton
+ .default_text_style_orange_600(styleSize: 14)
+ .frame(height: 30)
+ })
+ .padding(.horizontal, 20)
+ .padding(.vertical, 10)
+ .cornerRadius(60)
+ .overlay(
+ RoundedRectangle(cornerRadius: 60)
+ .inset(by: 0.5)
+ .stroke(Color.orangeMain500, lineWidth: 1)
+ )
+ .padding(.horizontal, 2)
+ }
}
+ .frame(maxWidth: .infinity, alignment: .trailing)
}
.padding(.horizontal, 20)
.padding(.vertical, 20)
@@ -101,9 +121,11 @@ struct PopupView: View {
PopupView(isShowPopup: .constant(true),
title: Text("Title"),
content: Text("Content"),
- titleFirstButton: Text("Deny all"),
+ titleFirstButton: Text("Accept all"),
actionFirstButton: {},
- titleSecondButton: Text("Accept all"),
- actionSecondButton: {})
+ titleSecondButton: Text("dialog_confirm"),
+ actionSecondButton: {},
+ titleThirdButton: Text("dialog_cancel"),
+ actionThirdButton: {})
.background(.black.opacity(0.65))
}
diff --git a/Linphone/UI/Main/Fragments/PopupViewWithTextField.swift b/Linphone/UI/Main/Fragments/PopupViewWithTextField.swift
index 58d22c217..7f8f58bfc 100644
--- a/Linphone/UI/Main/Fragments/PopupViewWithTextField.swift
+++ b/Linphone/UI/Main/Fragments/PopupViewWithTextField.swift
@@ -72,7 +72,7 @@ struct PopupViewWithTextField: View {
setNewChatRoomSubject()
isShowConversationInfoPopup = false
}, label: {
- Text("dialog_ok")
+ Text("dialog_confirm")
.default_text_style_white_600(styleSize: 20)
.frame(height: 35)
.frame(maxWidth: .infinity)
diff --git a/Linphone/UI/Main/Help/Fragments/HelpFragment.swift b/Linphone/UI/Main/Help/Fragments/HelpFragment.swift
index 004bda777..549e64d49 100644
--- a/Linphone/UI/Main/Help/Fragments/HelpFragment.swift
+++ b/Linphone/UI/Main/Help/Fragments/HelpFragment.swift
@@ -177,7 +177,7 @@ struct HelpFragment: View {
.frame(maxWidth: .infinity, alignment: .leading)
.multilineTextAlignment(.leading)
- Text(helpViewModel.version)
+ Text(helpViewModel.appVersion)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.multilineTextAlignment(.leading)
@@ -302,17 +302,17 @@ struct HelpFragment: View {
isShowPopup: $helpViewModel.checkUpdateAvailable,
title: Text("help_dialog_update_available_title"),
content: Text(String(format: String(localized: "help_dialog_update_available_message"), helpViewModel.versionAvailable)),
- titleFirstButton: Text("dialog_cancel"),
- actionFirstButton: {
- helpViewModel.checkUpdateAvailable = false
- },
+ titleFirstButton: nil,
+ actionFirstButton: {},
titleSecondButton: Text("dialog_install"),
actionSecondButton: {
helpViewModel.checkUpdateAvailable = false
if let url = URL(string: helpViewModel.urlVersionAvailable) {
UIApplication.shared.open(url)
}
- }
+ },
+ titleThirdButton: Text("dialog_cancel"),
+ actionThirdButton: { helpViewModel.checkUpdateAvailable = false }
)
.background(.black.opacity(0.65))
.zIndex(3)
diff --git a/Linphone/UI/Main/Help/ViewModel/HelpViewModel.swift b/Linphone/UI/Main/Help/ViewModel/HelpViewModel.swift
index 75d4b0456..f8d71ad84 100644
--- a/Linphone/UI/Main/Help/ViewModel/HelpViewModel.swift
+++ b/Linphone/UI/Main/Help/ViewModel/HelpViewModel.swift
@@ -38,13 +38,20 @@ class HelpViewModel: ObservableObject {
private var coreDelegate: CoreDelegate?
init() {
- let appName = Bundle.main.infoDictionary?["CFBundleName"] as? String
- let versionTmp = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
- let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String
+ let appGitVersion = APP_GIT_COMMIT
+ let appGitBranch = APP_GIT_BRANCH
+ let appGitTag = APP_GIT_TAG
+ let sdkGitVersion = linphonesw.sdkVersion
+ var sdkGitBranch = linphonesw.sdkBranch
- self.version = (versionTmp ?? "6.0.0")
+ if sdkGitBranch.hasPrefix("remotes/origin/") {
+ sdkGitBranch = String(sdkGitBranch.dropFirst("remotes/origin/".count))
+ }
- self.sdkVersion = Core.getVersion
+ self.appVersion = appGitTag
+ self.version = appGitTag + "-" + appGitVersion + "\n(\(appGitBranch))"
+
+ self.sdkVersion = sdkGitVersion + "\n(\(sdkGitBranch))"
if let path = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist"),
let plist = NSDictionary(contentsOfFile: path) as? [String: Any],
diff --git a/Linphone/UI/Main/History/Fragments/HistoryContactFragment.swift b/Linphone/UI/Main/History/Fragments/HistoryContactFragment.swift
index 8ecbc13ad..132d4442d 100644
--- a/Linphone/UI/Main/History/Fragments/HistoryContactFragment.swift
+++ b/Linphone/UI/Main/History/Fragments/HistoryContactFragment.swift
@@ -196,14 +196,8 @@ struct HistoryContactFragment: View {
.padding(.top, 5)
}
- if let avatarModel = historyModel.avatarModel {
- Text(avatarModel.lastPresenceInfo)
- .foregroundStyle(avatarModel.lastPresenceInfo == "Online" ? Color.greenSuccess500 : Color.orangeWarning600)
- .multilineTextAlignment(.center)
- .default_text_style_300(styleSize: 12)
- .frame(maxWidth: .infinity)
- .frame(height: 20)
- .padding(.top, 5)
+ if let avatar = historyModel.avatarModel {
+ AvatarPresenceView(avatarModel: avatar)
} else {
Text("")
.multilineTextAlignment(.center)
@@ -423,6 +417,21 @@ struct HistoryContactFragment: View {
}
}
+struct AvatarPresenceView: View {
+ @ObservedObject var avatarModel: ContactAvatarModel
+
+ var body: some View {
+ Text(avatarModel.lastPresenceInfo)
+ .foregroundStyle(avatarModel.lastPresenceInfo == "Online" ? Color.greenSuccess500 : Color.orangeWarning600)
+ .multilineTextAlignment(.center)
+ .default_text_style_300(styleSize: 12)
+ .frame(maxWidth: .infinity)
+ .frame(height: 20)
+ .padding(.top, 5)
+ }
+}
+
+
#Preview {
HistoryContactFragment(
isShowDeleteAllHistoryPopup: .constant(false),
diff --git a/Linphone/UI/Main/History/Fragments/StartCallFragment.swift b/Linphone/UI/Main/History/Fragments/StartCallFragment.swift
index 735f3928a..6e526c32c 100644
--- a/Linphone/UI/Main/History/Fragments/StartCallFragment.swift
+++ b/Linphone/UI/Main/History/Fragments/StartCallFragment.swift
@@ -235,75 +235,83 @@ struct StartCallFragment: View {
)
}
- ScrollView {
- if !ContactsManager.shared.lastSearch.isEmpty {
- HStack(alignment: .center) {
- Text("contacts_list_all_contacts_title")
- .default_text_style_800(styleSize: 16)
-
- Spacer()
+ ZStack {
+ ScrollView {
+ if !ContactsManager.shared.lastSearch.isEmpty {
+ HStack(alignment: .center) {
+ Text("contacts_list_all_contacts_title")
+ .default_text_style_800(styleSize: 16)
+
+ Spacer()
+ }
+ .padding(.vertical, 10)
+ .padding(.horizontal, 16)
}
- .padding(.vertical, 10)
- .padding(.horizontal, 16)
- }
-
- ContactsListFragment(showingSheet: .constant(false)
- , startCallFunc: { addr in
- if callViewModel.isTransferInsteadCall {
- showingDialer = false
-
- startCallViewModel.searchField = ""
- magicSearch.currentFilter = ""
-
- magicSearch.searchForContacts()
-
- if callViewModel.isTransferInsteadCall == true {
- callViewModel.isTransferInsteadCall = false
+
+ ContactsListFragment(showingSheet: .constant(false)
+ , startCallFunc: { addr in
+ if callViewModel.isTransferInsteadCall {
+ showingDialer = false
+
+ startCallViewModel.searchField = ""
+ magicSearch.currentFilter = ""
+
+ magicSearch.searchForContacts()
+
+ if callViewModel.isTransferInsteadCall == true {
+ callViewModel.isTransferInsteadCall = false
+ }
+
+ resetCallView()
+
+ delayColorDismiss()
+
+ withAnimation {
+ isShowStartCallFragment.toggle()
+ callViewModel.blindTransferCallTo(toAddress: addr)
+ }
+ } else {
+ showingDialer = false
+
+ startCallViewModel.searchField = ""
+ magicSearch.currentFilter = ""
+
+ magicSearch.searchForContacts()
+
+ if callViewModel.isTransferInsteadCall == true {
+ callViewModel.isTransferInsteadCall = false
+ }
+
+ resetCallView()
+
+ delayColorDismiss()
+
+ withAnimation {
+ isShowStartCallFragment.toggle()
+ telecomManager.doCallOrJoinConf(address: addr)
+ }
}
-
- resetCallView()
-
- delayColorDismiss()
-
- withAnimation {
- isShowStartCallFragment.toggle()
- callViewModel.blindTransferCallTo(toAddress: addr)
- }
- } else {
- showingDialer = false
-
- startCallViewModel.searchField = ""
- magicSearch.currentFilter = ""
-
- magicSearch.searchForContacts()
-
- if callViewModel.isTransferInsteadCall == true {
- callViewModel.isTransferInsteadCall = false
- }
-
- resetCallView()
-
- delayColorDismiss()
-
- withAnimation {
- isShowStartCallFragment.toggle()
- telecomManager.doCallOrJoinConf(address: addr)
- }
- }
- })
- .padding(.horizontal, 16)
-
- if !contactsManager.lastSearchSuggestions.isEmpty {
- HStack(alignment: .center) {
- Text("generic_address_picker_suggestions_list_title")
- .default_text_style_800(styleSize: 16)
-
- Spacer()
- }
- .padding(.vertical, 10)
+ })
.padding(.horizontal, 16)
- suggestionsList
+ if !contactsManager.lastSearchSuggestions.isEmpty {
+ HStack(alignment: .center) {
+ Text("generic_address_picker_suggestions_list_title")
+ .default_text_style_800(styleSize: 16)
+
+ Spacer()
+ }
+ .padding(.vertical, 10)
+ .padding(.horizontal, 16)
+
+ suggestionsList
+ }
+ }
+
+ if magicSearch.isLoading {
+ ProgressView()
+ .controlSize(.large)
+ .progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500))
}
}
}
diff --git a/Linphone/UI/Main/History/ViewModel/HistoryListViewModel.swift b/Linphone/UI/Main/History/ViewModel/HistoryListViewModel.swift
index 90aff417b..ceb6df8ea 100644
--- a/Linphone/UI/Main/History/ViewModel/HistoryListViewModel.swift
+++ b/Linphone/UI/Main/History/ViewModel/HistoryListViewModel.swift
@@ -45,7 +45,12 @@ class HistoryListViewModel: ObservableObject {
func computeCallLogsList() {
coreContext.doOnCoreQueue { core in
let account = core.defaultAccount
- let logs = account?.callLogs != nil ? account!.callLogs : core.callLogs
+
+ // Fetch all call logs if only one account to workaround no history issue
+ // TODO FIXME: remove workaround later
+ let logs = (core.accountList.count > 1)
+ ? (account?.callLogs ?? core.callLogs)
+ : core.callLogs
var callLogsBis: [HistoryModel] = []
var callLogsTmpBis: [HistoryModel] = []
diff --git a/Linphone/UI/Main/History/ViewModel/StartCallViewModel.swift b/Linphone/UI/Main/History/ViewModel/StartCallViewModel.swift
index 1b3580a95..3b69db4cf 100644
--- a/Linphone/UI/Main/History/ViewModel/StartCallViewModel.swift
+++ b/Linphone/UI/Main/History/ViewModel/StartCallViewModel.swift
@@ -157,7 +157,7 @@ class StartCallViewModel: ObservableObject {
func interpretAndStartCall() {
CoreContext.shared.doOnCoreQueue { core in
- let address = core.interpretUrl(url: self.searchField, applyInternationalPrefix: true)
+ let address = core.interpretUrl(url: self.searchField, applyInternationalPrefix: LinphoneUtils.applyInternationalPrefix(core: core))
if address != nil {
TelecomManager.shared.doCallOrJoinConf(address: address!)
}
diff --git a/Linphone/UI/Main/Meetings/Fragments/AddParticipantsFragment.swift b/Linphone/UI/Main/Meetings/Fragments/AddParticipantsFragment.swift
index 92cdfb634..7d2df6c33 100644
--- a/Linphone/UI/Main/Meetings/Fragments/AddParticipantsFragment.swift
+++ b/Linphone/UI/Main/Meetings/Fragments/AddParticipantsFragment.swift
@@ -167,76 +167,84 @@ struct AddParticipantsFragment: View {
.padding(.bottom)
.padding(.horizontal)
- ScrollView {
- ForEach(0.. \"$SRCROOT/Linphone/GeneratedGit.swift\"\nimport Foundation\n\npublic let APP_GIT_BRANCH = \"$branch\"\npublic let APP_GIT_COMMIT = \"$commit\"\npublic let APP_GIT_TAG = \"$tag\"\nEOF\n";
+ };
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -1398,6 +1419,7 @@
C628172E2C1C3A3600DBA646 /* AccountExtension.swift in Sources */,
66C491FF2B24D4AC00CEA16D /* FileUtils.swift in Sources */,
C62817322C1C400A00DBA646 /* StringExtension.swift in Sources */,
+ D7D1F5452EDDBBA70034EEB0 /* GeneratedGit.swift in Sources */,
D74C9D012ACB098C0021626A /* PermissionManager.swift in Sources */,
D7B99E992B29B39000BE7BF2 /* CallViewModel.swift in Sources */,
D7702EF22AC7205000557C00 /* WelcomeView.swift in Sources */,
@@ -1984,7 +2006,7 @@
kind = branch;
};
};
- D7690B3B2EAF8878009CB3B7 /* XCRemoteSwiftPackageReference "linphone-sdk-swift-ios" */ = {
+ D7D1F5482EDDD8D30034EEB0 /* XCRemoteSwiftPackageReference "linphone-sdk-swift-ios" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://gitlab.linphone.org/BC/public/linphone-sdk-swift-ios.git";
requirement = {
diff --git a/LinphoneApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/LinphoneApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 91670816a..197fc9498 100644
--- a/LinphoneApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/LinphoneApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -124,7 +124,7 @@
"location" : "https://gitlab.linphone.org/BC/public/linphone-sdk-swift-ios.git",
"state" : {
"branch" : "alpha",
- "revision" : "b92c41b87c69771ccd276a957ab02c20178dffeb"
+ "revision" : "43ee1a062ef73808e27afe3c5341a27c1b82aae7"
}
},
{