mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 02:58:07 +00:00
Merge remote-tracking branch 'refs/remotes/origin/master'
#Conflicts: # LinphoneApp.xcodeproj/project.pbxproj
This commit is contained in:
commit
0f8df65dff
52 changed files with 1423 additions and 636 deletions
|
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#4e6074" viewBox="0 0 256 256"><path d="M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM72,48v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24V80H48V48ZM208,208H48V96H208V208Zm-96-88v64a8,8,0,0,1-16,0V132.94l-4.42,2.22a8,8,0,0,1-7.16-14.32l16-8A8,8,0,0,1,112,120Zm59.16,30.45L152,176h16a8,8,0,0,1,0,16H136a8,8,0,0,1-6.4-12.8l28.78-38.37A8,8,0,1,0,145.07,132a8,8,0,1,1-13.85-8A24,24,0,0,1,176,136,23.76,23.76,0,0,1,171.16,150.45Z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM72,48v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24V80H48V48ZM208,208H48V96H208V208Zm-96-88v64a8,8,0,0,1-16,0V132.94l-4.42,2.22a8,8,0,0,1-7.16-14.32l16-8A8,8,0,0,1,112,120Zm59.16,30.45L152,176h16a8,8,0,0,1,0,16H136a8,8,0,0,1-6.4-12.8l28.78-38.37A8,8,0,1,0,145.07,132a8,8,0,1,1-13.85-8A24,24,0,0,1,176,136,23.76,23.76,0,0,1,171.16,150.45Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 604 B After Width: | Height: | Size: 604 B |
21
Linphone/Assets.xcassets/trash.imageset/Contents.json
vendored
Normal file
21
Linphone/Assets.xcassets/trash.imageset/Contents.json
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
1
Linphone/Assets.xcassets/trash.imageset/trash.svg
vendored
Normal file
1
Linphone/Assets.xcassets/trash.imageset/trash.svg
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 415 B |
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
5
Linphone/GeneratedGit.swift
Normal file
5
Linphone/GeneratedGit.swift
Normal file
|
|
@ -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"
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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" = "Подключен";
|
||||
|
|
|
|||
|
|
@ -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ť";
|
||||
|
|
|
|||
|
|
@ -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" = "Жоден";
|
||||
|
|
|
|||
|
|
@ -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" = "已连接";
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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!)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
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()
|
||||
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()
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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 = ""
|
||||
}
|
||||
},
|
||||
titleSecondButton: Text("dialog_ok"),
|
||||
actionSecondButton: {
|
||||
if let historyListVM = historyListViewModel {
|
||||
historyListVM.removeCallLogs()
|
||||
}
|
||||
self.isShowDeleteAllHistoryPopup.toggle()
|
||||
sharedMainViewModel.displayedCall = nil
|
||||
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()
|
||||
})
|
||||
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 = ""
|
||||
}
|
||||
}
|
||||
)
|
||||
.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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
messageText = " "
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
||||
messageText = ""
|
||||
isMessageTextFocused = true
|
||||
|
||||
conversationViewModel.sendMessage(messageText: messageTextTmp)
|
||||
}
|
||||
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)
|
||||
|
|
@ -1097,27 +1158,66 @@ 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)
|
||||
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)
|
||||
}
|
||||
.padding(.vertical, 5)
|
||||
.padding(.horizontal, 20)
|
||||
|
||||
Divider()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
ZStack {
|
||||
ScrollView {
|
||||
if !ContactsManager.shared.lastSearch.isEmpty {
|
||||
HStack(alignment: .center) {
|
||||
Text("contacts_list_all_contacts_title")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
|
||||
Spacer()
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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 magicSearch.isLoading {
|
||||
ProgressView()
|
||||
.controlSize(.large)
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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,12 +141,18 @@ 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
|
||||
|
||||
getContentTextMessage(chatRoom: chatRoom)
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Bool>) {
|
||||
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<Bool>) {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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!)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
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: 20)
|
||||
.frame(height: 35)
|
||||
.frame(maxWidth: .infinity)
|
||||
})
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.vertical, 10)
|
||||
.background(Color.orangeMain500)
|
||||
.cornerRadius(60)
|
||||
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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
ZStack {
|
||||
ScrollView {
|
||||
if !ContactsManager.shared.lastSearch.isEmpty {
|
||||
HStack(alignment: .center) {
|
||||
Text("contacts_list_all_contacts_title")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
|
||||
Spacer()
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
.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
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
.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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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] = []
|
||||
|
|
|
|||
|
|
@ -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!)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,76 +167,84 @@ struct AddParticipantsFragment: View {
|
|||
.padding(.bottom)
|
||||
.padding(.horizontal)
|
||||
|
||||
ScrollView {
|
||||
ForEach(0..<contactsManager.avatarListModel.count, id: \.self) { index in
|
||||
HStack {
|
||||
ZStack {
|
||||
ScrollView {
|
||||
ForEach(0..<contactsManager.avatarListModel.count, id: \.self) { index in
|
||||
HStack {
|
||||
if index == 0
|
||||
|| contactsManager.avatarListModel[index].name.lowercased().folding(
|
||||
options: .diacriticInsensitive,
|
||||
locale: .current
|
||||
).first
|
||||
!= contactsManager.avatarListModel[index-1].name.lowercased().folding(
|
||||
options: .diacriticInsensitive,
|
||||
locale: .current
|
||||
).first {
|
||||
Text(
|
||||
String(
|
||||
(contactsManager.avatarListModel[index].name.uppercased().folding(
|
||||
options: .diacriticInsensitive,
|
||||
locale: .current
|
||||
).first)!))
|
||||
.contact_text_style_500(styleSize: 20)
|
||||
.frame(width: 18)
|
||||
.padding(.leading, 5)
|
||||
.padding(.trailing, 5)
|
||||
} else {
|
||||
Text("")
|
||||
HStack {
|
||||
if index == 0
|
||||
|| contactsManager.avatarListModel[index].name.lowercased().folding(
|
||||
options: .diacriticInsensitive,
|
||||
locale: .current
|
||||
).first
|
||||
!= contactsManager.avatarListModel[index-1].name.lowercased().folding(
|
||||
options: .diacriticInsensitive,
|
||||
locale: .current
|
||||
).first {
|
||||
Text(
|
||||
String(
|
||||
(contactsManager.avatarListModel[index].name.uppercased().folding(
|
||||
options: .diacriticInsensitive,
|
||||
locale: .current
|
||||
).first)!))
|
||||
.contact_text_style_500(styleSize: 20)
|
||||
.frame(width: 18)
|
||||
.padding(.leading, 5)
|
||||
.padding(.trailing, 5)
|
||||
}
|
||||
} else {
|
||||
Text("")
|
||||
.contact_text_style_500(styleSize: 20)
|
||||
.frame(width: 18)
|
||||
.padding(.leading, 5)
|
||||
.padding(.trailing, 5)
|
||||
}
|
||||
|
||||
Avatar(contactAvatarModel: contactsManager.avatarListModel[index], avatarSize: 50)
|
||||
Avatar(contactAvatarModel: contactsManager.avatarListModel[index], avatarSize: 50)
|
||||
|
||||
Text(contactsManager.avatarListModel[index].name)
|
||||
.default_text_style(styleSize: 16)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.foregroundStyle(Color.orangeMain500)
|
||||
|
||||
if addParticipantsViewModel.participantsToAdd.contains(where: {
|
||||
$0.address.asStringUriOnly() == contactsManager.avatarListModel[index].address
|
||||
}) {
|
||||
Image("check")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
Text(contactsManager.avatarListModel[index].name)
|
||||
.default_text_style(styleSize: 16)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.foregroundStyle(Color.orangeMain500)
|
||||
.frame(width: 25, height: 25)
|
||||
.padding(.horizontal)
|
||||
|
||||
if addParticipantsViewModel.participantsToAdd.contains(where: {
|
||||
$0.address.asStringUriOnly() == contactsManager.avatarListModel[index].address
|
||||
}) {
|
||||
Image("check")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.orangeMain500)
|
||||
.frame(width: 25, height: 25)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.background(.white)
|
||||
.onTapGesture {
|
||||
if let addr = try? Factory.Instance.createAddress(addr: contactsManager.avatarListModel[index].address) {
|
||||
addParticipantsViewModel.selectParticipant(addr: addr)
|
||||
.background(.white)
|
||||
.onTapGesture {
|
||||
if let addr = try? Factory.Instance.createAddress(addr: contactsManager.avatarListModel[index].address) {
|
||||
addParticipantsViewModel.selectParticipant(addr: addr)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
.listRowSeparator(.hidden)
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
.listRowSeparator(.hidden)
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Text("generic_address_picker_suggestions_list_title")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
suggestionsList
|
||||
}
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Text("generic_address_picker_suggestions_list_title")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
|
||||
Spacer()
|
||||
if magicSearch.isLoading {
|
||||
ProgressView()
|
||||
.controlSize(.large)
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500))
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
suggestionsList
|
||||
}
|
||||
}
|
||||
Button {
|
||||
|
|
|
|||
|
|
@ -468,7 +468,7 @@ struct ScheduleMeetingFragment: View {
|
|||
showDatePicker.toggle()
|
||||
}
|
||||
}
|
||||
Text("dialog_ok")
|
||||
Text("dialog_confirm")
|
||||
.default_text_style_orange_500(styleSize: 16)
|
||||
.onTapGesture {
|
||||
pickDate()
|
||||
|
|
|
|||
|
|
@ -606,15 +606,16 @@ struct AccountProfileFragment: View {
|
|||
.background(Color.gray100)
|
||||
|
||||
if self.isShowPopup {
|
||||
PopupView(isShowPopup: $isShowPopup,
|
||||
PopupView(
|
||||
isShowPopup: $isShowPopup,
|
||||
title: Text("manage_account_international_prefix"),
|
||||
content: Text("manage_account_dialog_international_prefix_help_message"),
|
||||
titleFirstButton: nil,
|
||||
actionFirstButton: {},
|
||||
titleSecondButton: Text("dialog_ok"),
|
||||
actionSecondButton: {
|
||||
self.isShowPopup.toggle()
|
||||
}
|
||||
titleSecondButton: Text("dialog_confirm"),
|
||||
actionSecondButton: { self.isShowPopup.toggle() },
|
||||
titleThirdButton: nil,
|
||||
actionThirdButton: {}
|
||||
)
|
||||
.background(.black.opacity(0.65))
|
||||
.onTapGesture {
|
||||
|
|
@ -635,10 +636,8 @@ struct AccountProfileFragment: View {
|
|||
isShowPopup: $isShowLogoutPopup,
|
||||
title: Text("manage_account_dialog_remove_account_title"),
|
||||
content: contentPopup1 + contentPopup2,
|
||||
titleFirstButton: Text("dialog_cancel"),
|
||||
actionFirstButton: {
|
||||
self.isShowLogoutPopup.toggle()
|
||||
},
|
||||
titleFirstButton: nil,
|
||||
actionFirstButton: {},
|
||||
titleSecondButton: Text("manage_account_delete"),
|
||||
actionSecondButton: {
|
||||
if accountProfileViewModel.accountModelIndex != nil {
|
||||
|
|
@ -650,7 +649,9 @@ struct AccountProfileFragment: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
titleThirdButton: Text("dialog_cancel"),
|
||||
actionThirdButton: { self.isShowLogoutPopup.toggle() }
|
||||
)
|
||||
.background(.black.opacity(0.65))
|
||||
.onTapGesture {
|
||||
|
|
|
|||
|
|
@ -132,6 +132,12 @@ struct AccountSettingsFragment: View {
|
|||
)
|
||||
.focused($isMwiUriFocused)
|
||||
}
|
||||
|
||||
Toggle("account_settings_apply_international_prefix_title", isOn: $accountSettingsViewModel.applyInternationalPrefix)
|
||||
.default_text_style_700(styleSize: 15)
|
||||
|
||||
Toggle("account_settings_replace_plus_by_00_title", isOn: $accountSettingsViewModel.replacePlusBy00)
|
||||
.default_text_style_700(styleSize: 15)
|
||||
}
|
||||
.padding(.vertical, 30)
|
||||
.padding(.horizontal, 20)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ class AccountSettingsViewModel: ObservableObject {
|
|||
@Published var imEncryptionMandatory: Bool
|
||||
@Published var voicemailUri: String
|
||||
@Published var mwiUri: String
|
||||
@Published var applyInternationalPrefix: Bool
|
||||
@Published var replacePlusBy00: Bool
|
||||
@Published var stunServerUrl: String
|
||||
@Published var enableIce: Bool
|
||||
@Published var enableTurn: Bool
|
||||
|
|
@ -57,6 +59,8 @@ class AccountSettingsViewModel: ObservableObject {
|
|||
self.imEncryptionMandatory = accountModel.account.params?.instantMessagingEncryptionMandatory ?? false
|
||||
self.voicemailUri = accountModel.account.params?.voicemailAddress?.asStringUriOnly() ?? ""
|
||||
self.mwiUri = accountModel.account.params?.mwiServerAddress?.asStringUriOnly() ?? ""
|
||||
self.applyInternationalPrefix = accountModel.account.params?.useInternationalPrefixForCallsAndChats ?? false
|
||||
self.replacePlusBy00 = accountModel.account.params?.dialEscapePlusEnabled ?? false
|
||||
|
||||
self.natPolicy = accountModel.account.params?.natPolicy
|
||||
self.stunServerUrl = accountModel.account.params?.natPolicy?.stunServer ?? ""
|
||||
|
|
@ -190,6 +194,9 @@ class AccountSettingsViewModel: ObservableObject {
|
|||
newParams.voicemailAddress = nil
|
||||
}
|
||||
|
||||
newParams.useInternationalPrefixForCallsAndChats = self.applyInternationalPrefix
|
||||
newParams.dialEscapePlusEnabled = self.replacePlusBy00
|
||||
|
||||
let expireInt: Int = {
|
||||
if !self.expire.isEmpty {
|
||||
return Int(self.expire) ?? 31536000
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ class AccountModel: ObservableObject {
|
|||
self.computeNotificationsCount()
|
||||
}, onChatRoomRead: { (_: Core, _: ChatRoom) in
|
||||
self.computeNotificationsCount()
|
||||
}, onMessageRetracted: { (_: Core, _: ChatRoom, _: ChatMessage) in
|
||||
self.computeNotificationsCount()
|
||||
})
|
||||
core.addDelegate(delegate: coreDelegate!)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ final class MagicSearchSingleton: ObservableObject {
|
|||
@Published var allContact = false
|
||||
let allContactKey = "all_contact"
|
||||
|
||||
private var domainDefaultAccount = ""
|
||||
var domainDefaultAccount = ""
|
||||
|
||||
var searchDelegate: MagicSearchDelegate?
|
||||
|
||||
|
|
@ -49,6 +49,8 @@ final class MagicSearchSingleton: ObservableObject {
|
|||
let linphoneAddressBookFriendList = "Linphone address-book"
|
||||
let tempRemoteAddressBookFriendList = "TempRemoteDirectoryContacts address-book"
|
||||
|
||||
@Published var isLoading = false
|
||||
|
||||
func destroyMagicSearch() {
|
||||
magicSearch = nil
|
||||
}
|
||||
|
|
@ -62,7 +64,7 @@ final class MagicSearchSingleton: ObservableObject {
|
|||
}
|
||||
|
||||
coreContext.doOnCoreQueue { core in
|
||||
self.domainDefaultAccount = core.defaultAccount?.params?.domain ?? ""
|
||||
self.domainDefaultAccount = (core.defaultAccount?.params?.domain?.contains("sip.linphone.org") == true) ? (core.defaultAccount?.params?.domain ?? "") : "*"
|
||||
|
||||
self.magicSearch = try? core.createMagicSearch()
|
||||
|
||||
|
|
@ -171,6 +173,12 @@ final class MagicSearchSingleton: ObservableObject {
|
|||
addedAvatarListModel: [ContactAvatarModel]
|
||||
) {
|
||||
DispatchQueue.main.async {
|
||||
if SharedMainViewModel.shared.displayedFriend != nil {
|
||||
if let avatarModel = addedAvatarListModel.first(where: { $0.address == SharedMainViewModel.shared.displayedFriend?.address }) {
|
||||
SharedMainViewModel.shared.displayedFriend = avatarModel
|
||||
}
|
||||
}
|
||||
|
||||
self.contactsManager.lastSearch = sortedLastSearch
|
||||
self.contactsManager.lastSearchSuggestions = lastSearchSuggestions
|
||||
|
||||
|
|
@ -185,6 +193,8 @@ final class MagicSearchSingleton: ObservableObject {
|
|||
NotificationCenter.default.post(name: NSNotification.Name("ContactLoaded"), object: nil)
|
||||
}
|
||||
|
||||
self.isLoading = false
|
||||
|
||||
self.contactLoadedDebounceWorkItem = workItem
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: workItem)
|
||||
}
|
||||
|
|
@ -192,6 +202,10 @@ final class MagicSearchSingleton: ObservableObject {
|
|||
|
||||
func searchForContacts() {
|
||||
coreContext.doOnCoreQueue { _ in
|
||||
DispatchQueue.main.async {
|
||||
self.isLoading = true
|
||||
}
|
||||
|
||||
var needResetCache = false
|
||||
|
||||
if let oldFilter = self.previousFilter {
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@
|
|||
D7D1698C2AE66FA500109A5C /* MagicSearchSingleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1698B2AE66FA500109A5C /* MagicSearchSingleton.swift */; };
|
||||
D7D1F5262EDD91B30034EEB0 /* RecordingMediaPlayerFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1F5252EDD91B10034EEB0 /* RecordingMediaPlayerFragment.swift */; };
|
||||
D7D1F5282EDD939E0034EEB0 /* RecordingMediaPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1F5272EDD939D0034EEB0 /* RecordingMediaPlayerViewModel.swift */; };
|
||||
D7D1F5452EDDBBA70034EEB0 /* GeneratedGit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1F5442EDDBBA70034EEB0 /* GeneratedGit.swift */; };
|
||||
D7D24D132AC1B4E800C6F35B /* NotoSans-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0D2AC1B4E800C6F35B /* NotoSans-Medium.ttf */; };
|
||||
D7D24D142AC1B4E800C6F35B /* NotoSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0E2AC1B4E800C6F35B /* NotoSans-Regular.ttf */; };
|
||||
D7D24D152AC1B4E800C6F35B /* NotoSans-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0F2AC1B4E800C6F35B /* NotoSans-Light.ttf */; };
|
||||
|
|
@ -418,6 +419,7 @@
|
|||
D7D1698B2AE66FA500109A5C /* MagicSearchSingleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MagicSearchSingleton.swift; sourceTree = "<group>"; };
|
||||
D7D1F5252EDD91B10034EEB0 /* RecordingMediaPlayerFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingMediaPlayerFragment.swift; sourceTree = "<group>"; };
|
||||
D7D1F5272EDD939D0034EEB0 /* RecordingMediaPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingMediaPlayerViewModel.swift; sourceTree = "<group>"; };
|
||||
D7D1F5442EDDBBA70034EEB0 /* GeneratedGit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneratedGit.swift; sourceTree = "<group>"; };
|
||||
D7D24D0D2AC1B4E800C6F35B /* NotoSans-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Medium.ttf"; sourceTree = "<group>"; };
|
||||
D7D24D0E2AC1B4E800C6F35B /* NotoSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Regular.ttf"; sourceTree = "<group>"; };
|
||||
D7D24D0F2AC1B4E800C6F35B /* NotoSans-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Light.ttf"; sourceTree = "<group>"; };
|
||||
|
|
@ -672,6 +674,7 @@
|
|||
D719ABBD2ABC67BF00B41C10 /* Preview Content */,
|
||||
D7D24D0C2AC1B4C700C6F35B /* Fonts */,
|
||||
D7ADF6012AFE5C7C00212231 /* Ressources */,
|
||||
D7D1F5442EDDBBA70034EEB0 /* GeneratedGit.swift */,
|
||||
);
|
||||
path = Linphone;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1155,6 +1158,7 @@
|
|||
660AAF802B839272004C0FA6 /* Embed Foundation Extensions */,
|
||||
D719ABB12ABC67BF00B41C10 /* Resources */,
|
||||
D72F04F52DDB2CB800F4C713 /* ShellScript */,
|
||||
D7D1F5432EDDB9C20034EEB0 /* ShellScript */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -1231,7 +1235,7 @@
|
|||
D7D5AD7B2DD34E4D00016721 /* XCRemoteSwiftPackageReference "AppAuth-iOS" */,
|
||||
D7D5AD7C2DD34E7C00016721 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
|
||||
D7DF8BE42E2104D0003A3BC7 /* XCRemoteSwiftPackageReference "Elegant-Emoji-Picker" */,
|
||||
D7690B3B2EAF8878009CB3B7 /* XCRemoteSwiftPackageReference "linphone-sdk-swift-ios" */,
|
||||
D7D1F5482EDDD8D30034EEB0 /* XCRemoteSwiftPackageReference "linphone-sdk-swift-ios" */,
|
||||
);
|
||||
productRefGroup = D719ABB42ABC67BF00B41C10 /* Products */;
|
||||
projectDirPath = "";
|
||||
|
|
@ -1304,6 +1308,23 @@
|
|||
shellPath = /bin/sh;
|
||||
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n\"${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run\"\n";
|
||||
};
|
||||
D7D1F5432EDDB9C20034EEB0 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n#!/bin/bash\n\nbranch=$(git rev-parse --abbrev-ref HEAD)\ncommit=$(git rev-parse --short HEAD)\ntag=$(git describe --tags --abbrev=0 2>/dev/null || echo \"no-tag\")\n\ncat <<EOF > \"$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 = {
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@
|
|||
"location" : "https://gitlab.linphone.org/BC/public/linphone-sdk-swift-ios.git",
|
||||
"state" : {
|
||||
"branch" : "alpha",
|
||||
"revision" : "b92c41b87c69771ccd276a957ab02c20178dffeb"
|
||||
"revision" : "43ee1a062ef73808e27afe3c5341a27c1b82aae7"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue