Fix presence

This commit is contained in:
Benoit Martins 2023-11-28 17:28:46 +01:00
parent 07b2c1e04e
commit 7b476904cb
24 changed files with 445 additions and 280 deletions

View file

@ -27,6 +27,7 @@
D72343322ACEFF58009AA24E /* QRScannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72343312ACEFF58009AA24E /* QRScannerController.swift */; };
D72343342ACEFFC3009AA24E /* QRScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72343332ACEFFC3009AA24E /* QRScanner.swift */; };
D72343362AD037AF009AA24E /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72343352AD037AF009AA24E /* ToastView.swift */; };
D726E4392B16440C0083C415 /* ContactAvatarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D726E4382B16440C0083C415 /* ContactAvatarModel.swift */; };
D72992392ADD7F68003AF125 /* HistoryContactFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72992382ADD7F68003AF125 /* HistoryContactFragment.swift */; };
D732A9092AFD235500DB42BA /* ShareSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A9082AFD235500DB42BA /* ShareSheetController.swift */; };
D732A90C2B0376F500DB42BA /* linphonerc-default in Resources */ = {isa = PBXBuildFile; fileRef = D732A90A2B0376F500DB42BA /* linphonerc-default */; };
@ -104,6 +105,7 @@
D72343312ACEFF58009AA24E /* QRScannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScannerController.swift; sourceTree = "<group>"; };
D72343332ACEFFC3009AA24E /* QRScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScanner.swift; sourceTree = "<group>"; };
D72343352AD037AF009AA24E /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; };
D726E4382B16440C0083C415 /* ContactAvatarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactAvatarModel.swift; sourceTree = "<group>"; };
D72992382ADD7F68003AF125 /* HistoryContactFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryContactFragment.swift; sourceTree = "<group>"; };
D732A9082AFD235500DB42BA /* ShareSheetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheetController.swift; sourceTree = "<group>"; };
D732A90A2B0376F500DB42BA /* linphonerc-default */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "linphonerc-default"; sourceTree = "<group>"; };
@ -297,6 +299,14 @@
path = ViewModel;
sourceTree = "<group>";
};
D726E4372B1643FF0083C415 /* Model */ = {
isa = PBXGroup;
children = (
D726E4382B16440C0083C415 /* ContactAvatarModel.swift */,
);
path = Model;
sourceTree = "<group>";
};
D72992372ADD7F1C003AF125 /* Fragments */ = {
isa = PBXGroup;
children = (
@ -380,6 +390,7 @@
isa = PBXGroup;
children = (
D78290B62ADD38F9004AA85C /* Fragments */,
D726E4372B1643FF0083C415 /* Model */,
D78290B92ADD409D004AA85C /* ViewModel */,
D7A03FBC2ACC2DB60081A588 /* ContactsView.swift */,
);
@ -578,6 +589,7 @@
D719ABB72ABC67BF00B41C10 /* LinphoneApp.swift in Sources */,
D732A91B2B061BD900DB42BA /* HistoryListBottomSheet.swift in Sources */,
D72250632ADE9615008FB426 /* HistoryViewModel.swift in Sources */,
D726E4392B16440C0083C415 /* ContactAvatarModel.swift in Sources */,
D76005F62B0798B00054B79A /* IntExtension.swift in Sources */,
D7E6D0512AEBDBD500A57AAF /* ContactsListBottomSheet.swift in Sources */,
D7A2EDD62AC18115005D90FC /* SharedMainViewModel.swift in Sources */,

View file

@ -22,12 +22,11 @@ import Contacts
import SwiftUI
import ContactsUI
final class ContactsManager {
final class ContactsManager: ObservableObject {
static let shared = ContactsManager()
private var coreContext = CoreContext.shared
private var magicSearch = MagicSearchSingleton.shared
private let nativeAddressBookFriendList = "Native address-book"
let linphoneAddressBookFriendList = "Linphone address-book"
@ -35,6 +34,9 @@ final class ContactsManager {
var friendList: FriendList?
var linphoneFriendList: FriendList?
@Published var lastSearch: [SearchResult] = []
@Published var avatarListModel: [ContactAvatarModel] = []
private init() {
fetchContacts()
}
@ -132,7 +134,8 @@ final class ContactsManager {
print("\(#function) - access denied")
}
}
self.magicSearch.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
}
}

View file

@ -247,9 +247,6 @@
},
"En continuant, vous acceptez ces conditions, " : {
},
"En ligne" : {
},
"Error" : {

View file

@ -34,9 +34,11 @@ struct ContactFragment: View {
@State private var showShareSheet = false
var body: some View {
let indexDisplayed = contactViewModel.indexDisplayedFriend != nil ? contactViewModel.indexDisplayedFriend! : 0
if #available(iOS 16.0, *) {
if idiom != .pad {
ContactInnerFragment(
contactAvatarModel: ContactsManager.shared.avatarListModel[indexDisplayed],
contactViewModel: contactViewModel,
editContactViewModel: editContactViewModel,
cnContact: CNContact(),
@ -50,12 +52,13 @@ struct ContactFragment: View {
.presentationDetents([.fraction(0.2)])
}
.sheet(isPresented: $showShareSheet) {
ShareSheet(friendToShare: MagicSearchSingleton.shared.lastSearch[contactViewModel.indexDisplayedFriend!].friend!)
ShareSheet(friendToShare: ContactsManager.shared.lastSearch[contactViewModel.indexDisplayedFriend!].friend!)
.presentationDetents([.medium])
.edgesIgnoringSafeArea(.bottom)
}
} else {
ContactInnerFragment(
contactAvatarModel: ContactsManager.shared.avatarListModel[indexDisplayed],
contactViewModel: contactViewModel,
editContactViewModel: editContactViewModel,
cnContact: CNContact(),
@ -68,12 +71,13 @@ struct ContactFragment: View {
ContactListBottomSheet(contactViewModel: contactViewModel, showingSheet: $showingSheet)
} onDismiss: {}
.sheet(isPresented: $showShareSheet) {
ShareSheet(friendToShare: MagicSearchSingleton.shared.lastSearch[contactViewModel.indexDisplayedFriend!].friend!)
ShareSheet(friendToShare: ContactsManager.shared.lastSearch[contactViewModel.indexDisplayedFriend!].friend!)
.edgesIgnoringSafeArea(.bottom)
}
}
} else {
ContactInnerFragment(
contactAvatarModel: ContactsManager.shared.avatarListModel[indexDisplayed],
contactViewModel: contactViewModel,
editContactViewModel: editContactViewModel,
cnContact: CNContact(),
@ -86,7 +90,7 @@ struct ContactFragment: View {
ContactListBottomSheet(contactViewModel: contactViewModel, showingSheet: $showingSheet)
} onDismiss: {}
.sheet(isPresented: $showShareSheet) {
ShareSheet(friendToShare: MagicSearchSingleton.shared.lastSearch[contactViewModel.indexDisplayedFriend!].friend!)
ShareSheet(friendToShare: ContactsManager.shared.lastSearch[contactViewModel.indexDisplayedFriend!].friend!)
.edgesIgnoringSafeArea(.bottom)
}
}

View file

@ -21,7 +21,8 @@ import SwiftUI
struct ContactInnerActionsFragment: View {
@ObservedObject var magicSearch = MagicSearchSingleton.shared
@ObservedObject var contactsManager = ContactsManager.shared
@ObservedObject var contactViewModel: ContactViewModel
@ObservedObject var editContactViewModel: EditContactViewModel
@ -59,8 +60,8 @@ struct ContactInnerActionsFragment: View {
if informationIsOpen {
VStack(spacing: 0) {
if contactViewModel.indexDisplayedFriend != nil && magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil {
ForEach(0..<magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.addresses.count, id: \.self) { index in
if contactViewModel.indexDisplayedFriend != nil && contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil {
ForEach(0..<contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.addresses.count, id: \.self) { index in
Button {
} label: {
HStack {
@ -68,7 +69,7 @@ struct ContactInnerActionsFragment: View {
Text("SIP address :")
.default_text_style_700(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
Text(magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.addresses[index].asStringUriOnly().dropFirst(4))
Text(contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.addresses[index].asStringUriOnly().dropFirst(4))
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
@ -93,7 +94,7 @@ struct ContactInnerActionsFragment: View {
.simultaneousGesture(
LongPressGesture()
.onEnded { _ in
contactViewModel.stringToCopy = magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.addresses[index].asStringUriOnly()
contactViewModel.stringToCopy = contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.addresses[index].asStringUriOnly()
showingSheet.toggle()
}
)
@ -106,8 +107,8 @@ struct ContactInnerActionsFragment: View {
}
)
if !magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbers.isEmpty
|| index < magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.addresses.count - 1 {
if !contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbers.isEmpty
|| index < contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.addresses.count - 1 {
VStack {
Divider()
}
@ -115,14 +116,14 @@ struct ContactInnerActionsFragment: View {
}
}
ForEach(0..<magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbers.count, id: \.self) { index in
ForEach(0..<contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbers.count, id: \.self) { index in
Button {
} label: {
HStack {
VStack {
if magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbersWithLabel[index].label != nil
&& !magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbersWithLabel[index].label!.isEmpty {
Text("Phone (\(magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbersWithLabel[index].label!)) :")
if contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbersWithLabel[index].label != nil
&& !contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbersWithLabel[index].label!.isEmpty {
Text("Phone (\(contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbersWithLabel[index].label!)) :")
.default_text_style_700(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
} else {
@ -130,7 +131,7 @@ struct ContactInnerActionsFragment: View {
.default_text_style_700(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
}
Text(magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbersWithLabel[index].phoneNumber)
Text(contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbersWithLabel[index].phoneNumber)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
@ -156,7 +157,7 @@ struct ContactInnerActionsFragment: View {
LongPressGesture()
.onEnded { _ in
contactViewModel.stringToCopy =
magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbersWithLabel[index].phoneNumber
contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbersWithLabel[index].phoneNumber
showingSheet.toggle()
}
)
@ -169,7 +170,7 @@ struct ContactInnerActionsFragment: View {
}
)
if index < magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbers.count - 1 {
if index < contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.phoneNumbers.count - 1 {
VStack {
Divider()
}
@ -186,28 +187,28 @@ struct ContactInnerActionsFragment: View {
}
if contactViewModel.indexDisplayedFriend != nil
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& ((magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization != nil
&& !magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization!.isEmpty)
|| (magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.jobTitle != nil
&& !magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.jobTitle!.isEmpty)) {
&& contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& ((contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization != nil
&& !contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization!.isEmpty)
|| (contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.jobTitle != nil
&& !contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.jobTitle!.isEmpty)) {
VStack {
if magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization != nil
&& !magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization!.isEmpty {
Text("**Company :** \(magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization!)")
if contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization != nil
&& !contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization!.isEmpty {
Text("**Company :** \(contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization!)")
.default_text_style(styleSize: 14)
.padding(.vertical, 15)
.padding(.horizontal, 20)
.frame(maxWidth: .infinity, alignment: .leading)
}
if magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.jobTitle != nil
&& !magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.jobTitle!.isEmpty {
Text("**Job :** \(magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.jobTitle!)")
if contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.jobTitle != nil
&& !contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.jobTitle!.isEmpty {
Text("**Job :** \(contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.jobTitle!)")
.default_text_style(styleSize: 14)
.padding(.top,
magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization != nil
&& !magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization!.isEmpty
contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization != nil
&& !contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.organization!.isEmpty
? 0 : 15
)
.padding(.bottom, 15)
@ -238,10 +239,10 @@ struct ContactInnerActionsFragment: View {
.background(Color.gray100)
VStack(spacing: 0) {
if contactViewModel.indexDisplayedFriend != nil && contactViewModel.indexDisplayedFriend! < magicSearch.lastSearch.count
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.nativeUri != nil
&& !magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.nativeUri!.isEmpty {
if contactViewModel.indexDisplayedFriend != nil && contactViewModel.indexDisplayedFriend! < contactsManager.lastSearch.count
&& contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.nativeUri != nil
&& !contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.nativeUri!.isEmpty {
Button {
actionEditButton()
} label: {
@ -265,6 +266,7 @@ struct ContactInnerActionsFragment: View {
} else {
NavigationLink(destination: EditContactFragment(
editContactViewModel: editContactViewModel,
contactViewModel: contactViewModel,
isShowEditContactFragment: .constant(false),
isShowDismissPopup: $isShowDismissPopup)) {
HStack {
@ -286,7 +288,7 @@ struct ContactInnerActionsFragment: View {
}
.simultaneousGesture(
TapGesture().onEnded {
editContactViewModel.selectedEditFriend = magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend
editContactViewModel.selectedEditFriend = contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend
editContactViewModel.resetValues()
}
)
@ -298,22 +300,22 @@ struct ContactInnerActionsFragment: View {
.padding(.horizontal)
Button {
if magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil {
if contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil {
contactViewModel.objectWillChange.send()
magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.starred.toggle()
contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.starred.toggle()
}
} label: {
HStack {
Image(contactViewModel.indexDisplayedFriend != nil && magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.starred == true ? "heart-fill" : "heart")
Image(contactViewModel.indexDisplayedFriend != nil && contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.starred == true ? "heart-fill" : "heart")
.renderingMode(.template)
.resizable()
.foregroundStyle(contactViewModel.indexDisplayedFriend != nil && magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.starred == true ? Color.redDanger500 : Color.grayMain2c500)
.foregroundStyle(contactViewModel.indexDisplayedFriend != nil && contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.starred == true ? Color.redDanger500 : Color.grayMain2c500)
.frame(width: 25, height: 25)
Text(contactViewModel.indexDisplayedFriend != nil
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.starred == true
&& contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.starred == true
? "Remove from favourites"
: "Add to favourites")
.default_text_style(styleSize: 14)
@ -410,7 +412,7 @@ struct ContactInnerActionsFragment: View {
*/
Button {
if magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil {
if contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil {
isShowDeletePopup.toggle()
}
} label: {

View file

@ -24,9 +24,9 @@ import ContactsUI
struct ContactInnerFragment: View {
@ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared
@ObservedObject var contactsManager = ContactsManager.shared
@ObservedObject var magicSearch = MagicSearchSingleton.shared
@ObservedObject var contactAvatarModel: ContactAvatarModel
@ObservedObject var contactViewModel: ContactViewModel
@ObservedObject var editContactViewModel: EditContactViewModel
@ -65,10 +65,10 @@ struct ContactInnerFragment: View {
}
Spacer()
if contactViewModel.indexDisplayedFriend != nil && contactViewModel.indexDisplayedFriend! < magicSearch.lastSearch.count
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.nativeUri != nil
&& !magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.nativeUri!.isEmpty {
if contactViewModel.indexDisplayedFriend != nil && contactViewModel.indexDisplayedFriend! < contactsManager.lastSearch.count
&& contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.nativeUri != nil
&& !contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.nativeUri!.isEmpty {
Button(action: {
editNativeContact()
}, label: {
@ -82,6 +82,7 @@ struct ContactInnerFragment: View {
} else {
NavigationLink(destination: EditContactFragment(
editContactViewModel: editContactViewModel,
contactViewModel: contactViewModel,
isShowEditContactFragment: .constant(false),
isShowDismissPopup: $isShowDismissPopup)) {
Image("pencil-simple")
@ -93,7 +94,7 @@ struct ContactInnerFragment: View {
}
.simultaneousGesture(
TapGesture().onEnded {
editContactViewModel.selectedEditFriend = magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend
editContactViewModel.selectedEditFriend = contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend
editContactViewModel.resetValues()
}
)
@ -109,29 +110,31 @@ struct ContactInnerFragment: View {
VStack(spacing: 0) {
VStack(spacing: 0) {
VStack(spacing: 0) {
if contactViewModel.indexDisplayedFriend != nil && contactViewModel.indexDisplayedFriend! < magicSearch.lastSearch.count
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.photo != nil
&& !magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.photo!.isEmpty {
Avatar(friend: magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!, avatarSize: 100)
} else if contactViewModel.indexDisplayedFriend != nil && magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil {
if contactViewModel.indexDisplayedFriend != nil && contactViewModel.indexDisplayedFriend! < contactsManager.lastSearch.count
&& contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.photo != nil
&& !contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.photo!.isEmpty {
Avatar(contactAvatarModel: contactAvatarModel, avatarSize: 100)
} else if contactViewModel.indexDisplayedFriend != nil && contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil {
Image("profil-picture-default")
.resizable()
.frame(width: 100, height: 100)
.clipShape(Circle())
}
if contactViewModel.indexDisplayedFriend != nil
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend?.name != nil {
Text((magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend?.name)!)
&& contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil
&& contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend?.name != nil {
Text((contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend?.name)!)
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 10)
Text("En ligne")
.foregroundStyle(Color.greenSuccess500)
Text(contactAvatarModel.lastPresenceInfo)
.foregroundStyle(contactAvatarModel.lastPresenceInfo == "Online"
? Color.greenSuccess500
: Color.orangeWarning600)
.multilineTextAlignment(.center)
.default_text_style_300(styleSize: 12)
.frame(maxWidth: .infinity)
@ -268,7 +271,7 @@ struct ContactInnerFragment: View {
let store = CNContactStore()
let descriptor = CNContactViewController.descriptorForRequiredKeys()
cnContact = try store.unifiedContact(
withIdentifier: magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.nativeUri!,
withIdentifier: contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.nativeUri!,
keysToFetch: [descriptor]
)
@ -283,6 +286,7 @@ struct ContactInnerFragment: View {
#Preview {
ContactInnerFragment(
contactAvatarModel: ContactAvatarModel(friend: nil, withPresence: true),
contactViewModel: ContactViewModel(),
editContactViewModel: EditContactViewModel(),
isShowDeletePopup: .constant(false),

View file

@ -24,7 +24,6 @@ struct ContactListBottomSheet: View {
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
@ObservedObject var magicSearch = MagicSearchSingleton.shared
@ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared
@ObservedObject var contactViewModel: ContactViewModel

View file

@ -20,8 +20,10 @@
import SwiftUI
import linphonesw
struct ContactsInnerFragment: View {
@ObservedObject var magicSearch = MagicSearchSingleton.shared
struct ContactsInnerFragment: View {
@ObservedObject var contactsManager = ContactsManager.shared
@ObservedObject var contactViewModel: ContactViewModel
@State private var isFavoriteOpen = true
@ -30,7 +32,7 @@ struct ContactsInnerFragment: View {
var body: some View {
VStack(alignment: .leading) {
if !magicSearch.lastSearch.filter({ $0.friend?.starred == true }).isEmpty {
if !contactsManager.lastSearch.filter({ $0.friend?.starred == true }).isEmpty {
HStack(alignment: .center) {
Text("Favourites")
.default_text_style_800(styleSize: 16)

View file

@ -27,7 +27,6 @@ struct ContactsListBottomSheet: View {
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
@ObservedObject var magicSearch = MagicSearchSingleton.shared
@ObservedObject var contactViewModel: ContactViewModel
@State private var orientation = UIDevice.current.orientation
@ -61,7 +60,8 @@ struct ContactsListBottomSheet: View {
if contactViewModel.selectedFriend != nil {
contactViewModel.selectedFriend!.starred.toggle()
}
self.magicSearch.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
if #available(iOS 16.0, *) {
if idiom != .pad {

View file

@ -22,7 +22,7 @@ import linphonesw
struct ContactsListFragment: View {
@ObservedObject var magicSearch = MagicSearchSingleton.shared
@ObservedObject var contactsManager = ContactsManager.shared
@ObservedObject var contactViewModel: ContactViewModel
@ObservedObject var contactsListViewModel: ContactsListViewModel
@ -32,22 +32,22 @@ struct ContactsListFragment: View {
var body: some View {
VStack {
List {
ForEach(0..<magicSearch.lastSearch.count, id: \.self) { index in
ForEach(0..<contactsManager.lastSearch.count, id: \.self) { index in
Button {
} label: {
HStack {
if index == 0
|| magicSearch.lastSearch[index].friend?.name!.lowercased().folding(
|| contactsManager.lastSearch[index].friend?.name!.lowercased().folding(
options: .diacriticInsensitive,
locale: .current
).first
!= magicSearch.lastSearch[index-1].friend?.name!.lowercased().folding(
!= contactsManager.lastSearch[index-1].friend?.name!.lowercased().folding(
options: .diacriticInsensitive,
locale: .current
).first {
Text(
String(
(magicSearch.lastSearch[index].friend?.name!.uppercased().folding(
(contactsManager.lastSearch[index].friend?.name!.uppercased().folding(
options: .diacriticInsensitive,
locale: .current
).first)!))
@ -63,15 +63,17 @@ struct ContactsListFragment: View {
.padding(.trailing, 10)
}
if magicSearch.lastSearch[index].friend!.photo != nil && !magicSearch.lastSearch[index].friend!.photo!.isEmpty {
Avatar(friend: magicSearch.lastSearch[index].friend!, avatarSize: 45)
if index < contactsManager.avatarListModel.count
&& contactsManager.avatarListModel[index].friend!.photo != nil
&& !contactsManager.avatarListModel[index].friend!.photo!.isEmpty {
Avatar(contactAvatarModel: contactsManager.avatarListModel[index], avatarSize: 45)
} else {
Image("profil-picture-default")
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
}
Text((magicSearch.lastSearch[index].friend?.name)!)
Text((contactsManager.lastSearch[index].friend?.name)!)
.default_text_style(styleSize: 16)
.frame(maxWidth: .infinity, alignment: .leading)
.foregroundStyle(Color.orangeMain500)
@ -80,7 +82,7 @@ struct ContactsListFragment: View {
.simultaneousGesture(
LongPressGesture()
.onEnded { _ in
contactViewModel.selectedFriend = magicSearch.lastSearch[index].friend
contactViewModel.selectedFriend = contactsManager.lastSearch[index].friend
showingSheet.toggle()
}
)
@ -99,7 +101,7 @@ struct ContactsListFragment: View {
.listStyle(.plain)
.overlay(
VStack {
if magicSearch.lastSearch.isEmpty {
if contactsManager.lastSearch.isEmpty {
Spacer()
Image("illus-belledonne")
.resizable()

View file

@ -27,6 +27,8 @@ struct EditContactFragment: View {
@Environment(\.dismiss) var dismiss
var contactViewModel: ContactViewModel
@Binding var isShowEditContactFragment: Bool
@Binding var isShowDismissPopup: Bool
@ -129,7 +131,9 @@ struct EditContactFragment: View {
if editContactViewModel.selectedEditFriend != nil
&& editContactViewModel.selectedEditFriend!.photo != nil
&& !editContactViewModel.selectedEditFriend!.photo!.isEmpty && selectedImage == nil && !removedImage {
Avatar(friend: editContactViewModel.selectedEditFriend!, avatarSize: 100)
Avatar(contactAvatarModel: ContactAvatarModel(friend: editContactViewModel.selectedEditFriend!, withPresence: false), avatarSize: 100)
} else if selectedImage == nil {
Image("profil-picture-default")
.resizable()
@ -475,7 +479,7 @@ struct EditContactFragment: View {
)
if editContactViewModel.selectedEditFriend != nil && selectedImage == nil &&
!removedImage {
!removedImage && editContactViewModel.selectedEditFriend!.photo!.suffix(11) != "default.png" {
ContactsManager.shared.saveFriend(
result: String(editContactViewModel.selectedEditFriend!.photo!.dropFirst(6)),
contact: newContact,
@ -486,7 +490,7 @@ struct EditContactFragment: View {
image: selectedImage
?? ContactsManager.shared.textToImage(
firstName: editContactViewModel.firstName, lastName: editContactViewModel.lastName),
name: editContactViewModel.firstName
name: editContactViewModel.firstName
+ editContactViewModel.lastName
+ String(Int.random(in: 1...1000))
+ ((selectedImage == nil) ? "-default" : ""),
@ -495,6 +499,16 @@ struct EditContactFragment: View {
MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
if editContactViewModel.selectedEditFriend != nil && editContactViewModel.selectedEditFriend!.name != editContactViewModel.firstName + " " + editContactViewModel.lastName {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
let result = ContactsManager.shared.lastSearch.firstIndex(where: {
$0.friend!.name == newContact.firstName + " " + newContact.lastName
})
print("getFriendIndexWithFriendgetFriendIndexWithFriend \(newContact.firstName) \(newContact.lastName) \(result)")
contactViewModel.indexDisplayedFriend = result
}
}
delayColorDismiss()
if editContactViewModel.selectedEditFriend == nil {
withAnimation {
@ -508,5 +522,10 @@ struct EditContactFragment: View {
}
#Preview {
EditContactFragment(editContactViewModel: EditContactViewModel(), isShowEditContactFragment: .constant(false), isShowDismissPopup: .constant(false))
EditContactFragment(
editContactViewModel: EditContactViewModel(),
contactViewModel: ContactViewModel(),
isShowEditContactFragment: .constant(false),
isShowDismissPopup: .constant(false)
)
}

View file

@ -21,9 +21,9 @@ import SwiftUI
import linphonesw
struct FavoriteContactsListFragment: View {
@ObservedObject var magicSearch = MagicSearchSingleton.shared
@ObservedObject var contactsManager = ContactsManager.shared
@ObservedObject var contactViewModel: ContactViewModel
@ObservedObject var favoriteContactsListViewModel: FavoriteContactsListViewModel
@ -32,21 +32,21 @@ struct FavoriteContactsListFragment: View {
var body: some View {
ScrollView(.horizontal) {
HStack {
ForEach(0..<magicSearch.lastSearch.count, id: \.self) { index in
if magicSearch.lastSearch[index].friend != nil && magicSearch.lastSearch[index].friend!.starred == true {
ForEach(0..<contactsManager.lastSearch.count, id: \.self) { index in
if contactsManager.lastSearch[index].friend != nil && contactsManager.lastSearch[index].friend!.starred == true {
Button {
} label: {
VStack {
if magicSearch.lastSearch[index].friend!.photo != nil
&& !magicSearch.lastSearch[index].friend!.photo!.isEmpty {
Avatar(friend: magicSearch.lastSearch[index].friend!, avatarSize: 45)
if contactsManager.lastSearch[index].friend!.photo != nil
&& !contactsManager.lastSearch[index].friend!.photo!.isEmpty {
Avatar(contactAvatarModel: contactsManager.avatarListModel[index], avatarSize: 45)
} else {
Image("profil-picture-default")
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
}
Text((magicSearch.lastSearch[index].friend?.name)!)
Text((contactsManager.lastSearch[index].friend?.name)!)
.default_text_style(styleSize: 16)
.frame( maxWidth: .infinity, alignment: .center)
}
@ -54,7 +54,7 @@ struct FavoriteContactsListFragment: View {
.simultaneousGesture(
LongPressGesture()
.onEnded { _ in
contactViewModel.selectedFriend = magicSearch.lastSearch[index].friend
contactViewModel.selectedFriend = contactsManager.lastSearch[index].friend
showingSheet.toggle()
}
)

View file

@ -0,0 +1,121 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
import Foundation
import linphonesw
class ContactAvatarModel: ObservableObject {
let friend: Friend?
let withPresence: Bool?
@Published var lastPresenceInfo: String
@Published var presenceStatus: ConsolidatedPresence
private var friendDelegate: FriendDelegate?
init(friend: Friend?, withPresence: Bool?) {
self.friend = friend
self.withPresence = withPresence
if friend != nil &&
withPresence == true {
self.lastPresenceInfo = ""
self.presenceStatus = friend!.consolidatedPresence
if friend!.consolidatedPresence == .Online || friend!.consolidatedPresence == .Busy {
if friend!.consolidatedPresence == .Online || friend!.presenceModel!.latestActivityTimestamp != -1 {
self.lastPresenceInfo = friend!.consolidatedPresence == .Online ? "Online" : getCallTime(startDate: friend!.presenceModel!.latestActivityTimestamp)
} else {
self.lastPresenceInfo = "Away"
}
} else {
self.lastPresenceInfo = ""
}
if self.friendDelegate != nil {
self.friend!.removeDelegate(delegate: self.friendDelegate!)
self.friendDelegate = nil
}
addDelegate()
} else {
self.lastPresenceInfo = ""
self.presenceStatus = .Offline
}
}
func addDelegate() {
let newFriendDelegate = FriendDelegateStub(
onPresenceReceived: { (linphoneFriend: Friend) -> Void in
DispatchQueue.main.sync {
self.presenceStatus = linphoneFriend.consolidatedPresence
if linphoneFriend.consolidatedPresence == .Online || linphoneFriend.consolidatedPresence == .Busy {
if linphoneFriend.consolidatedPresence == .Online || linphoneFriend.presenceModel!.latestActivityTimestamp != -1 {
self.lastPresenceInfo = linphoneFriend.consolidatedPresence == .Online ? "Online" : self.getCallTime(startDate: linphoneFriend.presenceModel!.latestActivityTimestamp)
} else {
self.lastPresenceInfo = "Away"
}
} else {
self.lastPresenceInfo = ""
}
}
}
)
friendDelegate = newFriendDelegate
if friendDelegate != nil {
friend!.addDelegate(delegate: friendDelegate!)
}
}
func removeAllDelegate() {
if friendDelegate != nil {
presenceStatus = .Offline
friend!.removeDelegate(delegate: friendDelegate!)
friendDelegate = nil
}
}
func getCallTime(startDate: time_t) -> String {
let timeInterval = TimeInterval(startDate)
let myNSDate = Date(timeIntervalSince1970: timeInterval)
if Calendar.current.isDateInToday(myNSDate) {
let formatter = DateFormatter()
formatter.dateFormat = Locale.current.identifier == "fr_FR" ? "HH:mm" : "h:mm a"
return "Online today at " + formatter.string(from: myNSDate)
} else if Calendar.current.isDateInYesterday(myNSDate) {
let formatter = DateFormatter()
formatter.dateFormat = Locale.current.identifier == "fr_FR" ? "HH:mm" : "h:mm a"
return "Online yesterday at " + formatter.string(from: myNSDate)
} else if Calendar.current.isDate(myNSDate, equalTo: .now, toGranularity: .year) {
let formatter = DateFormatter()
formatter.dateFormat = Locale.current.identifier == "fr_FR" ? "dd/MM | HH:mm" : "MM/dd | h:mm a"
return "Online on " + formatter.string(from: myNSDate)
} else {
let formatter = DateFormatter()
formatter.dateFormat = Locale.current.identifier == "fr_FR" ? "dd/MM/yy | HH:mm" : "MM/dd/yy | h:mm a"
return "Online on " + formatter.string(from: myNSDate)
}
}
}

View file

@ -29,9 +29,5 @@ class ContactViewModel: ObservableObject {
var selectedFriendToShare: Friend?
var selectedFriendToDelete: Friend?
private var magicSearch = MagicSearchSingleton.shared
init() {
magicSearch.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)}
init() {}
}

View file

@ -20,6 +20,6 @@
import linphonesw
class ContactsListViewModel: ObservableObject {
init() {}
}

View file

@ -28,7 +28,7 @@ struct ContentView: View {
@ObservedObject private var coreContext = CoreContext.shared
@ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared
var contactManager = ContactsManager.shared
@ObservedObject var contactsManager = ContactsManager.shared
var magicSearch = MagicSearchSingleton.shared
@ObservedObject var contactViewModel: ContactViewModel
@ -146,7 +146,7 @@ struct ContentView: View {
Button {
isMenuOpen = false
magicSearch.allContact = true
magicSearch.searchForContacts(
MagicSearchSingleton.shared.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
} label: {
HStack {
@ -163,7 +163,7 @@ struct ContentView: View {
Button {
isMenuOpen = false
magicSearch.allContact = false
magicSearch.searchForContacts(
MagicSearchSingleton.shared.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
} label: {
HStack {
@ -219,7 +219,7 @@ struct ContentView: View {
if index == 0 {
magicSearch.currentFilter = ""
magicSearch.searchForContacts(
MagicSearchSingleton.shared.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
} else {
historyListViewModel.resetFilterCallLogs()
@ -256,7 +256,7 @@ struct ContentView: View {
.onChange(of: text) { newValue in
if index == 0 {
magicSearch.currentFilter = newValue
magicSearch.searchForContacts(
MagicSearchSingleton.shared.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
} else {
historyListViewModel.filterCallLogs(filter: text)
@ -284,7 +284,7 @@ struct ContentView: View {
}
.onChange(of: text) { newValue in
magicSearch.currentFilter = newValue
magicSearch.searchForContacts(
MagicSearchSingleton.shared.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
}
}
@ -428,7 +428,20 @@ struct ContentView: View {
.background(Color.gray100)
.ignoresSafeArea(.keyboard)
} else if self.index == 1 {
let fromAddressFriend = historyViewModel.displayedCall != nil ? contactsManager.getFriendWithAddress(address: historyViewModel.displayedCall!.fromAddress!) : nil
let toAddressFriend = historyViewModel.displayedCall != nil ? contactsManager.getFriendWithAddress(address: historyViewModel.displayedCall!.toAddress!) : nil
let addressFriend = historyViewModel.displayedCall != nil ? (historyViewModel.displayedCall!.dir == .Incoming ? fromAddressFriend : toAddressFriend) : nil
let contactAvatarModel = addressFriend != nil
? ContactsManager.shared.avatarListModel.first(where: {
($0.friend!.consolidatedPresence == .Online || $0.friend!.consolidatedPresence == .Busy)
&& $0.friend!.name == addressFriend!.name
&& $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly()
})
: ContactAvatarModel(friend: nil, withPresence: false)
HistoryContactFragment(
contactAvatarModel: contactAvatarModel!,
historyViewModel: historyViewModel,
historyListViewModel: historyListViewModel,
contactViewModel: contactViewModel,
@ -478,6 +491,7 @@ struct ContentView: View {
if isShowEditContactFragment {
EditContactFragment(
editContactViewModel: editContactViewModel,
contactViewModel: contactViewModel,
isShowEditContactFragment: $isShowEditContactFragment,
isShowDismissPopup: $isShowDismissPopup
)
@ -494,7 +508,7 @@ struct ContentView: View {
contactViewModel.selectedFriend != nil
? "Delete \(contactViewModel.selectedFriend!.name!)?"
: (contactViewModel.indexDisplayedFriend != nil
? "Delete \(magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.name!)?"
? "Delete \(contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.name!)?"
: "Error Name")),
content: Text("This contact will be deleted definitively."),
titleFirstButton: Text("Cancel"),
@ -514,9 +528,9 @@ struct ContentView: View {
withAnimation {
contactViewModel.indexDisplayedFriend = nil
}
magicSearch.lastSearch[tmpIndex!].friend!.remove()
contactsManager.lastSearch[tmpIndex!].friend!.remove()
}
magicSearch.searchForContacts(
MagicSearchSingleton.shared.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
self.isShowDeleteContactPopup.toggle()
})
@ -611,7 +625,7 @@ struct ContentView: View {
}
.onChange(of: scenePhase) { newPhase in
if newPhase == .active {
ContactsManager.shared.fetchContacts()
contactsManager.fetchContacts()
print("Active")
} else if newPhase == .inactive {
print("Inactive")

View file

@ -25,6 +25,9 @@ struct HistoryContactFragment: View {
@State private var orientation = UIDevice.current.orientation
@ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared
@ObservedObject var contactsManager = ContactsManager.shared
@ObservedObject var contactAvatarModel: ContactAvatarModel
@ObservedObject var historyViewModel: HistoryViewModel
@ObservedObject var historyListViewModel: HistoryListViewModel
@ObservedObject var contactViewModel: ContactViewModel
@ -66,14 +69,14 @@ struct HistoryContactFragment: View {
Spacer()
Menu {
let fromAddressFriend = historyViewModel.displayedCall != nil ? ContactsManager.shared.getFriendWithAddress(address: historyViewModel.displayedCall!.fromAddress!) : nil
let toAddressFriend = historyViewModel.displayedCall != nil ? ContactsManager.shared.getFriendWithAddress(address: historyViewModel.displayedCall!.toAddress!) : nil
let fromAddressFriend = historyViewModel.displayedCall != nil ? contactsManager.getFriendWithAddress(address: historyViewModel.displayedCall!.fromAddress!) : nil
let toAddressFriend = historyViewModel.displayedCall != nil ? contactsManager.getFriendWithAddress(address: historyViewModel.displayedCall!.toAddress!) : nil
let addressFriend = historyViewModel.displayedCall != nil ? (historyViewModel.displayedCall!.dir == .Incoming ? fromAddressFriend : toAddressFriend) : nil
Button {
isMenuOpen = false
if ContactsManager.shared.getFriendWithAddress(
if contactsManager.getFriendWithAddress(
address: historyViewModel.displayedCall != nil && historyViewModel.displayedCall!.dir == .Outgoing
? historyViewModel.displayedCall!.toAddress!
: historyViewModel.displayedCall!.fromAddress!
@ -82,7 +85,7 @@ struct HistoryContactFragment: View {
? historyViewModel.displayedCall!.toAddress!
: historyViewModel.displayedCall!.fromAddress!
let friendIndex = MagicSearchSingleton.shared.lastSearch.firstIndex(
let friendIndex = contactsManager.lastSearch.firstIndex(
where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressCall.asStringUriOnly()})})
if friendIndex != nil {
@ -190,40 +193,19 @@ struct HistoryContactFragment: View {
VStack(spacing: 0) {
VStack(spacing: 0) {
let fromAddressFriend = historyViewModel.displayedCall != nil ? ContactsManager.shared.getFriendWithAddress(address: historyViewModel.displayedCall!.fromAddress!) : nil
let toAddressFriend = historyViewModel.displayedCall != nil ? ContactsManager.shared.getFriendWithAddress(address: historyViewModel.displayedCall!.toAddress!) : nil
let fromAddressFriend = historyViewModel.displayedCall != nil ? contactsManager.getFriendWithAddress(address: historyViewModel.displayedCall!.fromAddress!) : nil
let toAddressFriend = historyViewModel.displayedCall != nil ? contactsManager.getFriendWithAddress(address: historyViewModel.displayedCall!.toAddress!) : nil
let addressFriend = historyViewModel.displayedCall != nil ? (historyViewModel.displayedCall!.dir == .Incoming ? fromAddressFriend : toAddressFriend) : nil
if historyViewModel.displayedCall != nil
&& addressFriend != nil
&& addressFriend!.photo != nil
&& !addressFriend!.photo!.isEmpty {
AsyncImage(
url: ContactsManager.shared.getImagePath(
friendPhotoPath: addressFriend!.photo!)) { image in
switch image {
case .empty:
ProgressView()
.frame(width: 100, height: 100)
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.clipShape(Circle())
case .failure:
Image("profil-picture-default")
.resizable()
.frame(width: 100, height: 100)
.clipShape(Circle())
@unknown default:
EmptyView()
}
}
Avatar(contactAvatarModel: contactAvatarModel, avatarSize: 100)
} else if historyViewModel.displayedCall != nil {
if historyViewModel.displayedCall!.dir == .Outgoing && historyViewModel.displayedCall!.toAddress != nil {
if historyViewModel.displayedCall!.toAddress!.displayName != nil {
Image(uiImage: ContactsManager.shared.textToImage(
Image(uiImage: contactsManager.textToImage(
firstName: historyViewModel.displayedCall!.toAddress!.displayName!,
lastName: historyViewModel.displayedCall!.toAddress!.displayName!.components(separatedBy: " ").count > 1
? historyViewModel.displayedCall!.toAddress!.displayName!.components(separatedBy: " ")[1]
@ -252,7 +234,7 @@ struct HistoryContactFragment: View {
.frame(maxWidth: .infinity)
.frame(height: 20)
} else {
Image(uiImage: ContactsManager.shared.textToImage(
Image(uiImage: contactsManager.textToImage(
firstName: historyViewModel.displayedCall!.toAddress!.username ?? "Username Error",
lastName: historyViewModel.displayedCall!.toAddress!.username!.components(separatedBy: " ").count > 1
? historyViewModel.displayedCall!.toAddress!.username!.components(separatedBy: " ")[1]
@ -284,7 +266,7 @@ struct HistoryContactFragment: View {
} else if historyViewModel.displayedCall!.fromAddress != nil {
if historyViewModel.displayedCall!.fromAddress!.displayName != nil {
Image(uiImage: ContactsManager.shared.textToImage(
Image(uiImage: contactsManager.textToImage(
firstName: historyViewModel.displayedCall!.fromAddress!.displayName!,
lastName: historyViewModel.displayedCall!.fromAddress!.displayName!.components(separatedBy: " ").count > 1
? historyViewModel.displayedCall!.fromAddress!.displayName!.components(separatedBy: " ")[1]
@ -313,7 +295,7 @@ struct HistoryContactFragment: View {
.frame(maxWidth: .infinity)
.frame(height: 20)
} else {
Image(uiImage: ContactsManager.shared.textToImage(
Image(uiImage: contactsManager.textToImage(
firstName: historyViewModel.displayedCall!.fromAddress!.username ?? "Username Error",
lastName: historyViewModel.displayedCall!.fromAddress!.username!.components(separatedBy: " ").count > 1
? historyViewModel.displayedCall!.fromAddress!.username!.components(separatedBy: " ")[1]
@ -370,13 +352,15 @@ struct HistoryContactFragment: View {
.padding(.top, 5)
}
Text("En ligne")
.foregroundStyle(Color.greenSuccess500)
Text(contactAvatarModel.lastPresenceInfo)
.foregroundStyle(contactAvatarModel.lastPresenceInfo == "Online"
? Color.greenSuccess500
: Color.orangeWarning600)
.multilineTextAlignment(.center)
.default_text_style_300(styleSize: 12)
.frame(maxWidth: .infinity)
.frame(height: 20)
.padding(.top, 5)
.padding(.top, 5)
}
}
.frame(minHeight: 150)
@ -508,7 +492,11 @@ struct HistoryContactFragment: View {
.frame(maxWidth: .infinity, alignment: .leading)
Text(historyListViewModel.getCallTime(startDate: callLogsFilter[index].startDate))
.foregroundStyle(callLogsFilter[index].status != .Success ? Color.redDanger500 : Color.grayMain2c600)
.foregroundStyle(
callLogsFilter[index].status != .Success
? Color.redDanger500
: Color.grayMain2c600
)
.default_text_style_300(styleSize: 12)
.frame(maxWidth: .infinity, alignment: .leading)
}
@ -546,7 +534,8 @@ struct HistoryContactFragment: View {
#Preview {
HistoryContactFragment(
historyViewModel: HistoryViewModel(),
contactAvatarModel: ContactAvatarModel(friend: nil, withPresence: false),
historyViewModel: HistoryViewModel(),
historyListViewModel: HistoryListViewModel(),
contactViewModel: ContactViewModel(),
editContactViewModel: EditContactViewModel(),

View file

@ -27,6 +27,7 @@ struct HistoryListBottomSheet: View {
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
@ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared
@ObservedObject var contactsManager = ContactsManager.shared
@ObservedObject var historyViewModel: HistoryViewModel
@ObservedObject var contactViewModel: ContactViewModel
@ -76,7 +77,7 @@ struct HistoryListBottomSheet: View {
index = 0
if ContactsManager.shared.getFriendWithAddress(
if contactsManager.getFriendWithAddress(
address: historyViewModel.selectedCall != nil && historyViewModel.selectedCall!.dir == .Outgoing
? historyViewModel.selectedCall!.toAddress!
: historyViewModel.selectedCall!.fromAddress!
@ -85,7 +86,7 @@ struct HistoryListBottomSheet: View {
? historyViewModel.selectedCall!.toAddress!
: historyViewModel.selectedCall!.fromAddress!
let friendIndex = MagicSearchSingleton.shared.lastSearch.firstIndex(where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressCall.asStringUriOnly()})})
let friendIndex = contactsManager.lastSearch.firstIndex(where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressCall.asStringUriOnly()})})
if friendIndex != nil {
withAnimation {
contactViewModel.indexDisplayedFriend = friendIndex
@ -105,7 +106,7 @@ struct HistoryListBottomSheet: View {
}
} label: {
HStack {
if ContactsManager.shared.getFriendWithAddress(
if contactsManager.getFriendWithAddress(
address: historyViewModel.selectedCall != nil && historyViewModel.selectedCall!.dir == .Outgoing
? historyViewModel.selectedCall!.toAddress!
: historyViewModel.selectedCall!.fromAddress!

View file

@ -22,6 +22,8 @@ import linphonesw
struct HistoryListFragment: View {
@ObservedObject var contactsManager = ContactsManager.shared
@ObservedObject var historyListViewModel: HistoryListViewModel
@ObservedObject var historyViewModel: HistoryViewModel
@ -34,37 +36,24 @@ struct HistoryListFragment: View {
Button {
} label: {
HStack {
let fromAddressFriend = ContactsManager.shared.getFriendWithAddress(address: historyListViewModel.callLogs[index].fromAddress!)
let toAddressFriend = ContactsManager.shared.getFriendWithAddress(address: historyListViewModel.callLogs[index].toAddress!)
let fromAddressFriend = contactsManager.getFriendWithAddress(address: historyListViewModel.callLogs[index].fromAddress!)
let toAddressFriend = contactsManager.getFriendWithAddress(address: historyListViewModel.callLogs[index].toAddress!)
let addressFriend = historyListViewModel.callLogs[index].dir == .Incoming ? fromAddressFriend : toAddressFriend
let contactAvatarModel = addressFriend != nil
? ContactsManager.shared.avatarListModel.first(where: {
($0.friend!.consolidatedPresence == .Online || $0.friend!.consolidatedPresence == .Busy)
&& $0.friend!.name == addressFriend!.name
&& $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly()
})
: ContactAvatarModel(friend: nil, withPresence: false)
if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty {
AsyncImage(url:
ContactsManager.shared.getImagePath(
friendPhotoPath: addressFriend!.photo!)) { image in
switch image {
case .empty:
ProgressView()
.frame(width: 45, height: 45)
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 45, height: 45)
.clipShape(Circle())
case .failure:
Image("profil-picture-default")
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
@unknown default:
EmptyView()
}
}
Avatar(contactAvatarModel: contactAvatarModel!, avatarSize: 45)
} else {
if historyListViewModel.callLogs[index].dir == .Outgoing && historyListViewModel.callLogs[index].toAddress != nil {
if historyListViewModel.callLogs[index].toAddress!.displayName != nil {
Image(uiImage: ContactsManager.shared.textToImage(
Image(uiImage: contactsManager.textToImage(
firstName: historyListViewModel.callLogs[index].toAddress!.displayName!,
lastName: historyListViewModel.callLogs[index].toAddress!.displayName!.components(separatedBy: " ").count > 1
? historyListViewModel.callLogs[index].toAddress!.displayName!.components(separatedBy: " ")[1]
@ -74,7 +63,7 @@ struct HistoryListFragment: View {
.clipShape(Circle())
} else {
Image(uiImage: ContactsManager.shared.textToImage(
Image(uiImage: contactsManager.textToImage(
firstName: historyListViewModel.callLogs[index].toAddress!.username ?? "Username Error",
lastName: historyListViewModel.callLogs[index].toAddress!.username!.components(separatedBy: " ").count > 1
? historyListViewModel.callLogs[index].toAddress!.username!.components(separatedBy: " ")[1]
@ -86,7 +75,7 @@ struct HistoryListFragment: View {
} else if historyListViewModel.callLogs[index].fromAddress != nil {
if historyListViewModel.callLogs[index].fromAddress!.displayName != nil {
Image(uiImage: ContactsManager.shared.textToImage(
Image(uiImage: contactsManager.textToImage(
firstName: historyListViewModel.callLogs[index].fromAddress!.displayName!,
lastName: historyListViewModel.callLogs[index].fromAddress!.displayName!.components(separatedBy: " ").count > 1
? historyListViewModel.callLogs[index].fromAddress!.displayName!.components(separatedBy: " ")[1]
@ -95,7 +84,7 @@ struct HistoryListFragment: View {
.frame(width: 45, height: 45)
.clipShape(Circle())
} else {
Image(uiImage: ContactsManager.shared.textToImage(
Image(uiImage: contactsManager.textToImage(
firstName: historyListViewModel.callLogs[index].fromAddress!.username ?? "Username Error",
lastName: historyListViewModel.callLogs[index].fromAddress!.username!.components(separatedBy: " ").count > 1
? historyListViewModel.callLogs[index].fromAddress!.username!.components(separatedBy: " ")[1]
@ -110,8 +99,8 @@ struct HistoryListFragment: View {
VStack(spacing: 0) {
Spacer()
let fromAddressFriend = ContactsManager.shared.getFriendWithAddress(address: historyListViewModel.callLogs[index].fromAddress!)
let toAddressFriend = ContactsManager.shared.getFriendWithAddress(address: historyListViewModel.callLogs[index].toAddress!)
let fromAddressFriend = contactsManager.getFriendWithAddress(address: historyListViewModel.callLogs[index].fromAddress!)
let toAddressFriend = contactsManager.getFriendWithAddress(address: historyListViewModel.callLogs[index].toAddress!)
let addressFriend = historyListViewModel.callLogs[index].dir == .Incoming ? fromAddressFriend : toAddressFriend
if addressFriend != nil {

View file

@ -53,9 +53,9 @@ class HistoryListViewModel: ObservableObject {
DispatchQueue.main.async {
self.coreDelegate = CoreDelegateStub(
onCallLogUpdated: { (_: Core, _: CallLog) -> Void in
DispatchQueue.main.async {
DispatchQueue.main.sync {
let account = core.defaultAccount
let logs = account?.callLogs != nil ? account!.callLogs : core.callLogs
let logs = account != nil ? account!.callLogs : core.callLogs
self.callLogs.removeAll()
self.callLogsTmp.removeAll()
@ -71,7 +71,6 @@ class HistoryListViewModel: ObservableObject {
core.addDelegate(delegate: self.coreDelegate!)
}
}
}
}

View file

@ -1,9 +1,21 @@
//
// ToastViewModel.swift
// Linphone
//
// Created by Benoît Martins on 20/11/2023.
//
/*
* 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 <http://www.gnu.org/licenses/>.
*/
import Foundation

View file

@ -22,84 +22,45 @@ import linphonesw
struct Avatar: View {
var friend: Friend
let avatarSize: CGFloat
@State private var friendDelegate: FriendDelegate?
@State private var presenceImage = ""
var body: some View {
AsyncImage(url: ContactsManager.shared.getImagePath(friendPhotoPath: friend.photo!)) { image in
switch image {
case .empty:
ProgressView()
.frame(width: avatarSize, height: avatarSize)
case .success(let image):
ZStack {
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: avatarSize, height: avatarSize)
.clipShape(Circle())
HStack {
Spacer()
VStack {
Spacer()
if !friend.addresses.isEmpty {
if presenceImage.isEmpty
&& (friend.consolidatedPresence == ConsolidatedPresence.Online || friend.consolidatedPresence == ConsolidatedPresence.Busy) {
Image(friend.consolidatedPresence == ConsolidatedPresence.Online ? "presence-online" : "presence-busy")
.resizable()
.frame(width: avatarSize/4, height: avatarSize/4)
.padding(.trailing, avatarSize == 45 ? 1 : 3)
.padding(.bottom, avatarSize == 45 ? 1 : 3)
} else if !presenceImage.isEmpty {
Image(presenceImage)
.resizable()
.frame(width: avatarSize/4, height: avatarSize/4)
.padding(.trailing, avatarSize == 45 ? 1 : 3)
.padding(.bottom, avatarSize == 45 ? 1 : 3)
}
}
}
}
.frame(width: avatarSize, height: avatarSize)
}
.onAppear {
addDelegate()
@ObservedObject var contactAvatarModel: ContactAvatarModel
let avatarSize: CGFloat
var body: some View {
AsyncImage(url: ContactsManager.shared.getImagePath(friendPhotoPath: contactAvatarModel.friend!.photo!)) { image in
switch image {
case .empty:
ProgressView()
.frame(width: avatarSize, height: avatarSize)
case .success(let image):
ZStack {
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: avatarSize, height: avatarSize)
.clipShape(Circle())
HStack {
Spacer()
VStack {
Spacer()
if contactAvatarModel.presenceStatus == .Online || contactAvatarModel.presenceStatus == .Busy {
Image(contactAvatarModel.presenceStatus == .Online ? "presence-online" : "presence-busy")
.resizable()
.frame(width: avatarSize/4, height: avatarSize/4)
.padding(.trailing, avatarSize == 45 ? 1 : 3)
.padding(.bottom, avatarSize == 45 ? 1 : 3)
}
}
}
.frame(width: avatarSize, height: avatarSize)
}
.onDisappear {
removeAllDelegate()
}
case .failure:
Image("profil-picture-default")
.resizable()
.frame(width: avatarSize, height: avatarSize)
.clipShape(Circle())
@unknown default:
EmptyView()
}
}
}
func addDelegate() {
let newFriendDelegate = FriendDelegateStub(
onPresenceReceived: { (linphoneFriend: Friend) -> Void in
self.presenceImage = linphoneFriend.consolidatedPresence == ConsolidatedPresence.Online ? "presence-online" : "presence-busy"
}
)
friendDelegate = newFriendDelegate
if friendDelegate != nil {
friend.addDelegate(delegate: friendDelegate!)
}
}
func removeAllDelegate() {
if friendDelegate != nil {
presenceImage = ""
friend.removeDelegate(delegate: friendDelegate!)
friendDelegate = nil
case .failure:
Image("profil-picture-default")
.resizable()
.frame(width: avatarSize, height: avatarSize)
.clipShape(Circle())
@unknown default:
EmptyView()
}
}
}
}

View file

@ -28,7 +28,7 @@ extension Int {
public func convertDurationToString() -> String {
var duration = ""
let (hour, minute, second) = self.hmsFrom()
if (hour > 0) {
if hour > 0 {
duration = self.getHour(hour: hour)
}
return "\(duration)\(self.getMinute(minute: minute))\(self.getSecond(second: second))"
@ -36,18 +36,18 @@ extension Int {
private func getHour(hour: Int) -> String {
var duration = "\(hour):"
if (hour < 10) {
if hour < 10 {
duration = "0\(hour):"
}
return duration
}
private func getMinute(minute: Int) -> String {
if (minute == 0) {
if minute == 0 {
return "00:"
}
if (minute < 10) {
if minute < 10 {
return "0\(minute):"
}
@ -55,11 +55,11 @@ extension Int {
}
private func getSecond(second: Int) -> String {
if (second == 0){
if second == 0 {
return "00"
}
if (second < 10) {
if second < 10 {
return "0\(second)"
}
return "\(second)"

View file

@ -23,6 +23,7 @@ final class MagicSearchSingleton: ObservableObject {
static let shared = MagicSearchSingleton()
private var coreContext = CoreContext.shared
private var contactsManager = ContactsManager.shared
private var magicSearch: MagicSearch!
@ -31,8 +32,6 @@ final class MagicSearchSingleton: ObservableObject {
var needUpdateLastSearchContacts = false
@Published var lastSearch: [SearchResult] = []
private var limitSearchToLinphoneAccounts = true
@Published var allContact = false
@ -47,7 +46,23 @@ final class MagicSearchSingleton: ObservableObject {
self.magicSearch.publisher?.onSearchResultsReceived?.postOnMainQueue { (magicSearch: MagicSearch) in
self.needUpdateLastSearchContacts = true
self.lastSearch = magicSearch.lastSearch
self.contactsManager.lastSearch = magicSearch.lastSearch.sorted(by: {
$0.friend!.name!.lowercased().folding(options: .diacriticInsensitive, locale: .current)
<
$1.friend!.name!.lowercased().folding(options: .diacriticInsensitive, locale: .current)
})
self.contactsManager.avatarListModel.forEach { contactAvatarModel in
contactAvatarModel.removeAllDelegate()
}
self.contactsManager.avatarListModel.removeAll()
self.contactsManager.lastSearch.forEach { searchResult in
if searchResult.friend != nil {
self.contactsManager.avatarListModel.append(ContactAvatarModel(friend: searchResult.friend!, withPresence: true))
}
}
}
}
}
@ -75,4 +90,28 @@ final class MagicSearchSingleton: ObservableObject {
aggregation: MagicSearch.Aggregation.Friend)
}
}
func searchForContactsWithResult(sourceFlags: Int) {
coreContext.doOnCoreQueue { _ in
var needResetCache = false
DispatchQueue.main.sync {
if let oldFilter = self.previousFilter {
if oldFilter.count > self.currentFilter.count || oldFilter != self.currentFilter {
needResetCache = true
}
}
self.previousFilter = self.currentFilter
}
if needResetCache {
self.magicSearch.resetSearchCache()
}
self.magicSearch.getContactsListAsync(
filter: self.currentFilter,
domain: self.allContact ? "" : self.domainDefaultAccount,
sourceFlags: sourceFlags,
aggregation: MagicSearch.Aggregation.Friend)
}
}
}