diff --git a/Linphone/Assets.xcassets/arrow-right.imageset/Contents.json b/Linphone/Assets.xcassets/arrow-right.imageset/Contents.json
new file mode 100644
index 000000000..e32a0420f
--- /dev/null
+++ b/Linphone/Assets.xcassets/arrow-right.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "arrow-right.svg",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Linphone/Assets.xcassets/arrow-right.imageset/arrow-right.svg b/Linphone/Assets.xcassets/arrow-right.imageset/arrow-right.svg
new file mode 100644
index 000000000..39e1452e8
--- /dev/null
+++ b/Linphone/Assets.xcassets/arrow-right.imageset/arrow-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Linphone/GeneratedGit.swift b/Linphone/GeneratedGit.swift
index 757db7c38..213b92b88 100644
--- a/Linphone/GeneratedGit.swift
+++ b/Linphone/GeneratedGit.swift
@@ -1,7 +1,7 @@
import Foundation
public enum AppGitInfo {
- public static let branch = "master"
- public static let commit = "304651d77"
+ public static let branch = "feature/trust_list"
+ public static let commit = "692a308d7"
public static let tag = "6.1.0-alpha"
}
diff --git a/Linphone/Localizable/en.lproj/Localizable.strings b/Linphone/Localizable/en.lproj/Localizable.strings
index 1eaa0e7fe..a8a1480cd 100644
--- a/Linphone/Localizable/en.lproj/Localizable.strings
+++ b/Linphone/Localizable/en.lproj/Localizable.strings
@@ -172,8 +172,15 @@
"contact_details_numbers_and_addresses_title" = "Phone numbers & SIP addresses";
"contact_details_remove_from_favourites" = "Remove from favourites";
"contact_details_share" = "Share";
-"contact_dialog_delete_message" = "This contact will be definitively removed.";
+"contact_details_trust_title" = "Trust";
+"contact_details_no_device_found" = "No device found…";
+"contact_details_trusted_devices_count" = "Number of trusted devices:";
+"contact_dialog_increase_trust_level_title" = "Increase trust level";
+"contact_dialog_increase_trust_level_message" = "You're about to make a call to %1$@'s device %2$@.\nDo you want to make the call?";
+"contact_dialog_devices_trust_help_title" = "Trust level";
+"contact_dialog_devices_trust_help_message" = "Check all of your contact devices to make sure your communications will be secured an unaltered.\nWhen all will be verified, you'll reach maximum trust level.";
"contact_dialog_delete_title" = "Delete %@?";
+"contact_dialog_delete_message" = "This contact will be definitively removed.";
"contact_dialog_pick_phone_number_or_sip_address_title" = "Choose a number or a SIP address";
"contact_edit_title" = "Edit contact";
"contact_editor_company" = "Company";
@@ -182,6 +189,8 @@
"contact_editor_first_name" = "First name";
"contact_editor_job_title" = "Job title";
"contact_editor_last_name" = "Last name";
+"contact_make_call_check_device_trust" = "Verify";
+"contact_device_without_name" = "Unnamed device";
"contact_message_action" = "Message";
"contact_new_title" = "New contact";
"contact_video_call_action" = "Video call";
@@ -288,8 +297,10 @@
"dialog_continue" = "Continue";
"dialog_deny" = "Deny";
"dialog_install" = "Install";
+"dialog_do_not_show_anymore" = "Do not show this dialog anymore";
"dialog_no" = "No";
"dialog_confirm" = "Confirm";
+"dialog_understood" = "Understood";
"dialog_yes" = "Yes";
"drawer_menu_account_connection_status_cleared" = "Disabled";
"drawer_menu_account_connection_status_connected" = "Connected";
diff --git a/Linphone/Localizable/fr.lproj/Localizable.strings b/Linphone/Localizable/fr.lproj/Localizable.strings
index 0d87159c3..c9ab342bb 100644
--- a/Linphone/Localizable/fr.lproj/Localizable.strings
+++ b/Linphone/Localizable/fr.lproj/Localizable.strings
@@ -172,8 +172,15 @@
"contact_details_numbers_and_addresses_title" = "Coordonnées";
"contact_details_remove_from_favourites" = "Retirer des favoris";
"contact_details_share" = "Partager";
-"contact_dialog_delete_message" = "Ce contact sera définitivement supprimé.";
+"contact_details_trust_title" = "Confiance";
+"contact_details_no_device_found" = "Aucun appareil trouvé";
+"contact_details_trusted_devices_count" = "Appareils du contact :";
+"contact_dialog_increase_trust_level_title" = "Vérifier l'appareil ?";
+"contact_dialog_increase_trust_level_message" = "Voulez-vous appeler l’appareil %2$@ de %1$@ ?\nVoulez-vous passer l’appel ?";
+"contact_dialog_devices_trust_help_title" = "Niveau de confiance";
+"contact_dialog_devices_trust_help_message" = "Vérifiez les appareils de votre contact pour confirmer que vos communications seront sécurisées et sans compromission.\nQuand tous seront vérifiés, vous atteindrez le niveau de confiance maximal.";
"contact_dialog_delete_title" = "Supprimer %@ ?";
+"contact_dialog_delete_message" = "Ce contact sera définitivement supprimé.";
"contact_dialog_pick_phone_number_or_sip_address_title" = "Choisissez un numéro ou adresse SIP";
"contact_edit_title" = "Modifier contact";
"contact_editor_company" = "Entreprise";
@@ -182,6 +189,8 @@
"contact_editor_first_name" = "Prénom";
"contact_editor_job_title" = "Poste";
"contact_editor_last_name" = "Nom de famille";
+"contact_make_call_check_device_trust" = "Vérifier";
+"contact_device_without_name" = "Appareil sans nom";
"contact_message_action" = "Message";
"contact_new_title" = "Nouveau contact";
"contact_video_call_action" = "Appel vidéo";
@@ -288,8 +297,10 @@
"dialog_continue" = "Continuer";
"dialog_deny" = "Refuser";
"dialog_install" = "Installer";
+"dialog_do_not_show_anymore" = "Ne plus me montrer ce message";
"dialog_no" = "Non";
"dialog_confirm" = "Confirmer";
+"dialog_understood" = "J'ai compris";
"dialog_yes" = "Oui";
"drawer_menu_account_connection_status_cleared" = "Désactivé";
"drawer_menu_account_connection_status_connected" = "Connecté";
diff --git a/Linphone/UI/Main/Contacts/Fragments/ContactFragment.swift b/Linphone/UI/Main/Contacts/Fragments/ContactFragment.swift
index 10ec4c7ca..f61119f9b 100644
--- a/Linphone/UI/Main/Contacts/Fragments/ContactFragment.swift
+++ b/Linphone/UI/Main/Contacts/Fragments/ContactFragment.swift
@@ -29,8 +29,10 @@ struct ContactFragment: View {
@Binding var isShowDeletePopup: Bool
@Binding var isShowDismissPopup: Bool
+ @Binding var isShowTrustLevelPopup: Bool
@Binding var isShowSipAddressesPopup: Bool
@Binding var isShowSipAddressesPopupType: Int
+ @Binding var isShowIncreaseTrustLevelPopup: Bool
@Binding var isShowEditContactFragmentInContactDetails: Bool
@State private var showingSheet = false
@@ -68,19 +70,11 @@ struct ContactFragment: View {
showingSheet: $showingSheet,
showShareSheet: $showShareSheet,
isShowDismissPopup: $isShowDismissPopup,
+ isShowTrustLevelPopup: $isShowTrustLevelPopup,
isShowSipAddressesPopup: $isShowSipAddressesPopup,
isShowSipAddressesPopupType: $isShowSipAddressesPopupType,
+ isShowIncreaseTrustLevelPopup: $isShowIncreaseTrustLevelPopup,
isShowEditContactFragmentInContactDetails: $isShowEditContactFragmentInContactDetails
)
}
}
-
-#Preview {
- ContactFragment(
- isShowDeletePopup: .constant(false),
- isShowDismissPopup: .constant(false),
- isShowSipAddressesPopup: .constant(false),
- isShowSipAddressesPopupType: .constant(0),
- isShowEditContactFragmentInContactDetails: .constant(false)
- )
-}
diff --git a/Linphone/UI/Main/Contacts/Fragments/ContactInnerActionsFragment.swift b/Linphone/UI/Main/Contacts/Fragments/ContactInnerActionsFragment.swift
index d411ed26c..0b3182e4d 100644
--- a/Linphone/UI/Main/Contacts/Fragments/ContactInnerActionsFragment.swift
+++ b/Linphone/UI/Main/Contacts/Fragments/ContactInnerActionsFragment.swift
@@ -29,14 +29,19 @@ struct ContactInnerActionsFragment: View {
@EnvironmentObject var contactAvatarModel: ContactAvatarModel
@EnvironmentObject var contactsListViewModel: ContactsListViewModel
+ @State private var trustIsOpen = true
@State private var informationIsOpen = true
@Binding var showingSheet: Bool
@Binding var showShareSheet: Bool
@Binding var isShowDeletePopup: Bool
@Binding var isShowDismissPopup: Bool
+ @Binding var isShowTrustLevelPopup: Bool
+ @Binding var isShowIncreaseTrustLevelPopup: Bool
@Binding var isShowEditContactFragmentInContactDetails: Bool
+ let geometry: GeometryProxy
+
var actionEditButton: () -> Void
var body: some View {
@@ -180,12 +185,14 @@ struct ContactInnerActionsFragment: View {
.padding(.horizontal)
.zIndex(-1)
.transition(.move(edge: .top))
+ .background(Color.gray100)
}
} else {
HStack {}
.frame(height: 20)
}
+
if !contactAvatarModel.organization.isEmpty || !contactAvatarModel.jobTitle.isEmpty {
VStack {
if !contactAvatarModel.organization.isEmpty {
@@ -213,17 +220,188 @@ struct ContactInnerActionsFragment: View {
.transition(.move(edge: .top))
}
- // TODO Trust Fragment
+ HStack(alignment: .center) {
+ Button {
+ isShowTrustLevelPopup = true
+ } label: {
+ HStack {
+ Text("contact_details_trust_title")
+ .default_text_style_800(styleSize: 15)
+
+ Image("question")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.grayMain2c600)
+ .frame(width: 22, height: 22)
+ }
+ }
+
+ Spacer()
+
+ Image(trustIsOpen ? "caret-up" : "caret-down")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.grayMain2c600)
+ .frame(width: 25, height: 25, alignment: .leading)
+ .padding(.all, 10)
+ }
+ .padding(.vertical, 10)
+ .padding(.horizontal, 16)
+ .background(Color.gray100)
+ .onTapGesture {
+ withAnimation {
+ trustIsOpen.toggle()
+ }
+ }
- // TODO Medias Fragment
+ if trustIsOpen {
+ VStack(spacing: 0) {
+ if !contactsListViewModel.devices.isEmpty {
+ Text("contact_details_trusted_devices_count")
+ .default_text_style_700(styleSize: 14)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .padding(.top, 20)
+ .padding(.horizontal, 20)
+ .padding(.bottom, 10)
+
+ let radius = geometry.size.height * 0.5
+ let barWidth = min(geometry.size.width - 70, SharedMainViewModel.shared.maxWidth - 70)
+
+ ZStack(alignment: .leading) {
+ Rectangle()
+ .foregroundColor(Color.blueInfo500.opacity(0.2))
+ .frame(width: barWidth, height: 30)
+ .clipShape(RoundedRectangle(cornerRadius: radius))
+
+ if contactsListViewModel.trustedDevicesPercentage >= 15 {
+ Rectangle()
+ .foregroundColor(Color.blueInfo500)
+ .frame(width: ((contactsListViewModel.trustedDevicesPercentage / 100) * barWidth) - 6, height: 25)
+ .clipShape(RoundedRectangle(cornerRadius: radius))
+ .padding(.horizontal, 3)
+ } else if contactsListViewModel.trustedDevicesPercentage > 0 {
+ Rectangle()
+ .foregroundColor(Color.blueInfo500)
+ .frame(width: ((10 / 100) * barWidth) - 6, height: 25)
+ .clipShape(RoundedRectangle(cornerRadius: radius))
+ .padding(.horizontal, 3)
+ }
+
+ if contactsListViewModel.trustedDevicesPercentage >= 30 {
+ Text(String(Int(contactsListViewModel.trustedDevicesPercentage)) + "%")
+ .default_text_style_white_700(styleSize: 14)
+ .frame(width: (contactsListViewModel.trustedDevicesPercentage / 100) * barWidth, height: 25, alignment: .center)
+ } else {
+ Text(String(Int(contactsListViewModel.trustedDevicesPercentage)) + "%")
+ .foregroundStyle(contactsListViewModel.trustedDevicesPercentage == 0 ? Color.redDanger500 : Color.blueInfo500)
+ .default_text_style_white_700(styleSize: 14)
+ .frame(width: barWidth, height: 25, alignment: .center)
+ }
+ }
+ .frame(width: barWidth, height: 30)
+ .contentShape(Rectangle())
+ .padding(.bottom, 10)
+
+ ForEach(contactsListViewModel.devices) { device in
+ HStack {
+ Text(device.name)
+ .default_text_style(styleSize: 14)
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ HStack {
+ if !device.trusted {
+ Button {
+ SharedMainViewModel.shared.increaseTrustLevelPopupDeviceName = device.name
+ SharedMainViewModel.shared.increaseTrustLevelPopupDeviceAddress = device.address
+ isShowIncreaseTrustLevelPopup = true
+ } label: {
+ HStack {
+ Image("warning-circle")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.orangeMain500)
+ .frame(width: 25, height: 25)
+ .padding(.all, 6)
+
+ Text("contact_make_call_check_device_trust")
+ .foregroundStyle(Color.orangeMain500)
+ .default_text_style(styleSize: 14)
+ .lineLimit(1)
+ .padding(.leading, -5)
+ .padding(.trailing, 15)
+ }
+ }
+ .background(Color.orangeMain100)
+ .cornerRadius(25)
+ } else {
+ ZStack {
+ Button {
+ } label: {
+ HStack {
+ Image("warning-circle")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.orangeMain500)
+ .frame(width: 25, height: 25)
+ .padding(.all, 6)
+
+ Text("contact_make_call_check_device_trust")
+ .foregroundStyle(Color.orangeMain500)
+ .default_text_style(styleSize: 14)
+ .lineLimit(1)
+ .padding(.leading, -5)
+ .padding(.trailing, 15)
+ }
+ }
+ .background(Color.orangeMain100)
+ .cornerRadius(25)
+ .hidden()
+
+ Image("trusted")
+ .resizable()
+ .frame(width: 28, height: 28)
+ }
+ }
+ }
+ .frame(height: 40)
+ }
+ .background(.white)
+ .padding(.vertical, 10)
+ .padding(.horizontal, 20)
+ }
+ } else {
+ Text("contact_details_no_device_found")
+ .default_text_style_700(styleSize: 14)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .padding(.top, 15)
+ .padding(.horizontal, 20)
+ .padding(.bottom, 10)
+ }
+ }
+ .padding(.bottom, 5)
+ .background(.white)
+ .cornerRadius(15)
+ .padding(.horizontal)
+ .zIndex(-2)
+ .transition(.move(edge: .top))
+ }
HStack(alignment: .center) {
Text("contact_details_actions_title")
.default_text_style_800(styleSize: 16)
Spacer()
+
+ Image("caret-up")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.grayMain2c600)
+ .frame(width: 25, height: 25, alignment: .leading)
+ .padding(.all, 10)
+ .hidden()
}
- .padding(.vertical, 10)
+ .padding(.top, 20)
+ .padding(.bottom, 10)
.padding(.horizontal, 16)
.background(Color.gray100)
@@ -372,18 +550,6 @@ struct ContactInnerActionsFragment: View {
.padding(.horizontal)
.zIndex(-1)
.transition(.move(edge: .top))
- }
+ }
}
-
-#Preview {
- ContactInnerActionsFragment(
- showingSheet: .constant(false),
- showShareSheet: .constant(false),
- isShowDeletePopup: .constant(false),
- isShowDismissPopup: .constant(false),
- isShowEditContactFragmentInContactDetails: .constant(false),
- actionEditButton: {}
- )
-}
-
// swiftlint:enable type_body_length
diff --git a/Linphone/UI/Main/Contacts/Fragments/ContactInnerFragment.swift b/Linphone/UI/Main/Contacts/Fragments/ContactInnerFragment.swift
index e58345846..90cb4d3e7 100644
--- a/Linphone/UI/Main/Contacts/Fragments/ContactInnerFragment.swift
+++ b/Linphone/UI/Main/Contacts/Fragments/ContactInnerFragment.swift
@@ -39,57 +39,47 @@ struct ContactInnerFragment: View {
@Binding var showingSheet: Bool
@Binding var showShareSheet: Bool
@Binding var isShowDismissPopup: Bool
+ @Binding var isShowTrustLevelPopup: Bool
@Binding var isShowSipAddressesPopup: Bool
@Binding var isShowSipAddressesPopupType: Int
+ @Binding var isShowIncreaseTrustLevelPopup: Bool
@Binding var isShowEditContactFragmentInContactDetails: Bool
var body: some View {
NavigationView {
- ZStack {
- VStack(spacing: 1) {
- Rectangle()
- .foregroundColor(Color.orangeMain500)
- .edgesIgnoringSafeArea(.top)
- .frame(height: 0)
-
- HStack {
- if !(orientation == .landscapeLeft || orientation == .landscapeRight
- || UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) {
- Image("caret-left")
- .renderingMode(.template)
- .resizable()
- .foregroundStyle(Color.orangeMain500)
- .frame(width: 25, height: 25, alignment: .leading)
- .padding(.all, 10)
- .padding(.top, 2)
- .padding(.leading, -10)
- .onTapGesture {
- withAnimation {
- SharedMainViewModel.shared.displayedFriend = nil
+ GeometryReader { geometry in
+ ZStack {
+ VStack(spacing: 1) {
+ Rectangle()
+ .foregroundColor(Color.orangeMain500)
+ .edgesIgnoringSafeArea(.top)
+ .frame(height: 0)
+
+ HStack {
+ if !(orientation == .landscapeLeft || orientation == .landscapeRight
+ || UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) {
+ Image("caret-left")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.orangeMain500)
+ .frame(width: 25, height: 25, alignment: .leading)
+ .padding(.all, 10)
+ .padding(.top, 2)
+ .padding(.leading, -10)
+ .onTapGesture {
+ withAnimation {
+ SharedMainViewModel.shared.displayedFriend = nil
+ }
}
- }
- }
-
- Spacer()
-
- if !contactAvatarModel.isReadOnly {
- if !contactAvatarModel.editable {
- Button(action: {
- editNativeContact()
- }, label: {
- Image("pencil-simple")
- .renderingMode(.template)
- .resizable()
- .foregroundStyle(Color.orangeMain500)
- .frame(width: 25, height: 25, alignment: .leading)
- .padding(.all, 10)
- .padding(.top, 2)
- })
- } else {
- NavigationLink(destination: EditContactFragment(
- contactAvatarModel: contactAvatarModel,
- isShowEditContactFragment: $isShowEditContactFragmentInContactDetails,
- isShowDismissPopup: $isShowDismissPopup)) {
+ }
+
+ Spacer()
+
+ if !contactAvatarModel.isReadOnly {
+ if !contactAvatarModel.editable {
+ Button(action: {
+ editNativeContact()
+ }, label: {
Image("pencil-simple")
.renderingMode(.template)
.resizable()
@@ -97,151 +87,81 @@ struct ContactInnerFragment: View {
.frame(width: 25, height: 25, alignment: .leading)
.padding(.all, 10)
.padding(.top, 2)
- }
- .simultaneousGesture(
- TapGesture().onEnded {
- isShowEditContactFragmentInContactDetails = true
+ })
+ } else {
+ NavigationLink(destination: EditContactFragment(
+ contactAvatarModel: contactAvatarModel,
+ isShowEditContactFragment: $isShowEditContactFragmentInContactDetails,
+ isShowDismissPopup: $isShowDismissPopup)) {
+ Image("pencil-simple")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.orangeMain500)
+ .frame(width: 25, height: 25, alignment: .leading)
+ .padding(.all, 10)
+ .padding(.top, 2)
}
- )
+ .simultaneousGesture(
+ TapGesture().onEnded {
+ isShowEditContactFragmentInContactDetails = true
+ }
+ )
+ }
}
}
- }
- .frame(maxWidth: .infinity)
- .frame(height: 50)
- .padding(.horizontal)
- .padding(.bottom, 4)
- .background(.white)
-
- ScrollView {
- VStack(spacing: 0) {
+ .frame(maxWidth: .infinity)
+ .frame(height: 50)
+ .padding(.horizontal)
+ .padding(.bottom, 4)
+ .background(.white)
+
+ ScrollView {
VStack(spacing: 0) {
VStack(spacing: 0) {
- if SharedMainViewModel.shared.displayedFriend != nil {
- Avatar(contactAvatarModel: contactAvatarModel, avatarSize: 100)
-
- Text(contactAvatarModel.name)
- .foregroundStyle(Color.grayMain2c700)
- .multilineTextAlignment(.center)
- .default_text_style(styleSize: 14)
- .frame(maxWidth: .infinity)
- .padding(.top, 10)
-
- Text(contactAvatarModel.lastPresenceInfo)
- .foregroundStyle(contactAvatarModel.lastPresenceInfo == "Online"
- ? Color.greenSuccess500
- : Color.orangeWarning600)
- .multilineTextAlignment(.center)
- .default_text_style_300(styleSize: 12)
- .frame(maxWidth: .infinity)
- }
- }
- .frame(minHeight: 150)
- .frame(maxWidth: .infinity)
- .padding(.top, 10)
- .background(Color.gray100)
-
- HStack {
- Spacer()
-
- Button(action: {
- CoreContext.shared.doOnCoreQueue { core in
- if contactAvatarModel.addresses.count == 1 {
- do {
- let address = try Factory.Instance.createAddress(addr: contactAvatarModel.address)
- telecomManager.doCallOrJoinConf(address: address, isVideo: false)
- } catch {
- 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: LinphoneUtils.applyInternationalPrefix(core: core)) {
- telecomManager.doCallOrJoinConf(address: address, isVideo: false)
- }
- } else {
- DispatchQueue.main.async {
- isShowSipAddressesPopupType = 0
- isShowSipAddressesPopup = true
- }
- }
- }
- }, label: {
- VStack {
- HStack(alignment: .center) {
- Image("phone")
- .renderingMode(.template)
- .resizable()
- .foregroundStyle(Color.grayMain2c600)
- .frame(width: 25, height: 25)
- }
- .padding(16)
- .background(Color.grayMain2c200)
- .cornerRadius(40)
+ VStack(spacing: 0) {
+ if SharedMainViewModel.shared.displayedFriend != nil {
+ Avatar(contactAvatarModel: contactAvatarModel, avatarSize: 100)
- Text("contact_call_action")
+ Text(contactAvatarModel.name)
+ .foregroundStyle(Color.grayMain2c700)
+ .multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
+ .frame(maxWidth: .infinity)
+ .padding(.top, 10)
+
+ Text(contactAvatarModel.lastPresenceInfo)
+ .foregroundStyle(contactAvatarModel.lastPresenceInfo == "Online"
+ ? Color.greenSuccess500
+ : Color.orangeWarning600)
+ .multilineTextAlignment(.center)
+ .default_text_style_300(styleSize: 12)
+ .frame(maxWidth: .infinity)
}
- })
-
- if !AppServices.corePreferences.disableChatFeature {
- Spacer()
-
- Button(action: {
- CoreContext.shared.doOnCoreQueue { core in
- if contactAvatarModel.addresses.count == 1 {
- do {
- let address = try Factory.Instance.createAddress(addr: contactAvatarModel.address)
- contactsListViewModel.createOneToOneChatRoomWith(remote: address)
- } catch {
- 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: LinphoneUtils.applyInternationalPrefix(core: core)) {
- contactsListViewModel.createOneToOneChatRoomWith(remote: address)
- }
- } else {
- DispatchQueue.main.async {
- isShowSipAddressesPopupType = 1
- isShowSipAddressesPopup = true
- }
- }
- }
- }, label: {
- VStack {
- HStack(alignment: .center) {
- Image("chat-teardrop-text")
- .renderingMode(.template)
- .resizable()
- .foregroundStyle(Color.grayMain2c600)
- .frame(width: 25, height: 25)
- }
- .padding(16)
- .background(Color.grayMain2c200)
- .cornerRadius(40)
-
- Text("contact_message_action")
- .default_text_style(styleSize: 14)
- }
- })
- }
-
- Spacer()
+ }
+ .frame(minHeight: 150)
+ .frame(maxWidth: .infinity)
+ .padding(.top, 10)
+ .background(Color.gray100)
- if !SharedMainViewModel.shared.disableVideoCall {
+ HStack {
+ Spacer()
+
Button(action: {
CoreContext.shared.doOnCoreQueue { core in
if contactAvatarModel.addresses.count == 1 {
do {
let address = try Factory.Instance.createAddress(addr: contactAvatarModel.address)
- telecomManager.doCallOrJoinConf(address: address, isVideo: true)
+ telecomManager.doCallOrJoinConf(address: address, isVideo: false)
} catch {
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: LinphoneUtils.applyInternationalPrefix(core: core)) {
- telecomManager.doCallOrJoinConf(address: address, isVideo: true)
+ telecomManager.doCallOrJoinConf(address: address, isVideo: false)
}
} else {
DispatchQueue.main.async {
- isShowSipAddressesPopupType = 2
+ isShowSipAddressesPopupType = 0
isShowSipAddressesPopup = true
}
}
@@ -249,7 +169,7 @@ struct ContactInnerFragment: View {
}, label: {
VStack {
HStack(alignment: .center) {
- Image("video-camera")
+ Image("phone")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c600)
@@ -259,44 +179,134 @@ struct ContactInnerFragment: View {
.background(Color.grayMain2c200)
.cornerRadius(40)
- Text("contact_video_call_action")
+ Text("contact_call_action")
.default_text_style(styleSize: 14)
}
})
+ if !AppServices.corePreferences.disableChatFeature {
+ Spacer()
+
+ Button(action: {
+ CoreContext.shared.doOnCoreQueue { core in
+ if contactAvatarModel.addresses.count == 1 {
+ do {
+ let address = try Factory.Instance.createAddress(addr: contactAvatarModel.address)
+ contactsListViewModel.createOneToOneChatRoomWith(remote: address)
+ } catch {
+ 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: LinphoneUtils.applyInternationalPrefix(core: core)) {
+ contactsListViewModel.createOneToOneChatRoomWith(remote: address)
+ }
+ } else {
+ DispatchQueue.main.async {
+ isShowSipAddressesPopupType = 1
+ isShowSipAddressesPopup = true
+ }
+ }
+ }
+ }, label: {
+ VStack {
+ HStack(alignment: .center) {
+ Image("chat-teardrop-text")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.grayMain2c600)
+ .frame(width: 25, height: 25)
+ }
+ .padding(16)
+ .background(Color.grayMain2c200)
+ .cornerRadius(40)
+
+ Text("contact_message_action")
+ .default_text_style(styleSize: 14)
+ }
+ })
+ }
+
Spacer()
+
+ if !SharedMainViewModel.shared.disableVideoCall {
+ Button(action: {
+ CoreContext.shared.doOnCoreQueue { core in
+ if contactAvatarModel.addresses.count == 1 {
+ do {
+ let address = try Factory.Instance.createAddress(addr: contactAvatarModel.address)
+ telecomManager.doCallOrJoinConf(address: address, isVideo: true)
+ } catch {
+ 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: LinphoneUtils.applyInternationalPrefix(core: core)) {
+ telecomManager.doCallOrJoinConf(address: address, isVideo: true)
+ }
+ } else {
+ DispatchQueue.main.async {
+ isShowSipAddressesPopupType = 2
+ isShowSipAddressesPopup = true
+ }
+ }
+ }
+ }, label: {
+ VStack {
+ HStack(alignment: .center) {
+ Image("video-camera")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.grayMain2c600)
+ .frame(width: 25, height: 25)
+ }
+ .padding(16)
+ .background(Color.grayMain2c200)
+ .cornerRadius(40)
+
+ Text("contact_video_call_action")
+ .default_text_style(styleSize: 14)
+ }
+ })
+
+ Spacer()
+ }
+ }
+ .padding(.top, 20)
+ .frame(maxWidth: .infinity)
+ .background(Color.gray100)
+
+ ContactInnerActionsFragment(
+ showingSheet: $showingSheet,
+ showShareSheet: $showShareSheet,
+ isShowDeletePopup: $isShowDeletePopup,
+ isShowDismissPopup: $isShowDismissPopup,
+ isShowTrustLevelPopup: $isShowTrustLevelPopup,
+ isShowIncreaseTrustLevelPopup: $isShowIncreaseTrustLevelPopup,
+ isShowEditContactFragmentInContactDetails: $isShowEditContactFragmentInContactDetails,
+ geometry: geometry,
+ actionEditButton: editNativeContact
+ )
+ .onAppear {
+ contactsListViewModel.fetchDevicesAndTrust()
}
}
- .padding(.top, 20)
- .frame(maxWidth: .infinity)
- .background(Color.gray100)
-
- ContactInnerActionsFragment(
- showingSheet: $showingSheet,
- showShareSheet: $showShareSheet,
- isShowDeletePopup: $isShowDeletePopup,
- isShowDismissPopup: $isShowDismissPopup,
- isShowEditContactFragmentInContactDetails: $isShowEditContactFragmentInContactDetails,
- actionEditButton: editNativeContact
- )
+ .frame(maxWidth: SharedMainViewModel.shared.maxWidth)
}
- .frame(maxWidth: SharedMainViewModel.shared.maxWidth)
+ .frame(maxWidth: .infinity)
}
- .frame(maxWidth: .infinity)
+ .background(Color.gray100)
}
- .background(Color.gray100)
- }
- .background(.white)
- .navigationBarHidden(true)
- .onRotate { newOrientation in
- orientation = newOrientation
- }
- .fullScreenCover(isPresented: $presentingEditContact) {
- NavigationView {
- EditContactView(contact: $cnContact)
- .navigationBarTitle("contact_edit_title")
- .navigationBarTitleDisplayMode(.inline)
- .edgesIgnoringSafeArea(.vertical)
+ .background(.white)
+ .navigationBarHidden(true)
+ .onRotate { newOrientation in
+ orientation = newOrientation
+ }
+ .fullScreenCover(isPresented: $presentingEditContact) {
+ NavigationView {
+ EditContactView(contact: $cnContact)
+ .navigationBarTitle("contact_edit_title")
+ .navigationBarTitleDisplayMode(.inline)
+ .edgesIgnoringSafeArea(.vertical)
+ }
}
}
}
@@ -321,15 +331,3 @@ struct ContactInnerFragment: View {
}
}
}
-
-#Preview {
- ContactInnerFragment(
- isShowDeletePopup: .constant(false),
- showingSheet: .constant(false),
- showShareSheet: .constant(false),
- isShowDismissPopup: .constant(false),
- isShowSipAddressesPopup: .constant(false),
- isShowSipAddressesPopupType: .constant(0),
- isShowEditContactFragmentInContactDetails: .constant(false)
- )
-}
diff --git a/Linphone/UI/Main/Contacts/Model/ContactDeviceModel.swift b/Linphone/UI/Main/Contacts/Model/ContactDeviceModel.swift
new file mode 100644
index 000000000..29c40a4e7
--- /dev/null
+++ b/Linphone/UI/Main/Contacts/Model/ContactDeviceModel.swift
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2010-2023 Belledonne Communications SARL.
+ *
+ * This file is part of Linphone
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+import Foundation
+import linphonesw
+
+class ContactDeviceModel: ObservableObject, Identifiable {
+ let id = UUID()
+ var name: String
+ var address: Address
+ var trusted: Bool
+
+ init(name: String, address: Address, trusted: Bool) {
+ self.name = name
+ self.address = address
+ self.trusted = trusted
+ }
+}
diff --git a/Linphone/UI/Main/Contacts/ViewModel/ContactsListViewModel.swift b/Linphone/UI/Main/Contacts/ViewModel/ContactsListViewModel.swift
index fa7a74983..c23f4b178 100644
--- a/Linphone/UI/Main/Contacts/ViewModel/ContactsListViewModel.swift
+++ b/Linphone/UI/Main/Contacts/ViewModel/ContactsListViewModel.swift
@@ -23,6 +23,8 @@ import SwiftUI
// swiftlint:disable line_length
class ContactsListViewModel: ObservableObject {
+ static let TAG = "[ConversationForwardMessageViewModel]"
+
@Published var selectedEditFriend: ContactAvatarModel?
var stringToCopy: String = ""
@@ -31,22 +33,28 @@ class ContactsListViewModel: ObservableObject {
var selectedFriendToShare: ContactAvatarModel?
var selectedFriendToDelete: ContactAvatarModel?
+ @Published var devices: [ContactDeviceModel] = []
+ @Published var trustedDevicesPercentage: Double = 0.0
+
@Published var displayedConversation: ConversationModel?
+ private var coreDelegate: CoreDelegate?
private var contactChatRoomDelegate: ChatRoomDelegate?
private let nativeAddressBookFriendList = "Native address-book"
let linphoneAddressBookFriendList = "Linphone address-book"
let tempRemoteAddressBookFriendList = "TempRemoteDirectoryContacts address-book"
- init() {}
+ init() {
+ addCoreDelegate()
+ }
func createOneToOneChatRoomWith(remote: Address) {
CoreContext.shared.doOnCoreQueue { core in
let account = core.defaultAccount
if account == nil {
Log.error(
- "\(ConversationForwardMessageViewModel.TAG) No default account found, can't create conversation with \(remote.asStringUriOnly())"
+ "\(Self.TAG) No default account found, can't create conversation with \(remote.asStringUriOnly())"
)
return
}
@@ -211,6 +219,30 @@ class ContactsListViewModel: ObservableObject {
chatRoom.addDelegate(delegate: contactChatRoomDelegate!)
}
+ func addCoreDelegate() {
+ CoreContext.shared.doOnCoreQueue { core in
+ if let coreDelegate = self.coreDelegate {
+ core.removeDelegate(delegate: coreDelegate)
+ self.coreDelegate = nil
+ }
+
+ self.coreDelegate = CoreDelegateStub(
+ onCallStateChanged: { (core: Core, call: Call, state: Call.State, message: String) in
+ if call.state == Call.State.End && SharedMainViewModel.shared.displayedFriend != nil {
+ // Updates trust if need be
+ DispatchQueue.main.async {
+ self.fetchDevicesAndTrust()
+ }
+ }
+ }
+ )
+
+ if self.coreDelegate != nil {
+ core.addDelegate(delegate: self.coreDelegate!)
+ }
+ }
+ }
+
func deleteSelectedContact() {
CoreContext.shared.doOnCoreQueue { core in
if self.selectedFriendToDelete != nil && self.selectedFriendToDelete!.friend != nil {
@@ -266,5 +298,44 @@ class ContactsListViewModel: ObservableObject {
}
}
}
+
+ func fetchDevicesAndTrust() {
+ if let friend = SharedMainViewModel.shared.displayedFriend?.friend {
+ var devicesList: [ContactDeviceModel] = []
+
+ let friendDevices = friend.devices
+ if friendDevices.isEmpty {
+ Log.info("\(Self.TAG) No device found for friend [\(friend.name ?? "")]")
+ } else {
+ let devicesCount = friendDevices.count
+ var trustedDevicesCount = 0
+
+ for device in friendDevices {
+ let trusted = device.securityLevel == .EndToEndEncryptedAndVerified
+
+ if let address = device.address {
+ devicesList.append(
+ ContactDeviceModel(
+ name: device.displayName ?? NSLocalizedString("contact_device_without_name", comment: ""),
+ address: address,
+ trusted: trusted
+ )
+ )
+ }
+
+ if trusted {
+ trustedDevicesCount += 1
+ }
+ }
+
+ if !devicesList.isEmpty {
+ let percentage = trustedDevicesCount * 100 / devicesCount
+ trustedDevicesPercentage = Double(percentage)
+ }
+ }
+
+ devices = devicesList
+ }
+ }
}
// swiftlint:enable line_length
diff --git a/Linphone/UI/Main/ContentView.swift b/Linphone/UI/Main/ContentView.swift
index f65fc6f4b..c4f01de7b 100644
--- a/Linphone/UI/Main/ContentView.swift
+++ b/Linphone/UI/Main/ContentView.swift
@@ -59,6 +59,9 @@ struct ContentView: View {
@State var isShowStartConversationFragment = false
@State var isShowDismissPopup = false
@State var isShowSendCancelMeetingNotificationPopup = false
+ @State var isShowTrustLevelPopup = false
+ @State var isShowIncreaseTrustLevelPopup = false
+ @State var increaseTrustLevelPopupAcceptedTmp = false
@State var isShowStartCallGroupPopup = false
@State var isShowDeleteMessagePopup = false
@State var isShowSipAddressesPopup = false
@@ -1013,8 +1016,10 @@ struct ContentView: View {
ContactFragment(
isShowDeletePopup: $isShowDeleteContactPopup,
isShowDismissPopup: $isShowDismissPopup,
+ isShowTrustLevelPopup: $isShowTrustLevelPopup,
isShowSipAddressesPopup: $isShowSipAddressesPopup,
isShowSipAddressesPopupType: $isShowSipAddressesPopupType,
+ isShowIncreaseTrustLevelPopup: $isShowIncreaseTrustLevelPopup,
isShowEditContactFragmentInContactDetails: $isShowEditContactFragmentInContactDetails
)
.environmentObject(contactsListVM)
@@ -1389,6 +1394,131 @@ struct ContentView: View {
}
}
+ if isShowTrustLevelPopup {
+ if let displayedFriend = sharedMainViewModel.displayedFriend {
+ PopupView(
+ isShowPopup: $isShowTrustLevelPopup,
+ title: Text("contact_dialog_devices_trust_help_title"),
+ content: Text("contact_dialog_devices_trust_help_message"),
+ additionalContent: {
+ HStack {
+ let avatarSize = 50.0
+ let avatar = Avatar(contactAvatarModel: displayedFriend, avatarSize: avatarSize, hidePresence: true)
+
+ avatar
+
+ Image("arrow-right")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.grayMain2c600)
+ .frame(width: 25, height: 25, alignment: .leading)
+ .padding(.all, 10)
+
+ ZStack {
+ avatar
+
+ Circle()
+ .stroke(Color.blueInfo500, lineWidth: 2)
+ .frame(width: avatarSize, height: avatarSize)
+
+ HStack {
+ VStack {
+ Spacer()
+ Image("trusted")
+ .resizable()
+ .frame(width: avatarSize/4, height: avatarSize/4)
+ .padding(.trailing, 1)
+ .padding(.bottom, 1)
+ }
+ Spacer()
+ }
+ .frame(width: avatarSize, height: avatarSize)
+ }
+ }
+ .frame(maxWidth: .infinity)
+ .padding(.bottom, 10)
+ },
+ titleFirstButton: nil,
+ actionFirstButton: {},
+ titleSecondButton: Text("dialog_understood"),
+ actionSecondButton: { self.isShowTrustLevelPopup.toggle() },
+ titleThirdButton: nil,
+ actionThirdButton: {}
+ )
+ .background(.black.opacity(0.65))
+ .zIndex(3)
+ .onTapGesture {
+ self.isShowTrustLevelPopup.toggle()
+ }
+ }
+ }
+
+ if isShowIncreaseTrustLevelPopup {
+ if let displayedFriend = sharedMainViewModel.displayedFriend {
+ PopupView(
+ isShowPopup: $isShowIncreaseTrustLevelPopup,
+ title: Text("contact_dialog_increase_trust_level_title"),
+ content: Text(String(format: String(localized: "contact_dialog_increase_trust_level_message"), displayedFriend.name, SharedMainViewModel.shared.increaseTrustLevelPopupDeviceName)),
+ additionalContent: {
+ if !SharedMainViewModel.shared.increaseTrustLevelPopupAccepted {
+ HStack {
+ Button(action: {
+ increaseTrustLevelPopupAcceptedTmp.toggle()
+ }, label: {
+ HStack {
+ Image(systemName: increaseTrustLevelPopupAcceptedTmp ? "checkmark.square.fill" : "square")
+ .renderingMode(.template)
+ .resizable()
+ .foregroundStyle(Color.orangeMain500)
+ .frame(width: 20, height: 20)
+ }
+ })
+ .buttonStyle(PlainButtonStyle())
+
+ Text("dialog_do_not_show_anymore")
+ .tint(Color.grayMain2c600)
+ .default_text_style(styleSize: 15)
+ }
+ .padding(.bottom, 10)
+ }
+ },
+ titleFirstButton: nil,
+ actionFirstButton: {},
+ titleSecondButton: Text("contact_call_action"),
+ actionSecondButton: {
+ if increaseTrustLevelPopupAcceptedTmp == true {
+ SharedMainViewModel.shared.changeIncreaseTrustLevelPopupAccepted()
+ }
+
+ if let deviceAddress = SharedMainViewModel.shared.increaseTrustLevelPopupDeviceAddress {
+ TelecomManager.shared.doCallOrJoinConf(address: deviceAddress)
+ }
+
+ self.isShowIncreaseTrustLevelPopup.toggle()
+ self.increaseTrustLevelPopupAcceptedTmp = false
+ SharedMainViewModel.shared.increaseTrustLevelPopupDeviceName = ""
+ SharedMainViewModel.shared.increaseTrustLevelPopupDeviceAddress = nil
+ },
+ titleThirdButton: Text("dialog_cancel"),
+ actionThirdButton: {
+ self.isShowIncreaseTrustLevelPopup.toggle()
+ self.increaseTrustLevelPopupAcceptedTmp = false
+ SharedMainViewModel.shared.increaseTrustLevelPopupDeviceName = ""
+ SharedMainViewModel.shared.increaseTrustLevelPopupDeviceAddress = nil
+
+ }
+ )
+ .background(.black.opacity(0.65))
+ .zIndex(3)
+ .onTapGesture {
+ self.isShowIncreaseTrustLevelPopup.toggle()
+ self.increaseTrustLevelPopupAcceptedTmp = false
+ SharedMainViewModel.shared.increaseTrustLevelPopupDeviceName = ""
+ SharedMainViewModel.shared.increaseTrustLevelPopupDeviceAddress = nil
+ }
+ }
+ }
+
if isShowStartCallGroupPopup {
PopupView(
isShowPopup: $isShowStartCallGroupPopup,
diff --git a/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift b/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift
index 60d56d0b7..5fbd97a99 100644
--- a/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift
+++ b/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift
@@ -885,7 +885,7 @@ struct ChatBubbleView: View {
}
}
}
- .frame(width: geometryProxy.size.width - 150)
+ .frame(width: max(0, geometryProxy.size.width - 150))
}
}
diff --git a/Linphone/UI/Main/Fragments/PopupView.swift b/Linphone/UI/Main/Fragments/PopupView.swift
index 784ed58ef..c80c57c62 100644
--- a/Linphone/UI/Main/Fragments/PopupView.swift
+++ b/Linphone/UI/Main/Fragments/PopupView.swift
@@ -20,7 +20,7 @@
import SwiftUI
import Photos
-struct PopupView: View {
+struct PopupView: View {
var permissionManager = PermissionManager.shared
@@ -28,6 +28,8 @@ struct PopupView: View {
var title: Text
var content: Text?
+ private let additionalContent: AdditionalContent
+
var titleFirstButton: Text?
var actionFirstButton: () -> Void
@@ -37,6 +39,30 @@ struct PopupView: View {
var titleThirdButton: Text?
var actionThirdButton: () -> Void
+ init(
+ isShowPopup: Binding,
+ title: Text,
+ content: Text? = nil,
+ @ViewBuilder additionalContent: () -> AdditionalContent = { EmptyView() },
+ titleFirstButton: Text? = nil,
+ actionFirstButton: @escaping () -> Void = {},
+ titleSecondButton: Text? = nil,
+ actionSecondButton: @escaping () -> Void = {},
+ titleThirdButton: Text? = nil,
+ actionThirdButton: @escaping () -> Void = {}
+ ) {
+ self._isShowPopup = isShowPopup
+ self.title = title
+ self.content = content
+ self.additionalContent = additionalContent()
+ self.titleFirstButton = titleFirstButton
+ self.actionFirstButton = actionFirstButton
+ self.titleSecondButton = titleSecondButton
+ self.actionSecondButton = actionSecondButton
+ self.titleThirdButton = titleThirdButton
+ self.actionThirdButton = actionThirdButton
+ }
+
var body: some View {
GeometryReader { geometry in
VStack(alignment: .leading) {
@@ -52,6 +78,8 @@ struct PopupView: View {
.padding(.bottom, 20)
}
+ additionalContent
+
HStack {
if titleFirstButton != nil {
Button(action: {
@@ -116,16 +144,3 @@ struct PopupView: View {
}
}
}
-
-#Preview {
- PopupView(isShowPopup: .constant(true),
- title: Text("Title"),
- content: Text("Content"),
- titleFirstButton: Text("Accept all"),
- actionFirstButton: {},
- titleSecondButton: Text("dialog_confirm"),
- actionSecondButton: {},
- titleThirdButton: Text("dialog_cancel"),
- actionThirdButton: {})
- .background(.black.opacity(0.65))
-}
diff --git a/Linphone/UI/Main/Viewmodel/SharedMainViewModel.swift b/Linphone/UI/Main/Viewmodel/SharedMainViewModel.swift
index 793da071d..a84ac2804 100644
--- a/Linphone/UI/Main/Viewmodel/SharedMainViewModel.swift
+++ b/Linphone/UI/Main/Viewmodel/SharedMainViewModel.swift
@@ -36,6 +36,9 @@ class SharedMainViewModel: ObservableObject {
@Published var displayProfileMode = false
@Published var defaultAvatar: URL?
@Published var indexView: Int = 0
+ @Published var increaseTrustLevelPopupAccepted = false
+ @Published var increaseTrustLevelPopupDeviceName = ""
+ @Published var increaseTrustLevelPopupDeviceAddress: Address?
@Published var displayedFriend: ContactAvatarModel?
@Published var displayedCall: HistoryModel?
@@ -64,6 +67,7 @@ class SharedMainViewModel: ObservableObject {
let displayProfileModeKey = "display_profile_mode"
let defaultAvatarKey = "default_avatar"
let indexViewKey = "index_view"
+ let increaseTrustLevelKey = "increase_trust_level"
var maxWidth = 600.0
@@ -101,6 +105,12 @@ class SharedMainViewModel: ObservableObject {
defaultAvatar = defaultAvatarTmp
}
}
+
+ if preferences.object(forKey: increaseTrustLevelKey) == nil {
+ preferences.set(increaseTrustLevelPopupAccepted, forKey: increaseTrustLevelKey)
+ } else {
+ increaseTrustLevelPopupAccepted = preferences.bool(forKey: increaseTrustLevelKey)
+ }
updateMissedCallsCount()
updateDisableVideoCall()
@@ -146,6 +156,14 @@ class SharedMainViewModel: ObservableObject {
preferences.set(defaultAvatar, forKey: defaultAvatarKey)
}
+
+ func changeIncreaseTrustLevelPopupAccepted() {
+ let preferences = UserDefaults.standard
+
+ increaseTrustLevelPopupAccepted = true
+ preferences.set(increaseTrustLevelPopupAccepted, forKey: increaseTrustLevelKey)
+ }
+
func changeIndexView(indexViewInt: Int) {
let preferences = UserDefaults.standard
diff --git a/LinphoneApp.xcodeproj/project.pbxproj b/LinphoneApp.xcodeproj/project.pbxproj
index f59a49f66..3b8ee53a7 100644
--- a/LinphoneApp.xcodeproj/project.pbxproj
+++ b/LinphoneApp.xcodeproj/project.pbxproj
@@ -168,6 +168,7 @@
D7AEB9762F39E2A400298546 /* ConversationMediaListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AEB9752F39E2A300298546 /* ConversationMediaListViewModel.swift */; };
D7AEB9782F39E2C300298546 /* ConversationDocumentsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AEB9772F39E2C100298546 /* ConversationDocumentsListViewModel.swift */; };
D7AEB97A2F39E83600298546 /* FileModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AEB9792F39E83500298546 /* FileModel.swift */; };
+ D7AEB9A02F3E219600298546 /* ContactDeviceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AEB99F2F3E218A00298546 /* ContactDeviceModel.swift */; };
D7B5066D2AEFA9B900CEB4E9 /* ContactInnerFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B5066C2AEFA9B900CEB4E9 /* ContactInnerFragment.swift */; };
D7B5678E2B28888F00DE63EB /* CallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B5678D2B28888F00DE63EB /* CallView.swift */; };
D7B99E992B29B39000BE7BF2 /* CallViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B99E982B29B39000BE7BF2 /* CallViewModel.swift */; };
@@ -433,6 +434,7 @@
D7AEB9752F39E2A300298546 /* ConversationMediaListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMediaListViewModel.swift; sourceTree = ""; };
D7AEB9772F39E2C100298546 /* ConversationDocumentsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationDocumentsListViewModel.swift; sourceTree = ""; };
D7AEB9792F39E83500298546 /* FileModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileModel.swift; sourceTree = ""; };
+ D7AEB99F2F3E218A00298546 /* ContactDeviceModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDeviceModel.swift; sourceTree = ""; };
D7B5066C2AEFA9B900CEB4E9 /* ContactInnerFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactInnerFragment.swift; sourceTree = ""; };
D7B5678D2B28888F00DE63EB /* CallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallView.swift; sourceTree = ""; };
D7B99E982B29B39000BE7BF2 /* CallViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewModel.swift; sourceTree = ""; };
@@ -822,6 +824,7 @@
D726E4372B1643FF0083C415 /* Model */ = {
isa = PBXGroup;
children = (
+ D7AEB99F2F3E218A00298546 /* ContactDeviceModel.swift */,
D726E4382B16440C0083C415 /* ContactAvatarModel.swift */,
);
path = Model;
@@ -1601,6 +1604,7 @@
D74DA0122C047F0700A8561D /* HistoryModel.swift in Sources */,
66D382052CEB7E0A0063E1C5 /* ShortcutModel.swift in Sources */,
D72250692ADFBF2D008FB426 /* SideMenu.swift in Sources */,
+ D7AEB9A02F3E219600298546 /* ContactDeviceModel.swift in Sources */,
D703F7082DC8C605005B8F75 /* FilePicker.swift in Sources */,
C6DC4E3F2C19C289009096FD /* SideMenuEntry.swift in Sources */,
D714DE622C1C4636006C1F1D /* RegisterCodeConfirmationFragment.swift in Sources */,