/* * Copyright (c) 2010-2023 Belledonne Communications SARL. * * This file is part of linphone-iphone * * 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 SwiftUI import Contacts import ContactsUI import linphonesw struct ContactInnerFragment: View { @ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared @ObservedObject var contactsManager = ContactsManager.shared @ObservedObject private var telecomManager = TelecomManager.shared @ObservedObject var contactAvatarModel: ContactAvatarModel @ObservedObject var contactViewModel: ContactViewModel @ObservedObject var editContactViewModel: EditContactViewModel @ObservedObject var conversationViewModel: ConversationViewModel @State private var orientation = UIDevice.current.orientation @State var cnContact: CNContact? @State private var presentingEditContact = false @Binding var isShowDeletePopup: Bool @Binding var showingSheet: Bool @Binding var showShareSheet: Bool @Binding var isShowDismissPopup: Bool @Binding var isShowSipAddressesPopup: Bool @Binding var isShowSipAddressesPopupType: Int 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 { contactViewModel.indexDisplayedFriend = nil } } } Spacer() if !contactAvatarModel.nativeUri.isEmpty { 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( editContactViewModel: editContactViewModel, contactViewModel: contactViewModel, isShowEditContactFragment: .constant(false), 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 { editContactViewModel.selectedEditFriend = contactAvatarModel.friend editContactViewModel.resetValues() } ) } } .frame(maxWidth: .infinity) .frame(height: 50) .padding(.horizontal) .padding(.bottom, 4) .background(.white) ScrollView { VStack(spacing: 0) { VStack(spacing: 0) { VStack(spacing: 0) { if contactViewModel.indexDisplayedFriend != 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: { 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 { isShowSipAddressesPopupType = 0 isShowSipAddressesPopup = true } }, label: { VStack { HStack(alignment: .center) { Image("phone") .renderingMode(.template) .resizable() .foregroundStyle(contactAvatarModel.address.isEmpty ? Color.grayMain2c400 : Color.grayMain2c600) .frame(width: 25, height: 25) } .padding(16) .background(contactAvatarModel.address.isEmpty ? Color.grayMain2c100 : Color.grayMain2c200) .cornerRadius(40) Text("contact_call_action") .default_text_style(styleSize: 14) } }) .disabled(contactAvatarModel.address.isEmpty) Spacer() Button(action: { if contactAvatarModel.addresses.count <= 1 { do { let address = try Factory.Instance.createAddress(addr: contactAvatarModel.address) contactViewModel.createOneToOneChatRoomWith(remote: address) } catch { Log.error("[ContactInnerFragment] unable to create address for a new outgoing call : \(contactAvatarModel.address) \(error) ") } } else { isShowSipAddressesPopupType = 1 isShowSipAddressesPopup = true } }, label: { VStack { HStack(alignment: .center) { Image("chat-teardrop-text") .renderingMode(.template) .resizable() .foregroundStyle(contactAvatarModel.address.isEmpty ? Color.grayMain2c400 : Color.grayMain2c600) .frame(width: 25, height: 25) } .padding(16) .background(contactAvatarModel.address.isEmpty ? Color.grayMain2c100 : Color.grayMain2c200) .cornerRadius(40) Text("contact_message_action") .default_text_style(styleSize: 14) } }) .disabled(contactAvatarModel.address.isEmpty) Spacer() Button(action: { 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 { isShowSipAddressesPopupType = 2 isShowSipAddressesPopup = true } }, label: { VStack { HStack(alignment: .center) { Image("video-camera") .renderingMode(.template) .resizable() .foregroundStyle(contactAvatarModel.address.isEmpty ? Color.grayMain2c400 : Color.grayMain2c600) .frame(width: 25, height: 25) } .padding(16) .background(contactAvatarModel.address.isEmpty ? Color.grayMain2c100 : Color.grayMain2c200) .cornerRadius(40) Text("contact_video_call_action") .default_text_style(styleSize: 14) } }) .disabled(contactAvatarModel.address.isEmpty) Spacer() } .padding(.top, 20) .frame(maxWidth: .infinity) .background(Color.gray100) ContactInnerActionsFragment( contactViewModel: contactViewModel, editContactViewModel: editContactViewModel, contactAvatarModel: contactAvatarModel, showingSheet: $showingSheet, showShareSheet: $showShareSheet, isShowDeletePopup: $isShowDeletePopup, isShowDismissPopup: $isShowDismissPopup, actionEditButton: editNativeContact ) } .frame(maxWidth: sharedMainViewModel.maxWidth) } .frame(maxWidth: .infinity) } .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) } } } } .navigationViewStyle(.stack) } func editNativeContact() { do { let store = CNContactStore() let descriptor = CNContactViewController.descriptorForRequiredKeys() cnContact = try store.unifiedContact( withIdentifier: contactAvatarModel.nativeUri, keysToFetch: [descriptor] ) if cnContact != nil { presentingEditContact.toggle() } } catch { print(error) } } } #Preview { ContactInnerFragment( contactAvatarModel: ContactAvatarModel(friend: nil, name: "", address: "", withPresence: true), contactViewModel: ContactViewModel(), editContactViewModel: EditContactViewModel(), conversationViewModel: ConversationViewModel(), isShowDeletePopup: .constant(false), showingSheet: .constant(false), showShareSheet: .constant(false), isShowDismissPopup: .constant(false), isShowSipAddressesPopup: .constant(false), isShowSipAddressesPopupType: .constant(0) ) }