mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 11:08:06 +00:00
Changes ContactsManager
This commit is contained in:
parent
9ef96bbd78
commit
219ee2d438
10 changed files with 643 additions and 617 deletions
21
Linphone/Assets.xcassets/profil-picture-default.imageset/Contents.json
vendored
Normal file
21
Linphone/Assets.xcassets/profil-picture-default.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "profil-picture-default.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
18
Linphone/Assets.xcassets/profil-picture-default.imageset/profil-picture-default.svg
vendored
Normal file
18
Linphone/Assets.xcassets/profil-picture-default.imageset/profil-picture-default.svg
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d_3627_17791)">
|
||||
<rect x="0" y="0" width="120" height="120" rx="60" fill="#DFECF2"/>
|
||||
<path d="M59.9666 26.667C41.5666 26.667 26.6666 41.6003 26.6666 60.0003C26.6666 78.4003 41.5666 93.3337 59.9666 93.3337C78.4 93.3337 93.3333 78.4003 93.3333 60.0003C93.3333 41.6003 78.4 26.667 59.9666 26.667ZM60 86.667C45.2666 86.667 33.3333 74.7337 33.3333 60.0003C33.3333 45.267 45.2666 33.3337 60 33.3337C74.7333 33.3337 86.6666 45.267 86.6666 60.0003C86.6666 74.7337 74.7333 86.667 60 86.667ZM71.6666 56.667C74.4333 56.667 76.6666 54.4337 76.6666 51.667C76.6666 48.9003 74.4333 46.667 71.6666 46.667C68.9 46.667 66.6666 48.9003 66.6666 51.667C66.6666 54.4337 68.9 56.667 71.6666 56.667ZM48.3333 56.667C51.1 56.667 53.3333 54.4337 53.3333 51.667C53.3333 48.9003 51.1 46.667 48.3333 46.667C45.5666 46.667 43.3333 48.9003 43.3333 51.667C43.3333 54.4337 45.5666 56.667 48.3333 56.667ZM60 78.3337C67.7666 78.3337 74.3666 73.467 77.0333 66.667H42.9666C45.6333 73.467 52.2333 78.3337 60 78.3337Z" fill="#4E6074"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_3627_17791" x="0" y="0" width="120" height="120" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="5"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3627_17791"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3627_17791" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -24,213 +24,198 @@ import SwiftUI
|
|||
final class ContactsManager: ObservableObject {
|
||||
|
||||
static let shared = ContactsManager()
|
||||
|
||||
|
||||
private var coreContext = CoreContext.shared
|
||||
|
||||
@Published var contacts: [Contact] = []
|
||||
private var magicSearch = MagicSearchSingleton.shared
|
||||
|
||||
private let nativeAddressBookFriendList = "Native address-book"
|
||||
let linphoneAddressBookFirendList = "Linphone address-book"
|
||||
|
||||
@Published var friendList: FriendList?
|
||||
|
||||
private init() {
|
||||
fetchContacts()
|
||||
}
|
||||
|
||||
func fetchContacts() {
|
||||
contacts.removeAll()
|
||||
DispatchQueue.global().async {
|
||||
let store = CNContactStore()
|
||||
store.requestAccess(for: .contacts) { (granted, error) in
|
||||
if let error = error {
|
||||
print("failed to request access", error)
|
||||
return
|
||||
}
|
||||
if granted {
|
||||
let keys = [CNContactEmailAddressesKey, CNContactPhoneNumbersKey,
|
||||
CNContactFamilyNameKey, CNContactGivenNameKey, CNContactNicknameKey,
|
||||
CNContactPostalAddressesKey, CNContactIdentifierKey,
|
||||
CNInstantMessageAddressUsernameKey, CNContactInstantMessageAddressesKey,
|
||||
CNContactImageDataKey, CNContactThumbnailImageDataKey, CNContactOrganizationNameKey]
|
||||
let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
|
||||
do {
|
||||
try store.enumerateContacts(with: request, usingBlock: { (contact, _) in
|
||||
|
||||
DispatchQueue.main.sync {
|
||||
self.contacts.append(
|
||||
Contact(
|
||||
firstName: contact.givenName,
|
||||
lastName: contact.familyName,
|
||||
organizationName: contact.organizationName,
|
||||
displayName: contact.nickname,
|
||||
sipAddresses: contact.instantMessageAddresses.map { $0.value.service == "SIP" ? $0.value.username : "" },
|
||||
phoneNumbers: contact.phoneNumbers.map { PhoneNumber(numLabel: $0.label ?? "", num: $0.value.stringValue)},
|
||||
imageData: self.saveImage(
|
||||
image:
|
||||
UIImage(data: contact.thumbnailImageData ?? Data())
|
||||
?? self.textToImage(firstName: contact.givenName.isEmpty
|
||||
&& contact.familyName.isEmpty
|
||||
&& contact.phoneNumbers.first?.value.stringValue != nil
|
||||
? contact.phoneNumbers.first!.value.stringValue
|
||||
: contact.givenName, lastName: contact.familyName),
|
||||
name: contact.identifier)
|
||||
)
|
||||
)
|
||||
}
|
||||
self.contacts.sort(by: {
|
||||
$0.firstName.folding(
|
||||
options: .diacriticInsensitive, locale: .current
|
||||
) < $1.firstName.folding(
|
||||
options: .diacriticInsensitive, locale: .current
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
} catch let error {
|
||||
print("Failed to enumerate contact", error)
|
||||
}
|
||||
|
||||
} else {
|
||||
print("access denied")
|
||||
}
|
||||
}
|
||||
|
||||
var friends: [Friend] = []
|
||||
|
||||
self.contacts.forEach { contact in
|
||||
do {
|
||||
let friend = try self.coreContext.mCore.createFriend()
|
||||
friend.edit()
|
||||
try friend.setName(newValue: contact.firstName + " " + contact.lastName)
|
||||
friend.organization = contact.organizationName
|
||||
|
||||
var friendAddresses: [Address] = []
|
||||
contact.sipAddresses.forEach { sipAddress in
|
||||
let address = self.coreContext.mCore.interpretUrl(url: sipAddress, applyInternationalPrefix: true)
|
||||
|
||||
if address != nil && ((friendAddresses.firstIndex(where: {$0.asString() == address?.asString()})) == nil) {
|
||||
friend.addAddress(address: address!)
|
||||
friendAddresses.append(address!)
|
||||
}
|
||||
}
|
||||
|
||||
var friendPhoneNumbers: [PhoneNumber] = []
|
||||
contact.phoneNumbers.forEach { phone in
|
||||
do {
|
||||
if (friendPhoneNumbers.firstIndex(where: {$0.numLabel == phone.numLabel})) == nil {
|
||||
let phoneNumber = try Factory.Instance.createFriendPhoneNumber(phoneNumber: phone.num, label: phone.numLabel)
|
||||
friend.addPhoneNumberWithLabel(phoneNumber: phoneNumber)
|
||||
friendPhoneNumbers.append(phone)
|
||||
}
|
||||
} catch let error {
|
||||
print("Failed to enumerate contact", error)
|
||||
}
|
||||
}
|
||||
|
||||
let contactImage = contact.imageData.dropFirst(8)
|
||||
friend.photo = "file:/" + contactImage
|
||||
|
||||
friend.done()
|
||||
friends.append(friend)
|
||||
|
||||
} catch let error {
|
||||
print("Failed to enumerate contact", error)
|
||||
}
|
||||
}
|
||||
|
||||
if self.coreContext.mCore.globalState == GlobalState.Shutdown || self.coreContext.mCore.globalState == GlobalState.Off {
|
||||
print("$TAG Core is being stopped or already destroyed, abort")
|
||||
} else if friends.isEmpty {
|
||||
print("$TAG No friend created!")
|
||||
} else {
|
||||
} else {
|
||||
print("$TAG ${friends.size} friends created")
|
||||
|
||||
let fetchedFriends = friends
|
||||
|
||||
let nativeFriendList = self.coreContext.mCore.getFriendListByName(name: self.nativeAddressBookFriendList)
|
||||
var friendList = nativeFriendList
|
||||
if friendList == nil {
|
||||
self.friendList = self.coreContext.mCore.getFriendListByName(name: self.nativeAddressBookFriendList)
|
||||
if self.friendList == nil {
|
||||
do {
|
||||
friendList = try self.coreContext.mCore.createFriendList()
|
||||
self.friendList = try self.coreContext.mCore.createFriendList()
|
||||
} catch let error {
|
||||
print("Failed to enumerate contact", error)
|
||||
}
|
||||
}
|
||||
|
||||
if friendList!.displayName == nil || friendList!.displayName!.isEmpty {
|
||||
if self.friendList!.displayName == nil || self.friendList!.displayName!.isEmpty {
|
||||
print(
|
||||
"$TAG Friend list [$nativeAddressBookFriendList] didn't exist yet, let's create it"
|
||||
)
|
||||
|
||||
friendList?.databaseStorageEnabled = false // We don't want to store local address-book in DB
|
||||
self.friendList!.databaseStorageEnabled = false // We don't want to store local address-book in DB
|
||||
|
||||
friendList!.displayName = self.nativeAddressBookFriendList
|
||||
self.coreContext.mCore.addFriendList(list: friendList!)
|
||||
self.friendList!.displayName = self.nativeAddressBookFriendList
|
||||
self.coreContext.mCore.addFriendList(list: self.friendList!)
|
||||
} else {
|
||||
print(
|
||||
"$TAG Friend list [$LINPHONE_ADDRESS_BOOK_FRIEND_LIST] found, removing existing friends if any"
|
||||
)
|
||||
friendList!.friends.forEach { friend in
|
||||
_ = friendList!.removeFriend(linphoneFriend: friend)
|
||||
self.friendList!.friends.forEach { friend in
|
||||
_ = self.friendList!.removeFriend(linphoneFriend: friend)
|
||||
}
|
||||
}
|
||||
|
||||
fetchedFriends.forEach { friend in
|
||||
_ = friendList!.addLocalFriend(linphoneFriend: friend)
|
||||
}
|
||||
|
||||
friends.removeAll()
|
||||
|
||||
print("$TAG Friends added")
|
||||
|
||||
friendList!.updateSubscriptions()
|
||||
print("$TAG Subscription(s) updated")
|
||||
}
|
||||
|
||||
let store = CNContactStore()
|
||||
store.requestAccess(for: .contacts) { (granted, error) in
|
||||
if let error = error {
|
||||
print("failed to request access", error)
|
||||
return
|
||||
}
|
||||
if granted {
|
||||
let keys = [CNContactEmailAddressesKey, CNContactPhoneNumbersKey,
|
||||
CNContactFamilyNameKey, CNContactGivenNameKey, CNContactNicknameKey,
|
||||
CNContactPostalAddressesKey, CNContactIdentifierKey,
|
||||
CNInstantMessageAddressUsernameKey, CNContactInstantMessageAddressesKey,
|
||||
CNContactImageDataKey, CNContactThumbnailImageDataKey, CNContactOrganizationNameKey]
|
||||
let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
|
||||
do {
|
||||
try store.enumerateContacts(with: request, usingBlock: { (contact, _) in
|
||||
|
||||
DispatchQueue.main.sync {
|
||||
let newContact = Contact(
|
||||
firstName: contact.givenName,
|
||||
lastName: contact.familyName,
|
||||
organizationName: contact.organizationName,
|
||||
displayName: contact.nickname,
|
||||
sipAddresses: contact.instantMessageAddresses.map { $0.value.service == "SIP" ? $0.value.username : "" },
|
||||
phoneNumbers: contact.phoneNumbers.map { PhoneNumber(numLabel: $0.label ?? "", num: $0.value.stringValue)},
|
||||
imageData: ""
|
||||
)
|
||||
|
||||
self.saveImage(
|
||||
image:
|
||||
UIImage(data: contact.thumbnailImageData ?? Data())
|
||||
?? self.textToImage(
|
||||
firstName: contact.givenName.isEmpty
|
||||
&& contact.familyName.isEmpty
|
||||
&& contact.phoneNumbers.first?.value.stringValue != nil
|
||||
? contact.phoneNumbers.first!.value.stringValue
|
||||
: contact.givenName, lastName: contact.familyName),
|
||||
name: contact.givenName + contact.familyName + String(Int.random(in: 1...1000)),
|
||||
contact: newContact)
|
||||
}
|
||||
})
|
||||
|
||||
} catch let error {
|
||||
print("Failed to enumerate contact", error)
|
||||
}
|
||||
|
||||
} else {
|
||||
print("access denied")
|
||||
}
|
||||
}
|
||||
self.magicSearch.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
func textToImage(firstName: String, lastName: String) -> UIImage {
|
||||
let lblNameInitialize = UILabel()
|
||||
lblNameInitialize.frame.size = CGSize(width: 100.0, height: 100.0)
|
||||
lblNameInitialize.font = UIFont(name: "NotoSans-ExtraBold", size: 40)
|
||||
lblNameInitialize.textColor = UIColor(Color.grayMain2c600)
|
||||
|
||||
var textToDisplay = ""
|
||||
if firstName.first != nil {
|
||||
textToDisplay += String(firstName.first!)
|
||||
}
|
||||
if lastName.first != nil {
|
||||
textToDisplay += String(lastName.first!)
|
||||
}
|
||||
|
||||
lblNameInitialize.text = textToDisplay.uppercased()
|
||||
lblNameInitialize.textAlignment = .center
|
||||
lblNameInitialize.backgroundColor = UIColor(Color.grayMain2c200)
|
||||
lblNameInitialize.layer.cornerRadius = 10.0
|
||||
|
||||
var IBImgViewUserProfile = UIImage()
|
||||
UIGraphicsBeginImageContext(lblNameInitialize.frame.size)
|
||||
lblNameInitialize.layer.render(in: UIGraphicsGetCurrentContext()!)
|
||||
IBImgViewUserProfile = UIGraphicsGetImageFromCurrentImageContext()!
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
return IBImgViewUserProfile
|
||||
}
|
||||
|
||||
func saveImage(image: UIImage, name: String) -> String {
|
||||
func saveImage(image: UIImage, name: String, contact: Contact) {
|
||||
guard let data = image.jpegData(compressionQuality: 1) ?? image.pngData() else {
|
||||
return ""
|
||||
}
|
||||
let directory = FileManager.default.temporaryDirectory
|
||||
print("FileManagerFileManager \(directory.absoluteString)")
|
||||
do {
|
||||
try data.write(to: directory.appendingPathComponent(name + ".png"))
|
||||
return directory.appendingPathComponent(name + ".png").absoluteString
|
||||
} catch {
|
||||
print(error.localizedDescription)
|
||||
return ""
|
||||
return
|
||||
}
|
||||
|
||||
awaitDataWrite(data: data, name: name) { _, result in
|
||||
do {
|
||||
let friend = try self.coreContext.mCore.createFriend()
|
||||
friend.edit()
|
||||
try friend.setName(newValue: contact.firstName + " " + contact.lastName)
|
||||
friend.organization = contact.organizationName
|
||||
|
||||
var friendAddresses: [Address] = []
|
||||
contact.sipAddresses.forEach { sipAddress in
|
||||
let address = self.coreContext.mCore.interpretUrl(url: sipAddress, applyInternationalPrefix: true)
|
||||
|
||||
if address != nil && ((friendAddresses.firstIndex(where: {$0.asString() == address?.asString()})) == nil) {
|
||||
friend.addAddress(address: address!)
|
||||
friendAddresses.append(address!)
|
||||
}
|
||||
}
|
||||
|
||||
var friendPhoneNumbers: [PhoneNumber] = []
|
||||
contact.phoneNumbers.forEach { phone in
|
||||
do {
|
||||
if (friendPhoneNumbers.firstIndex(where: {$0.numLabel == phone.numLabel})) == nil {
|
||||
let phoneNumber = try Factory.Instance.createFriendPhoneNumber(phoneNumber: phone.num, label: phone.numLabel)
|
||||
friend.addPhoneNumberWithLabel(phoneNumber: phoneNumber)
|
||||
friendPhoneNumbers.append(phone)
|
||||
}
|
||||
} catch let error {
|
||||
print("Failed to enumerate contact", error)
|
||||
}
|
||||
}
|
||||
|
||||
let contactImage = result.dropFirst(8)
|
||||
friend.photo = "file:/" + contactImage
|
||||
|
||||
friend.done()
|
||||
|
||||
_ = self.friendList!.addLocalFriend(linphoneFriend: friend)
|
||||
|
||||
self.friendList!.updateSubscriptions()
|
||||
|
||||
} catch let error {
|
||||
print("Failed to enumerate contact", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func textToImage(firstName: String, lastName: String) -> UIImage {
|
||||
|
||||
let lblNameInitialize = UILabel()
|
||||
lblNameInitialize.frame.size = CGSize(width: 100.0, height: 100.0)
|
||||
lblNameInitialize.font = UIFont(name: "NotoSans-ExtraBold", size: 40)
|
||||
lblNameInitialize.textColor = UIColor(Color.grayMain2c600)
|
||||
|
||||
var textToDisplay = ""
|
||||
if firstName.first != nil {
|
||||
textToDisplay += String(firstName.first!)
|
||||
}
|
||||
if lastName.first != nil {
|
||||
textToDisplay += String(lastName.first!)
|
||||
}
|
||||
|
||||
lblNameInitialize.text = textToDisplay.uppercased()
|
||||
lblNameInitialize.textAlignment = .center
|
||||
lblNameInitialize.backgroundColor = UIColor(Color.grayMain2c200)
|
||||
lblNameInitialize.layer.cornerRadius = 10.0
|
||||
|
||||
var IBImgViewUserProfile = UIImage()
|
||||
UIGraphicsBeginImageContext(lblNameInitialize.frame.size)
|
||||
lblNameInitialize.layer.render(in: UIGraphicsGetCurrentContext()!)
|
||||
IBImgViewUserProfile = UIGraphicsGetImageFromCurrentImageContext()!
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
return IBImgViewUserProfile
|
||||
}
|
||||
|
||||
func awaitDataWrite(data: Data, name: String, completion: @escaping ((), String) -> Void) {
|
||||
let directory = FileManager.default.temporaryDirectory
|
||||
|
||||
DispatchQueue.main.async {
|
||||
do {
|
||||
let decodedData: () = try data.write(to: directory.appendingPathComponent(name + ".png"))
|
||||
completion(decodedData, directory.appendingPathComponent(name + ".png").absoluteString) // <--- here, return the results
|
||||
} catch {
|
||||
print("Error: ", error) // need to deal with errors
|
||||
completion((), "") // <--- here, should return the error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PhoneNumber {
|
||||
|
|
|
|||
|
|
@ -23,13 +23,22 @@ import SwiftUI
|
|||
struct LinphoneApp: App {
|
||||
|
||||
@ObservedObject private var coreContext = CoreContext.shared
|
||||
@ObservedObject private var sharedMainViewModel = SharedMainViewModel()
|
||||
|
||||
@State private var isActive = false
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
if isActive {
|
||||
ContentView(sharedMainViewModel: SharedMainViewModel(), contactViewModel: ContactViewModel(), historyViewModel: HistoryViewModel())
|
||||
.toast(isShowing: $coreContext.toastMessage)
|
||||
if !sharedMainViewModel.welcomeViewDisplayed {
|
||||
WelcomeView(sharedMainViewModel: sharedMainViewModel)
|
||||
} else if coreContext.mCore.defaultAccount == nil || sharedMainViewModel.displayProfileMode {
|
||||
AssistantView(sharedMainViewModel: sharedMainViewModel)
|
||||
.toast(isShowing: $coreContext.toastMessage)
|
||||
} else {
|
||||
ContentView(contactViewModel: ContactViewModel(), historyViewModel: HistoryViewModel())
|
||||
.toast(isShowing: $coreContext.toastMessage)
|
||||
}
|
||||
} else {
|
||||
SplashScreen(isActive: $isActive)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import SwiftUI
|
|||
|
||||
struct ContactsFragment: View {
|
||||
|
||||
@ObservedObject var magicSearch = MagicSearchSingleton.shared
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
|
|
@ -29,42 +30,43 @@ struct ContactsFragment: View {
|
|||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
HStack(alignment: .center) {
|
||||
Text("Favourites")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(isFavoriteOpen ? "caret-up" : "caret-down")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
}
|
||||
.padding(.top, 30)
|
||||
.padding(.horizontal, 16)
|
||||
.background(.white)
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
isFavoriteOpen.toggle()
|
||||
if !magicSearch.lastSearch.filter({ $0.friend?.starred == true }).isEmpty {
|
||||
HStack(alignment: .center) {
|
||||
Text("Favourites")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(isFavoriteOpen ? "caret-up" : "caret-down")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
}
|
||||
.padding(.top, 30)
|
||||
.padding(.horizontal, 16)
|
||||
.background(.white)
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
isFavoriteOpen.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isFavoriteOpen {
|
||||
FavoriteContactsListFragment(contactViewModel: contactViewModel, favoriteContactsListViewModel: FavoriteContactsListViewModel())
|
||||
.zIndex(-1)
|
||||
.transition(.move(edge: .top))
|
||||
}
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Text("All contacts")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
|
||||
Spacer()
|
||||
if isFavoriteOpen {
|
||||
FavoriteContactsListFragment(contactViewModel: contactViewModel, favoriteContactsListViewModel: FavoriteContactsListViewModel())
|
||||
.zIndex(-1)
|
||||
.transition(.move(edge: .top))
|
||||
}
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Text("All contacts")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.top, 10)
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
.padding(.top, 10)
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
ContactsListFragment(contactViewModel: contactViewModel, contactsListViewModel: ContactsListViewModel())
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ struct ContactsListFragment: View {
|
|||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
case .failure:
|
||||
Image("profile-image-example")
|
||||
Image("profil-picture-default")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
|
|
@ -73,7 +73,7 @@ struct ContactsListFragment: View {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
Image("profile-image-example")
|
||||
Image("profil-picture-default")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ struct FavoriteContactsListFragment: View {
|
|||
var body: some View {
|
||||
ScrollView(.horizontal) {
|
||||
HStack {
|
||||
ForEach(0..<magicSearch.lastSearch.count, id: \.self) { index in
|
||||
ForEach(0..<magicSearch.lastSearch.count, id: \.self) { index in
|
||||
Button {
|
||||
withAnimation {
|
||||
contactViewModel.contactTitle = (magicSearch.lastSearch[index].friend?.name)!
|
||||
|
|
@ -48,7 +48,7 @@ struct FavoriteContactsListFragment: View {
|
|||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
case .failure:
|
||||
Image("profile-image-example")
|
||||
Image("profil-picture-default")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
|
|
@ -57,7 +57,7 @@ struct FavoriteContactsListFragment: View {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
Image("profile-image-example")
|
||||
Image("profil-picture-default")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
|
|
@ -67,7 +67,7 @@ struct FavoriteContactsListFragment: View {
|
|||
.frame( maxWidth: .infinity, alignment: .center)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: 70)
|
||||
.frame(minWidth: 70, maxWidth: 70)
|
||||
}
|
||||
}
|
||||
.padding(.all, 10)
|
||||
|
|
|
|||
|
|
@ -23,5 +23,9 @@ class ContactViewModel: ObservableObject {
|
|||
|
||||
@Published var contactTitle: String = ""
|
||||
|
||||
init() {}
|
||||
private var magicSearch = MagicSearchSingleton.shared
|
||||
|
||||
init() {
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,5 @@ import linphonesw
|
|||
|
||||
class ContactsListViewModel: ObservableObject {
|
||||
|
||||
private var magicSearch = MagicSearchSingleton.shared
|
||||
private var coreContext = CoreContext.shared
|
||||
|
||||
init() {
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
}
|
||||
init() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,418 +23,411 @@ import linphonesw
|
|||
struct ContentView: View {
|
||||
|
||||
var contactManager = ContactsManager.shared
|
||||
var magicSearch = MagicSearchSingleton.shared
|
||||
|
||||
@ObservedObject var sharedMainViewModel: SharedMainViewModel
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
@ObservedObject var historyViewModel: HistoryViewModel
|
||||
@ObservedObject private var coreContext = CoreContext.shared
|
||||
|
||||
@State var index = 0
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
@State var sideMenuIsOpen: Bool = false
|
||||
|
||||
@State private var searchIsActive = false
|
||||
@State private var text = ""
|
||||
@FocusState private var focusedField: Bool
|
||||
var magicSearch = MagicSearchSingleton.shared
|
||||
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
@ObservedObject var historyViewModel: HistoryViewModel
|
||||
@ObservedObject private var coreContext = CoreContext.shared
|
||||
|
||||
@State var index = 0
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
@State var sideMenuIsOpen: Bool = false
|
||||
|
||||
@State private var searchIsActive = false
|
||||
@State private var text = ""
|
||||
@FocusState private var focusedField: Bool
|
||||
@State var isMenuOpen: Bool = false
|
||||
|
||||
var body: some View {
|
||||
if !sharedMainViewModel.welcomeViewDisplayed {
|
||||
WelcomeView(sharedMainViewModel: sharedMainViewModel)
|
||||
} else if coreContext.mCore.defaultAccount == nil || sharedMainViewModel.displayProfileMode {
|
||||
AssistantView(sharedMainViewModel: sharedMainViewModel)
|
||||
} else {
|
||||
GeometryReader { geometry in
|
||||
ZStack {
|
||||
VStack(spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
if orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height {
|
||||
VStack {
|
||||
Group {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
self.index = 0
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("address-book")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 0 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 0 {
|
||||
Text("Contacts")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Contacts")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
self.index = 1
|
||||
contactViewModel.contactTitle = ""
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("phone")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 1 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 1 {
|
||||
Text("Calls")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Calls")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.frame(width: 75)
|
||||
.padding(.leading,
|
||||
orientation == .landscapeRight && geometry.safeAreaInsets.bottom > 0
|
||||
? -geometry.safeAreaInsets.leading
|
||||
: 0)
|
||||
}
|
||||
|
||||
VStack(spacing: 0) {
|
||||
if searchIsActive == false {
|
||||
HStack {
|
||||
Image("profile-image-example")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
.onTapGesture {
|
||||
openMenu()
|
||||
}
|
||||
|
||||
Text(index == 0 ? "Contacts" : "Calls")
|
||||
.default_text_style_white_800(styleSize: 20)
|
||||
.padding(.leading, 10)
|
||||
|
||||
Spacer()
|
||||
|
||||
Button {
|
||||
withAnimation {
|
||||
searchIsActive.toggle()
|
||||
}
|
||||
} label: {
|
||||
Image("search")
|
||||
}
|
||||
|
||||
Menu {
|
||||
Button {
|
||||
isMenuOpen = false
|
||||
magicSearch.allContact = true
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} label: {
|
||||
HStack {
|
||||
Text("See all")
|
||||
Spacer()
|
||||
if magicSearch.allContact {
|
||||
Image("green-check")
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
ZStack {
|
||||
VStack(spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
if orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height {
|
||||
VStack {
|
||||
Group {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
self.index = 0
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("address-book")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 0 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 0 {
|
||||
Text("Contacts")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Contacts")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
|
||||
Button {
|
||||
isMenuOpen = false
|
||||
magicSearch.allContact = false
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} label: {
|
||||
HStack {
|
||||
Text("See Linphone contact")
|
||||
Spacer()
|
||||
if !magicSearch.allContact {
|
||||
Image("green-check")
|
||||
}
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Image(index == 0 ? "filtres" : "more")
|
||||
}
|
||||
.padding(.leading)
|
||||
.onTapGesture {
|
||||
isMenuOpen = true
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom, 5)
|
||||
.background(Color.orangeMain500)
|
||||
} else {
|
||||
HStack {
|
||||
Button {
|
||||
withAnimation {
|
||||
self.focusedField = false
|
||||
searchIsActive.toggle()
|
||||
}
|
||||
|
||||
text = ""
|
||||
magicSearch.currentFilter = ""
|
||||
})
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
self.index = 1
|
||||
contactViewModel.contactTitle = ""
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("phone")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 1 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 1 {
|
||||
Text("Calls")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Calls")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.frame(width: 75)
|
||||
.padding(.leading,
|
||||
orientation == .landscapeRight && geometry.safeAreaInsets.bottom > 0
|
||||
? -geometry.safeAreaInsets.leading
|
||||
: 0)
|
||||
}
|
||||
|
||||
VStack(spacing: 0) {
|
||||
if searchIsActive == false {
|
||||
HStack {
|
||||
Image("profile-image-example")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
.onTapGesture {
|
||||
openMenu()
|
||||
}
|
||||
|
||||
Text(index == 0 ? "Contacts" : "Calls")
|
||||
.default_text_style_white_800(styleSize: 20)
|
||||
.padding(.leading, 10)
|
||||
|
||||
Spacer()
|
||||
|
||||
Button {
|
||||
withAnimation {
|
||||
searchIsActive.toggle()
|
||||
}
|
||||
} label: {
|
||||
Image("search")
|
||||
}
|
||||
|
||||
Menu {
|
||||
Button {
|
||||
isMenuOpen = false
|
||||
magicSearch.allContact = true
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} label: {
|
||||
Image("caret-left")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
}
|
||||
|
||||
if #available(iOS 16.0, *) {
|
||||
TextEditor(text: Binding(
|
||||
get: {
|
||||
return text
|
||||
},
|
||||
set: { value in
|
||||
var newValue = value
|
||||
if value.contains("\n") {
|
||||
newValue = value.replacingOccurrences(of: "\n", with: "")
|
||||
}
|
||||
text = newValue
|
||||
}
|
||||
))
|
||||
.default_text_style_white_700(styleSize: 15)
|
||||
.padding(.all, 6)
|
||||
.accentColor(.white)
|
||||
.scrollContentBackground(.hidden)
|
||||
.focused($focusedField)
|
||||
.onAppear {
|
||||
self.focusedField = true
|
||||
}
|
||||
.onChange(of: text) { newValue in
|
||||
magicSearch.currentFilter = newValue
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
}
|
||||
} else {
|
||||
TextEditor(text: Binding(
|
||||
get: {
|
||||
return text
|
||||
},
|
||||
set: { value in
|
||||
var newValue = value
|
||||
if value.contains("\n") {
|
||||
newValue = value.replacingOccurrences(of: "\n", with: "")
|
||||
}
|
||||
text = newValue
|
||||
}
|
||||
))
|
||||
.default_text_style_white_700(styleSize: 15)
|
||||
.padding(.all, 6)
|
||||
.accentColor(.white)
|
||||
.focused($focusedField)
|
||||
.onAppear {
|
||||
self.focusedField = true
|
||||
}
|
||||
.onChange(of: text) { newValue in
|
||||
magicSearch.currentFilter = newValue
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text = ""
|
||||
} label: {
|
||||
Image("x")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
}
|
||||
.padding(.leading)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom, 5)
|
||||
.background(Color.orangeMain500)
|
||||
}
|
||||
|
||||
if self.index == 0 {
|
||||
ContactsView(contactViewModel: contactViewModel, historyViewModel: historyViewModel)
|
||||
} else if self.index == 1 {
|
||||
HistoryView()
|
||||
}
|
||||
}
|
||||
.frame(maxWidth:
|
||||
(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
? geometry.size.width/100*40
|
||||
: .infinity
|
||||
)
|
||||
.background(
|
||||
Color.white
|
||||
.shadow(color: Color.gray200, radius: 4, x: 0, y: 0)
|
||||
.mask(Rectangle().padding(.horizontal, -8))
|
||||
)
|
||||
|
||||
if orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
if !(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) && !searchIsActive {
|
||||
HStack {
|
||||
Group {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
self.index = 0
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("address-book")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 0 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 0 {
|
||||
Text("Contacts")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Contacts")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
}
|
||||
})
|
||||
.padding(.top)
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
self.index = 1
|
||||
contactViewModel.contactTitle = ""
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("phone")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 1 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 1 {
|
||||
Text("Calls")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Calls")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
}
|
||||
})
|
||||
.padding(.top)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.padding(.bottom, geometry.safeAreaInsets.bottom > 0 ? 0 : 15)
|
||||
.background(
|
||||
Color.white
|
||||
.shadow(color: Color.gray200, radius: 4, x: 0, y: 0)
|
||||
.mask(Rectangle().padding(.top, -8))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if !contactViewModel.contactTitle.isEmpty || !historyViewModel.historyTitle.isEmpty {
|
||||
HStack(spacing: 0) {
|
||||
Spacer()
|
||||
.frame(maxWidth:
|
||||
(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
? (geometry.size.width/100*40) + 75
|
||||
: 0
|
||||
)
|
||||
if self.index == 0 {
|
||||
ContactFragment(contactViewModel: contactViewModel)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color.gray100)
|
||||
.ignoresSafeArea(.keyboard)
|
||||
} else if self.index == 1 {
|
||||
HistoryContactFragment()
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color.gray100)
|
||||
.ignoresSafeArea(.keyboard)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if !(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
&& searchIsActive {
|
||||
self.focusedField = false
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
if !(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
&& searchIsActive {
|
||||
self.focusedField = true
|
||||
}
|
||||
}
|
||||
.padding(.leading,
|
||||
orientation == .landscapeRight && geometry.safeAreaInsets.bottom > 0
|
||||
? -geometry.safeAreaInsets.leading
|
||||
: 0)
|
||||
.transition(.move(edge: .trailing))
|
||||
.zIndex(1)
|
||||
}
|
||||
|
||||
SideMenu(
|
||||
width: geometry.size.width / 5 * 4,
|
||||
isOpen: self.sideMenuIsOpen,
|
||||
menuClose: self.openMenu,
|
||||
safeAreaInsets: geometry.safeAreaInsets
|
||||
)
|
||||
.ignoresSafeArea(.all)
|
||||
.zIndex(2)
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
if isMenuOpen {
|
||||
Color.white.opacity(0.001)
|
||||
} label: {
|
||||
HStack {
|
||||
Text("See all")
|
||||
Spacer()
|
||||
if magicSearch.allContact {
|
||||
Image("green-check")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
isMenuOpen = false
|
||||
magicSearch.allContact = false
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} label: {
|
||||
HStack {
|
||||
Text("See Linphone contact")
|
||||
Spacer()
|
||||
if !magicSearch.allContact {
|
||||
Image("green-check")
|
||||
}
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Image(index == 0 ? "filtres" : "more")
|
||||
}
|
||||
.padding(.leading)
|
||||
.onTapGesture {
|
||||
isMenuOpen = true
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom, 5)
|
||||
.background(Color.orangeMain500)
|
||||
} else {
|
||||
HStack {
|
||||
Button {
|
||||
withAnimation {
|
||||
self.focusedField = false
|
||||
searchIsActive.toggle()
|
||||
}
|
||||
|
||||
text = ""
|
||||
magicSearch.currentFilter = ""
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} label: {
|
||||
Image("caret-left")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
}
|
||||
|
||||
if #available(iOS 16.0, *) {
|
||||
TextEditor(text: Binding(
|
||||
get: {
|
||||
return text
|
||||
},
|
||||
set: { value in
|
||||
var newValue = value
|
||||
if value.contains("\n") {
|
||||
newValue = value.replacingOccurrences(of: "\n", with: "")
|
||||
}
|
||||
text = newValue
|
||||
}
|
||||
))
|
||||
.default_text_style_white_700(styleSize: 15)
|
||||
.padding(.all, 6)
|
||||
.accentColor(.white)
|
||||
.scrollContentBackground(.hidden)
|
||||
.focused($focusedField)
|
||||
.onAppear {
|
||||
self.focusedField = true
|
||||
}
|
||||
.onChange(of: text) { newValue in
|
||||
magicSearch.currentFilter = newValue
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
}
|
||||
} else {
|
||||
TextEditor(text: Binding(
|
||||
get: {
|
||||
return text
|
||||
},
|
||||
set: { value in
|
||||
var newValue = value
|
||||
if value.contains("\n") {
|
||||
newValue = value.replacingOccurrences(of: "\n", with: "")
|
||||
}
|
||||
text = newValue
|
||||
}
|
||||
))
|
||||
.default_text_style_white_700(styleSize: 15)
|
||||
.padding(.all, 6)
|
||||
.accentColor(.white)
|
||||
.focused($focusedField)
|
||||
.onAppear {
|
||||
self.focusedField = true
|
||||
}
|
||||
.onChange(of: text) { newValue in
|
||||
magicSearch.currentFilter = newValue
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text = ""
|
||||
} label: {
|
||||
Image("x")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
}
|
||||
.padding(.leading)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom, 5)
|
||||
.background(Color.orangeMain500)
|
||||
}
|
||||
|
||||
if self.index == 0 {
|
||||
ContactsView(contactViewModel: contactViewModel, historyViewModel: historyViewModel)
|
||||
} else if self.index == 1 {
|
||||
HistoryView()
|
||||
}
|
||||
}
|
||||
.frame(maxWidth:
|
||||
(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
? geometry.size.width/100*40
|
||||
: .infinity
|
||||
)
|
||||
.background(
|
||||
Color.white
|
||||
.shadow(color: Color.gray200, radius: 4, x: 0, y: 0)
|
||||
.mask(Rectangle().padding(.horizontal, -8))
|
||||
)
|
||||
|
||||
if orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
if !(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) && !searchIsActive {
|
||||
HStack {
|
||||
Group {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
self.index = 0
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("address-book")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 0 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 0 {
|
||||
Text("Contacts")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Contacts")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
}
|
||||
})
|
||||
.padding(.top)
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
self.index = 1
|
||||
contactViewModel.contactTitle = ""
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("phone")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 1 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 1 {
|
||||
Text("Calls")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Calls")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
}
|
||||
})
|
||||
.padding(.top)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.padding(.bottom, geometry.safeAreaInsets.bottom > 0 ? 0 : 15)
|
||||
.background(
|
||||
Color.white
|
||||
.shadow(color: Color.gray200, radius: 4, x: 0, y: 0)
|
||||
.mask(Rectangle().padding(.top, -8))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if !contactViewModel.contactTitle.isEmpty || !historyViewModel.historyTitle.isEmpty {
|
||||
HStack(spacing: 0) {
|
||||
Spacer()
|
||||
.frame(maxWidth:
|
||||
(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
? (geometry.size.width/100*40) + 75
|
||||
: 0
|
||||
)
|
||||
if self.index == 0 {
|
||||
ContactFragment(contactViewModel: contactViewModel)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color.gray100)
|
||||
.ignoresSafeArea(.keyboard)
|
||||
} else if self.index == 1 {
|
||||
HistoryContactFragment()
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color.gray100)
|
||||
.ignoresSafeArea(.keyboard)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if !(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
&& searchIsActive {
|
||||
self.focusedField = false
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
if !(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
&& searchIsActive {
|
||||
self.focusedField = true
|
||||
}
|
||||
}
|
||||
.padding(.leading,
|
||||
orientation == .landscapeRight && geometry.safeAreaInsets.bottom > 0
|
||||
? -geometry.safeAreaInsets.leading
|
||||
: 0)
|
||||
.transition(.move(edge: .trailing))
|
||||
.zIndex(1)
|
||||
}
|
||||
|
||||
SideMenu(
|
||||
width: geometry.size.width / 5 * 4,
|
||||
isOpen: self.sideMenuIsOpen,
|
||||
menuClose: self.openMenu,
|
||||
safeAreaInsets: geometry.safeAreaInsets
|
||||
)
|
||||
.ignoresSafeArea(.all)
|
||||
.zIndex(2)
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
if isMenuOpen {
|
||||
Color.white.opacity(0.001)
|
||||
.ignoresSafeArea()
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.onTapGesture {
|
||||
isMenuOpen = false
|
||||
}
|
||||
}
|
||||
}
|
||||
.onRotate { newOrientation in
|
||||
if (!contactViewModel.contactTitle.isEmpty || !historyViewModel.historyTitle.isEmpty) && searchIsActive {
|
||||
self.focusedField = false
|
||||
} else if searchIsActive {
|
||||
self.focusedField = true
|
||||
}
|
||||
orientation = newOrientation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openMenu() {
|
||||
withAnimation {
|
||||
self.sideMenuIsOpen.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
.onRotate { newOrientation in
|
||||
if (!contactViewModel.contactTitle.isEmpty || !historyViewModel.historyTitle.isEmpty) && searchIsActive {
|
||||
self.focusedField = false
|
||||
} else if searchIsActive {
|
||||
self.focusedField = true
|
||||
}
|
||||
orientation = newOrientation
|
||||
}
|
||||
}
|
||||
|
||||
func openMenu() {
|
||||
withAnimation {
|
||||
self.sideMenuIsOpen.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContentView(sharedMainViewModel: SharedMainViewModel(), contactViewModel: ContactViewModel(), historyViewModel: HistoryViewModel())
|
||||
ContentView(contactViewModel: ContactViewModel(), historyViewModel: HistoryViewModel())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue