Refactored HistoryView

This commit is contained in:
Benoit Martins 2025-06-03 11:33:56 +02:00
parent 865f889a3c
commit f534ccb560
28 changed files with 741 additions and 629 deletions

View file

@ -42,7 +42,14 @@ final class ContactsManager: ObservableObject {
@Published var lastSearch: [SearchResult] = []
@Published var lastSearchSuggestions: [SearchResult] = []
@Published var avatarListModel: [ContactAvatarModel] = []
@Published var avatarListModel: [ContactAvatarModel] = [] {
didSet {
setupSubscriptions()
}
}
@Published var starredChangeTrigger = UUID()
private var cancellables = Set<AnyCancellable>()
private var coreDelegate: CoreDelegate?
private var friendListDelegate: FriendListDelegate?
@ -97,6 +104,7 @@ final class ContactsManager: ObservableObject {
print("\(#function) - failed to request access", error)
self.addFriendListDelegate()
self.addCoreDelegate(core: core)
MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
return
}
if granted {
@ -238,9 +246,15 @@ final class ContactsManager: ObservableObject {
self.saveFriend(result: result, contact: contact, existingFriend: existingFriend) { resultFriend in
if resultFriend != nil {
if linphoneFriend && existingFriend == nil {
_ = self.linphoneFriendList?.addFriend(linphoneFriend: resultFriend!)
if let linphoneFL = self.linphoneFriendList {
_ = linphoneFL.addFriend(linphoneFriend: resultFriend!)
linphoneFL.updateSubscriptions()
}
} else if existingFriend == nil {
_ = self.friendList?.addLocalFriend(linphoneFriend: resultFriend!)
if let friendListTmp = self.friendList {
_ = friendListTmp.addLocalFriend(linphoneFriend: resultFriend!)
friendListTmp.updateSubscriptions()
}
}
}
completion()
@ -496,6 +510,17 @@ final class ContactsManager: ObservableObject {
core.addDelegate(delegate: self.coreDelegate!)
}
}
private func setupSubscriptions() {
cancellables.removeAll()
for contact in avatarListModel {
contact.$starred
.sink { [weak self] _ in
self?.starredChangeTrigger = UUID() // 🔁 Déclenche le refresh de la vue
}
.store(in: &cancellables)
}
}
}
struct PhoneNumber {

View file

@ -44,7 +44,7 @@ struct ContactFragment: View {
.presentationDetents([.fraction(0.2)])
}
.sheet(isPresented: $showShareSheet) {
ShareSheet(friendToShare: ContactsManager.shared.lastSearch[SharedMainViewModel.shared.indexDisplayedFriend!].friend!)
ShareSheet(friendToShare: contactAvatarModel)
.presentationDetents([.medium])
.edgesIgnoringSafeArea(.bottom)
}
@ -54,7 +54,7 @@ struct ContactFragment: View {
ContactListBottomSheet(contactsListViewModel: contactsListViewModel, showingSheet: $showingSheet)
} onDismiss: {}
.sheet(isPresented: $showShareSheet) {
ShareSheet(friendToShare: ContactsManager.shared.lastSearch[SharedMainViewModel.shared.indexDisplayedFriend!].friend!)
ShareSheet(friendToShare: contactAvatarModel)
.edgesIgnoringSafeArea(.bottom)
}
}

View file

@ -106,7 +106,7 @@ struct ContactInnerActionsFragment: View {
showingSheet.toggle()
}
if contactAvatarModel.friend != nil && !contactAvatarModel.friend!.phoneNumbers.isEmpty
if !contactAvatarModel.phoneNumbersWithLabel.isEmpty
|| index < contactAvatarModel.addresses.count - 1 {
VStack {
Divider()
@ -114,22 +114,22 @@ struct ContactInnerActionsFragment: View {
.padding(.horizontal)
}
}
if contactAvatarModel.friend != nil {
ForEach(0..<contactAvatarModel.friend!.phoneNumbers.count, id: \.self) { index in
ForEach(contactAvatarModel.phoneNumbersWithLabel.indices, id: \.self) { index in
let entry = contactAvatarModel.phoneNumbersWithLabel[index]
HStack {
HStack {
VStack {
if contactAvatarModel.friend!.phoneNumbersWithLabel[index].label != nil
&& !contactAvatarModel.friend!.phoneNumbersWithLabel[index].label!.isEmpty {
Text(String(localized: "phone_number") + " \(contactAvatarModel.friend!.phoneNumbersWithLabel[index].label!)) :")
if !entry.label.isEmpty {
Text(String(localized: "phone_number") + " (\(entry.label)):")
.default_text_style_700(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
} else {
Text(String(localized: "phone_number") + " :")
Text(String(localized: "phone_number") + ":")
.default_text_style_700(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
}
Text(contactAvatarModel.friend!.phoneNumbersWithLabel[index].phoneNumber)
Text(entry.phoneNumber)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
@ -142,19 +142,17 @@ struct ContactInnerActionsFragment: View {
}
.background(.white)
.onLongPressGesture(minimumDuration: 0.2) {
contactsListViewModel.stringToCopy =
contactAvatarModel.friend!.phoneNumbersWithLabel[index].phoneNumber
contactsListViewModel.stringToCopy = entry.phoneNumber
showingSheet.toggle()
}
if index < contactAvatarModel.friend!.phoneNumbers.count - 1 {
if index < contactAvatarModel.phoneNumbersWithLabel.count - 1 {
VStack {
Divider()
}
.padding(.horizontal)
}
}
}
}
.background(.white)
.cornerRadius(15)
@ -163,29 +161,20 @@ struct ContactInnerActionsFragment: View {
.transition(.move(edge: .top))
}
if contactAvatarModel.friend != nil && (contactAvatarModel.friend!.organization != nil
&& !contactAvatarModel.friend!.organization!.isEmpty)
|| (contactAvatarModel.friend!.jobTitle != nil
&& !contactAvatarModel.friend!.jobTitle!.isEmpty) {
if !contactAvatarModel.organization.isEmpty || !contactAvatarModel.jobTitle.isEmpty {
VStack {
if contactAvatarModel.friend!.organization != nil
&& !contactAvatarModel.friend!.organization!.isEmpty {
Text(.init(String(format:"**%@ :** %@", String(localized: "contact_editor_company"), contactAvatarModel.friend!.organization!)))
if !contactAvatarModel.organization.isEmpty {
Text(.init(String(format:"**%@ :** %@", String(localized: "contact_editor_company"), contactAvatarModel.organization)))
.default_text_style(styleSize: 14)
.padding(.vertical, 15)
.padding(.horizontal, 20)
.frame(maxWidth: .infinity, alignment: .leading)
}
if contactAvatarModel.friend!.jobTitle != nil
&& !contactAvatarModel.friend!.jobTitle!.isEmpty {
Text(.init(String(format:"**%@ :** %@", String(localized: "contact_editor_job_title"), contactAvatarModel.friend!.jobTitle!)))
if !contactAvatarModel.jobTitle.isEmpty {
Text(.init(String(format:"**%@ :** %@", String(localized: "contact_editor_job_title"), contactAvatarModel.jobTitle)))
.default_text_style(styleSize: 14)
.padding(.top,
contactAvatarModel.friend!.organization != nil
&& !contactAvatarModel.friend!.organization!.isEmpty
? 0 : 15
)
.padding(.top, !contactAvatarModel.organization.isEmpty ? 0 : 15)
.padding(.bottom, 15)
.padding(.horizontal, 20)
.frame(maxWidth: .infinity, alignment: .leading)
@ -238,7 +227,7 @@ struct ContactInnerActionsFragment: View {
}
} else {
NavigationLink(destination: EditContactFragment(
friend: contactAvatarModel.friend!,
contactAvatarModel: contactAvatarModel,
isShowEditContactFragment: $isShowEditContactFragmentInContactDetails,
isShowDismissPopup: $isShowDismissPopup)) {
HStack {
@ -272,20 +261,16 @@ struct ContactInnerActionsFragment: View {
.padding(.horizontal)
Button {
if contactAvatarModel.friend != nil {
contactAvatarModel.friend!.edit()
contactAvatarModel.friend!.starred.toggle()
contactAvatarModel.friend!.done()
}
contactsListViewModel.toggleStarredSelectedFriend()
} label: {
HStack {
Image(contactAvatarModel.friend != nil && contactAvatarModel.friend!.starred == true ? "heart-fill" : "heart")
.renderingMode(.template)
.resizable()
.foregroundStyle(contactAvatarModel.friend != nil && contactAvatarModel.friend!.starred == true ? Color.redDanger500 : Color.grayMain2c500)
.frame(width: 25, height: 25)
.padding(.all, 10)
Text(contactAvatarModel.friend != nil && contactAvatarModel.friend!.starred == true
Image(contactAvatarModel.starred == true ? "heart-fill" : "heart")
.renderingMode(.template)
.resizable()
.foregroundStyle(contactAvatarModel.starred == true ? Color.redDanger500 : Color.grayMain2c500)
.frame(width: 25, height: 25)
.padding(.all, 10)
Text(contactAvatarModel.starred == true
? "contact_details_remove_from_favourites"
: "contact_details_add_to_favourites")
.default_text_style(styleSize: 14)

View file

@ -65,7 +65,7 @@ struct ContactInnerFragment: View {
.padding(.leading, -10)
.onTapGesture {
withAnimation {
SharedMainViewModel.shared.indexDisplayedFriend = nil
SharedMainViewModel.shared.displayedFriend = nil
}
}
}
@ -86,7 +86,7 @@ struct ContactInnerFragment: View {
})
} else {
NavigationLink(destination: EditContactFragment(
friend: contactAvatarModel.friend,
contactAvatarModel: contactAvatarModel,
isShowEditContactFragment: $isShowEditContactFragmentInContactDetails,
isShowDismissPopup: $isShowDismissPopup)) {
Image("pencil-simple")
@ -114,7 +114,7 @@ struct ContactInnerFragment: View {
VStack(spacing: 0) {
VStack(spacing: 0) {
VStack(spacing: 0) {
if SharedMainViewModel.shared.indexDisplayedFriend != nil {
if SharedMainViewModel.shared.displayedFriend != nil {
Avatar(contactAvatarModel: contactAvatarModel, avatarSize: 100)
Text(contactAvatarModel.name)

View file

@ -33,7 +33,7 @@ struct ContactsInnerFragment: View {
var body: some View {
VStack(alignment: .leading) {
if !contactsManager.avatarListModel.filter({ $0.friend?.starred == true }).isEmpty {
if contactsManager.avatarListModel.contains(where: { $0.starred }) {
HStack(alignment: .center) {
Text("contacts_list_favourites_title")
.default_text_style_800(styleSize: 16)

View file

@ -57,13 +57,7 @@ struct ContactsListBottomSheet: View {
Spacer()
Button {
if contactsListViewModel.selectedFriend != nil {
contactsListViewModel.selectedFriend!.edit()
contactsListViewModel.selectedFriend!.starred.toggle()
contactsListViewModel.selectedFriend!.done()
}
MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
self.contactsListViewModel.toggleStarredSelectedFriend()
if #available(iOS 16.0, *) {
if idiom != .pad {

View file

@ -31,73 +31,81 @@ struct ContactsListFragment: View {
var startCallFunc: (_ addr: Address) -> Void
var body: some View {
ForEach(0..<contactsManager.avatarListModel.count, id: \.self) { index in
ForEach(Array(contactsManager.avatarListModel.enumerated()), id: \.element.id) { index, contactAvatarModel in
ContactRow(contactAvatarModel: contactAvatarModel, index: index, showingSheet: $showingSheet, startCallFunc: startCallFunc)
}
}
}
struct ContactRow: View {
@ObservedObject var contactsManager = ContactsManager.shared
@EnvironmentObject var contactsListViewModel: ContactsListViewModel
@ObservedObject var contactAvatarModel: ContactAvatarModel
let index: Int
@Binding var showingSheet: Bool
var startCallFunc: (_ addr: Address) -> Void
var body: some View {
HStack {
HStack {
HStack {
if index == 0
|| contactsManager.avatarListModel[index].name.lowercased().folding(
options: .diacriticInsensitive,
locale: .current
).first
!= contactsManager.avatarListModel[index-1].name.lowercased().folding(
options: .diacriticInsensitive,
locale: .current
).first {
Text(
String(
(contactsManager.avatarListModel[index].name.uppercased().folding(
options: .diacriticInsensitive,
locale: .current
).first)!))
if index <= 0
|| (index < contactsManager.avatarListModel.count && contactAvatarModel.name.lowercased().folding(
options: .diacriticInsensitive,
locale: .current
).first
!= contactsManager.avatarListModel[index-1].name.lowercased().folding(
options: .diacriticInsensitive,
locale: .current
).first) {
Text(
String(
(contactAvatarModel.name.uppercased().folding(
options: .diacriticInsensitive,
locale: .current
).first) ?? "?"))
.contact_text_style_500(styleSize: 20)
.frame(width: 18)
.padding(.leading, -5)
.padding(.trailing, 10)
} else {
Text("")
.contact_text_style_500(styleSize: 20)
.frame(width: 18)
.padding(.leading, -5)
.padding(.trailing, 10)
} else {
Text("")
.contact_text_style_500(styleSize: 20)
.frame(width: 18)
.padding(.leading, -5)
.padding(.trailing, 10)
}
if index < contactsManager.avatarListModel.count
&& contactsManager.avatarListModel[index].friend!.photo != nil
&& !contactsManager.avatarListModel[index].friend!.photo!.isEmpty {
Avatar(contactAvatarModel: contactsManager.avatarListModel[index], avatarSize: 50)
} else {
Image("profil-picture-default")
.resizable()
.frame(width: 50, height: 50)
.clipShape(Circle())
}
Text(contactsManager.avatarListModel[index].name)
.default_text_style(styleSize: 16)
.frame(maxWidth: .infinity, alignment: .leading)
.foregroundStyle(Color.orangeMain500)
}
}
.frame(height: 50)
.buttonStyle(.borderless)
.listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 20))
.listRowSeparator(.hidden)
.background(.white)
.onTapGesture {
withAnimation {
SharedMainViewModel.shared.indexDisplayedFriend = index
}
if index < contactsManager.avatarListModel.count && contactsManager.avatarListModel[index].friend != nil
&& contactsManager.avatarListModel[index].friend!.address != nil {
startCallFunc(contactsManager.avatarListModel[index].friend!.address!)
}
Avatar(contactAvatarModel: contactAvatarModel, avatarSize: 50)
Text(contactAvatarModel.name)
.default_text_style(styleSize: 16)
.frame(maxWidth: .infinity, alignment: .leading)
.foregroundStyle(Color.orangeMain500)
}
.onLongPressGesture(minimumDuration: 0.2) {
if index < contactsManager.avatarListModel.count && contactsManager.avatarListModel[index].friend != nil {
contactsListViewModel.selectedFriend = contactsManager.avatarListModel[index].friend
showingSheet.toggle()
}
}
.frame(height: 50)
.buttonStyle(.borderless)
.listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 20))
.listRowSeparator(.hidden)
.background(.white)
.onTapGesture {
withAnimation {
SharedMainViewModel.shared.displayedFriend = contactAvatarModel
}
if contactAvatarModel.friend != nil
&& contactAvatarModel.friend!.address != nil {
startCallFunc(contactAvatarModel.friend!.address!)
}
}
.onLongPressGesture(minimumDuration: 0.2) {
contactsListViewModel.selectedFriend = contactAvatarModel
showingSheet.toggle()
}
}
}

View file

@ -32,6 +32,7 @@ struct EditContactFragment: View {
@Binding var isShowEditContactFragment: Bool
@Binding var isShowDismissPopup: Bool
let isShowEditContactFragmentAddress: String
@State private var delayedColor = Color.white
@ -46,10 +47,11 @@ struct EditContactFragment: View {
@State private var selectedImage: UIImage?
@State private var removedImage = false
init(friend: Friend? = nil, isShowEditContactFragment: Binding<Bool>, isShowDismissPopup: Binding<Bool>) {
_editContactViewModel = StateObject(wrappedValue: EditContactViewModel(friend: friend))
init(contactAvatarModel: ContactAvatarModel? = nil, isShowEditContactFragment: Binding<Bool>, isShowDismissPopup: Binding<Bool>, isShowEditContactFragmentAddress: String = "") {
_editContactViewModel = StateObject(wrappedValue: EditContactViewModel(contactAvatarModel: contactAvatarModel))
self._isShowEditContactFragment = isShowEditContactFragment
self._isShowDismissPopup = isShowDismissPopup
self.isShowEditContactFragmentAddress = isShowEditContactFragmentAddress
}
var body: some View {
@ -152,18 +154,9 @@ struct EditContactFragment: View {
VStack(spacing: 0) {
VStack(spacing: 0) {
VStack(spacing: 0) {
if editContactViewModel.selectedEditFriend != nil
&& editContactViewModel.selectedEditFriend!.photo != nil
&& !editContactViewModel.selectedEditFriend!.photo!.isEmpty && selectedImage == nil && !removedImage {
if editContactViewModel.selectedEditFriend != nil && selectedImage == nil && !removedImage {
Avatar(contactAvatarModel:
ContactAvatarModel(
friend: editContactViewModel.selectedEditFriend!,
name: editContactViewModel.selectedEditFriend?.name ?? "",
address: editContactViewModel.selectedEditFriend?.address?.asStringUriOnly() ?? "",
withPresence: false
), avatarSize: 100
)
Avatar(contactAvatarModel: editContactViewModel.selectedEditFriend!, avatarSize: 100)
} else if selectedImage == nil {
Image("profil-picture-default")
@ -179,9 +172,8 @@ struct EditContactFragment: View {
}
if editContactViewModel.selectedEditFriend != nil
&& editContactViewModel.selectedEditFriend!.photo != nil
&& !editContactViewModel.selectedEditFriend!.photo!.isEmpty
&& (editContactViewModel.selectedEditFriend!.photo!.suffix(11) != "default.png" || selectedImage != nil) && !removedImage {
&& !editContactViewModel.selectedEditFriend!.photo.isEmpty
&& (editContactViewModel.selectedEditFriend!.photo.suffix(11) != "default.png" || selectedImage != nil) && !removedImage {
HStack {
Spacer()
@ -213,6 +205,7 @@ struct EditContactFragment: View {
removedImage = false
}
}
showPhotoPicker = false
}
}
.edgesIgnoringSafeArea(.all)
@ -265,6 +258,7 @@ struct EditContactFragment: View {
removedImage = false
}
}
showPhotoPicker = false
}
}
.edgesIgnoringSafeArea(.all)
@ -323,13 +317,13 @@ struct EditContactFragment: View {
.default_text_style_700(styleSize: 15)
.padding(.bottom, -5)
ForEach(0..<editContactViewModel.sipAddresses.count, id: \.self) { index in
ForEach(editContactViewModel.sipAddresses.indices, id: \.self) { index in
HStack(alignment: .center) {
TextField("sip_address", text: $editContactViewModel.sipAddresses[index])
.default_text_style(styleSize: 15)
.disableAutocorrection(true)
.autocapitalization(.none)
.autocapitalization(.none)
.frame(height: 25)
.padding(.horizontal, 20)
.padding(.vertical, 15)
@ -378,7 +372,7 @@ struct EditContactFragment: View {
TextField("phone_number", text: $editContactViewModel.phoneNumbers[index])
.default_text_style(styleSize: 15)
.textContentType(.oneTimeCode)
.keyboardType(.numberPad)
.keyboardType(.numberPad)
.frame(height: 25)
.padding(.horizontal, 20)
.padding(.vertical, 15)
@ -483,6 +477,14 @@ struct EditContactFragment: View {
}
}
.navigationBarHidden(true)
.onAppear {
if !self.isShowEditContactFragmentAddress.isEmpty {
DispatchQueue.main.async {
editContactViewModel.sipAddresses[0] = isShowEditContactFragmentAddress
editContactViewModel.sipAddresses.append("")
}
}
}
}
@Sendable private func delayColor() async {
@ -500,59 +502,86 @@ struct EditContactFragment: View {
}
func addOrEditFriend() {
let newContact = Contact(
identifier: editContactViewModel.identifier,
firstName: editContactViewModel.firstName,
lastName: editContactViewModel.lastName,
organizationName: editContactViewModel.company,
jobTitle: editContactViewModel.jobTitle,
displayName: "",
sipAddresses: editContactViewModel.sipAddresses.map { $0 },
phoneNumbers: editContactViewModel.phoneNumbers.map { PhoneNumber(numLabel: "", num: $0)},
imageData: ""
)
if editContactViewModel.selectedEditFriend != nil && selectedImage == nil &&
!removedImage && editContactViewModel.selectedEditFriend!.photo!.suffix(11) != "default.png" {
ContactsManager.shared.saveFriend(
result: String(editContactViewModel.selectedEditFriend!.photo!.dropFirst(6)),
contact: newContact,
existingFriend: editContactViewModel.selectedEditFriend, completion: {_ in }
CoreContext.shared.doOnCoreQueue { core in
let newContact = Contact(
identifier: editContactViewModel.identifier,
firstName: editContactViewModel.firstName,
lastName: editContactViewModel.lastName,
organizationName: editContactViewModel.company,
jobTitle: editContactViewModel.jobTitle,
displayName: "",
sipAddresses: editContactViewModel.sipAddresses.map { $0 },
phoneNumbers: editContactViewModel.phoneNumbers.map { PhoneNumber(numLabel: "", num: $0)},
imageData: ""
)
} else {
ContactsManager.shared.saveImage(
image: selectedImage
?? ContactsManager.shared.textToImage(
firstName: editContactViewModel.firstName, lastName: editContactViewModel.lastName),
name: editContactViewModel.firstName
+ editContactViewModel.lastName,
prefix: ((selectedImage == nil) ? "-default" : ""),
contact: newContact, linphoneFriend: true, existingFriend: editContactViewModel.selectedEditFriend) {
MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
DispatchQueue.main.async {
if editContactViewModel.selectedEditFriend != nil {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
let result = ContactsManager.shared.lastSearch.firstIndex(where: {
$0.friend!.name == newContact.firstName + " " + newContact.lastName
})
SharedMainViewModel.shared.indexDisplayedFriend = result
}
if editContactViewModel.selectedEditFriend != nil && editContactViewModel.selectedEditFriend!.friend != nil && selectedImage == nil &&
!removedImage && editContactViewModel.selectedEditFriend!.friend!.photo!.suffix(11) != "default.png" {
ContactsManager.shared.saveFriend(
result: String(editContactViewModel.selectedEditFriend!.friend!.photo!.dropFirst(6)),
contact: newContact,
existingFriend: editContactViewModel.selectedEditFriend!.friend, completion: {_ in
if let selectedFriendTmp = editContactViewModel.selectedEditFriend?.friend {
let addressTmp = selectedFriendTmp.address?.clone()?.asStringUriOnly() ?? ""
SharedMainViewModel.shared.displayedFriend?.resetContactAvatarModel(
friend: selectedFriendTmp,
name: selectedFriendTmp.name ?? "",
address: addressTmp,
withPresence: SharedMainViewModel.shared.displayedFriend?.withPresence
)
}
delayColorDismiss()
if editContactViewModel.selectedEditFriend == nil {
withAnimation {
isShowEditContactFragment.toggle()
}
} else {
withAnimation {
dismiss()
DispatchQueue.main.async {
delayColorDismiss()
if editContactViewModel.selectedEditFriend?.friend == nil {
withAnimation {
isShowEditContactFragment.toggle()
}
} else {
withAnimation {
dismiss()
}
}
editContactViewModel.resetValues()
}
editContactViewModel.resetValues()
}
}
)
} else {
ContactsManager.shared.saveImage(
image: selectedImage
?? ContactsManager.shared.textToImage(
firstName: editContactViewModel.firstName, lastName: editContactViewModel.lastName),
name: editContactViewModel.firstName
+ editContactViewModel.lastName,
prefix: ((selectedImage == nil) ? "-default" : ""),
contact: newContact, linphoneFriend: true, existingFriend: editContactViewModel.selectedEditFriend?.friend) {
if let selectedFriendTmp = editContactViewModel.selectedEditFriend?.friend {
let addressTmp = selectedFriendTmp.address?.clone()?.asStringUriOnly() ?? ""
SharedMainViewModel.shared.displayedFriend?.resetContactAvatarModel(
friend: selectedFriendTmp,
name: selectedFriendTmp.name ?? "",
address: addressTmp,
withPresence: SharedMainViewModel.shared.displayedFriend?.withPresence
)
} else {
MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
}
DispatchQueue.main.async {
delayColorDismiss()
if editContactViewModel.selectedEditFriend?.friend == nil {
withAnimation {
isShowEditContactFragment.toggle()
}
} else {
withAnimation {
dismiss()
}
}
editContactViewModel.resetValues()
}
}
}
}
}
}

View file

@ -31,36 +31,8 @@ struct FavoriteContactsListFragment: View {
var body: some View {
ScrollView(.horizontal) {
HStack {
ForEach(0..<contactsManager.lastSearch.count, id: \.self) { index in
if contactsManager.lastSearch[index].friend != nil && contactsManager.lastSearch[index].friend!.starred == true {
VStack {
VStack {
if contactsManager.lastSearch[index].friend!.photo != nil
&& !contactsManager.lastSearch[index].friend!.photo!.isEmpty {
Avatar(contactAvatarModel: contactsManager.avatarListModel[index], avatarSize: 50)
} else {
Image("profil-picture-default")
.resizable()
.frame(width: 50, height: 50)
.clipShape(Circle())
}
Text((contactsManager.lastSearch[index].friend?.name)!)
.default_text_style(styleSize: 16)
.frame( maxWidth: .infinity, alignment: .center)
}
}
.background(.white)
.onTapGesture {
withAnimation {
SharedMainViewModel.shared.indexDisplayedFriend = index
}
}
.onLongPressGesture(minimumDuration: 0.2) {
contactsListViewModel.selectedFriend = contactsManager.lastSearch[index].friend
showingSheet.toggle()
}
.frame(minWidth: 70, maxWidth: 70)
}
ForEach(contactsManager.avatarListModel) { contactAvatarModel in
FavoriteContactRow(contactAvatarModel: contactAvatarModel, showingSheet: $showingSheet)
}
}
.padding(.horizontal, 10)
@ -69,6 +41,41 @@ struct FavoriteContactsListFragment: View {
}
}
struct FavoriteContactRow: View {
@ObservedObject var contactsManager = ContactsManager.shared
@EnvironmentObject var contactsListViewModel: ContactsListViewModel
@ObservedObject var contactAvatarModel: ContactAvatarModel
@Binding var showingSheet: Bool
var body: some View {
if contactAvatarModel.starred == true {
VStack {
VStack {
Avatar(contactAvatarModel: contactAvatarModel, avatarSize: 50)
Text(contactAvatarModel.name)
.default_text_style(styleSize: 16)
.frame( maxWidth: .infinity, alignment: .center)
}
}
.background(.white)
.onTapGesture {
withAnimation {
SharedMainViewModel.shared.displayedFriend = contactAvatarModel
}
}
.onLongPressGesture(minimumDuration: 0.2) {
contactsListViewModel.selectedFriend = contactAvatarModel
showingSheet.toggle()
}
.frame(minWidth: 70, maxWidth: 70)
}
}
}
#Preview {
FavoriteContactsListFragment(
showingSheet: .constant(false))

View file

@ -24,62 +24,96 @@ import Combine
class ContactAvatarModel: ObservableObject, Identifiable {
let id = UUID()
let friend: Friend?
var friend: Friend?
let name: String
@Published var name: String = ""
@Published var address: String = ""
@Published var addresses: [String] = []
@Published var phoneNumbersWithLabel: [(label: String, phoneNumber: String)] = []
let address: String
var nativeUri: String = ""
var withPresence: Bool?
@Published var addresses: [String]
@Published var starred: Bool = false
let nativeUri: String
var vcard: Vcard?
var organization: String = ""
var jobTitle: String = ""
let withPresence: Bool?
@Published var lastPresenceInfo: String
@Published var presenceStatus: ConsolidatedPresence
@Published var photo: String = ""
@Published var lastPresenceInfo: String = ""
@Published var presenceStatus: ConsolidatedPresence = .Offline
private var friendDelegate: FriendDelegate?
init(friend: Friend?, name: String, address: String, withPresence: Bool?) {
self.friend = friend
self.name = name
self.address = address
var addressesTmp: [String] = []
if let friend = friend {
friend.addresses.forEach { address in
addressesTmp.append(address.asStringUriOnly())
}
}
self.addresses = addressesTmp
self.nativeUri = friend?.nativeUri ?? ""
self.withPresence = withPresence
if let friend = friend, 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"
self.resetContactAvatarModel(friend: friend, name: name, address: address, withPresence: withPresence)
}
func resetContactAvatarModel(friend: Friend?, name: String, address: String, withPresence: Bool?) {
CoreContext.shared.doOnCoreQueue { _ in
self.friend = friend
let nameTmp = name
let addressTmp = address
var addressesTmp: [String] = []
if let friend = friend {
friend.addresses.forEach { address in
addressesTmp.append(address.asStringUriOnly())
}
} else {
self.lastPresenceInfo = ""
}
var phoneNumbersWithLabelTmp: [(label: String, phoneNumber: String)] = []
if let friend = friend {
friend.phoneNumbersWithLabel.forEach { phoneNum in
phoneNumbersWithLabelTmp.append((label: phoneNum.label ?? "", phoneNumber: phoneNum.phoneNumber))
}
}
let nativeUriTmp = friend?.nativeUri ?? ""
let withPresenceTmp = withPresence
let starredTmp = friend?.starred ?? false
let vcardTmp = friend?.vcard ?? nil
let organizationTmp = friend?.organization ?? ""
let jobTitleTmp = friend?.jobTitle ?? ""
let photoTmp = friend?.photo ?? ""
var lastPresenceInfoTmp = ""
var presenceStatusTmp: ConsolidatedPresence = .Offline
if let friend = friend, withPresence == true {
lastPresenceInfoTmp = ""
presenceStatusTmp = friend.consolidatedPresence
if friend.consolidatedPresence == .Online || friend.consolidatedPresence == .Busy {
if friend.consolidatedPresence == .Online || friend.presenceModel?.latestActivityTimestamp != -1 {
lastPresenceInfoTmp = (friend.consolidatedPresence == .Online) ?
"Online" : self.getCallTime(startDate: friend.presenceModel!.latestActivityTimestamp)
} else {
lastPresenceInfoTmp = "Away"
}
}
if let delegate = self.friendDelegate {
self.friend?.removeDelegate(delegate: delegate)
self.friendDelegate = nil
}
self.addFriendDelegate()
}
if let delegate = friendDelegate {
self.friend?.removeDelegate(delegate: delegate)
self.friendDelegate = nil
DispatchQueue.main.async {
self.name = nameTmp
self.address = addressTmp
self.addresses = addressesTmp
self.phoneNumbersWithLabel = phoneNumbersWithLabelTmp
self.nativeUri = nativeUriTmp
self.withPresence = withPresenceTmp
self.starred = starredTmp
self.vcard = vcardTmp
self.organization = organizationTmp
self.jobTitle = jobTitleTmp
self.photo = photoTmp
self.lastPresenceInfo = lastPresenceInfoTmp
self.presenceStatus = presenceStatusTmp
}
addFriendDelegate()
} else {
self.lastPresenceInfo = ""
self.presenceStatus = .Offline
}
}
@ -110,7 +144,9 @@ class ContactAvatarModel: ObservableObject, Identifiable {
func removeFriendDelegate() {
if let delegate = friendDelegate {
presenceStatus = .Offline
friend?.removeDelegate(delegate: delegate)
if let friendTmp = friend {
friendTmp.removeDelegate(delegate: delegate)
}
friendDelegate = nil
}
}

View file

@ -23,13 +23,13 @@ import SwiftUI
// swiftlint:disable line_length
class ContactsListViewModel: ObservableObject {
@Published var selectedEditFriend: Friend?
@Published var selectedEditFriend: ContactAvatarModel?
var stringToCopy: String = ""
var selectedFriend: Friend?
var selectedFriendToShare: Friend?
var selectedFriendToDelete: Friend?
var selectedFriend: ContactAvatarModel?
var selectedFriendToShare: ContactAvatarModel?
var selectedFriendToDelete: ContactAvatarModel?
@Published var operationInProgress: Bool = false
@Published var displayedConversation: ConversationModel?
@ -211,5 +211,62 @@ class ContactsListViewModel: ObservableObject {
})
chatRoom.addDelegate(delegate: contactChatRoomDelegate!)
}
func deleteSelectedContact() {
CoreContext.shared.doOnCoreQueue { core in
if self.selectedFriendToDelete != nil && self.selectedFriendToDelete!.friend != nil {
if SharedMainViewModel.shared.displayedFriend != nil {
DispatchQueue.main.async {
withAnimation {
SharedMainViewModel.shared.displayedFriend = nil
}
}
}
self.selectedFriendToDelete!.friend!.remove()
} else if SharedMainViewModel.shared.displayedFriend != nil {
DispatchQueue.main.async {
withAnimation {
SharedMainViewModel.shared.displayedFriend = nil
}
}
SharedMainViewModel.shared.displayedFriend!.friend!.remove()
}
MagicSearchSingleton.shared.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
}
}
func changeSelectedFriendToDelete() {
if selectedFriend != nil {
self.selectedFriendToDelete = self.selectedFriend
}
}
func toggleStarredSelectedFriend() {
CoreContext.shared.doOnCoreQueue { core in
if let contactAvatar = self.selectedFriend, let friend = contactAvatar.friend {
friend.edit()
friend.starred.toggle()
friend.done()
let starredTmp = friend.starred
DispatchQueue.main.async {
contactAvatar.starred = starredTmp
}
} else if let displayedFriend = SharedMainViewModel.shared.displayedFriend, let friend = displayedFriend.friend {
friend.edit()
friend.starred.toggle()
friend.done()
let starredTmp = friend.starred
DispatchQueue.main.async {
displayedFriend.starred = starredTmp
}
}
}
}
}
// swiftlint:enable line_length

View file

@ -22,7 +22,7 @@ import SwiftUI
class EditContactViewModel: ObservableObject {
let selectedEditFriend: Friend?
let selectedEditFriend: ContactAvatarModel?
@Published var identifier: String = ""
@Published var firstName: String = ""
@ -32,8 +32,8 @@ class EditContactViewModel: ObservableObject {
@Published var company: String = ""
@Published var jobTitle: String = ""
init(friend: Friend? = nil) {
self.selectedEditFriend = friend
init(contactAvatarModel: ContactAvatarModel? = nil) {
self.selectedEditFriend = contactAvatarModel
resetValues()
}
@ -50,11 +50,11 @@ class EditContactViewModel: ObservableObject {
if self.selectedEditFriend != nil {
self.selectedEditFriend?.addresses.forEach({ address in
sipAddressesTmp.append(String(address.asStringUriOnly().dropFirst(4)))
sipAddressesTmp.append(String(address.dropFirst(4)))
})
self.selectedEditFriend?.phoneNumbers.forEach({ phoneNumber in
phoneNumbersTmp.append(phoneNumber)
self.selectedEditFriend?.phoneNumbersWithLabel.forEach({ phoneNumber in
phoneNumbersTmp.append(phoneNumber.phoneNumber)
})
}

View file

@ -36,9 +36,7 @@ struct ContentView: View {
@ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared
@State private var contactsListViewModel: ContactsListViewModel?
//@ObservedObject var historyViewModel: HistoryViewModel
//@ObservedObject var historyListViewModel: HistoryListViewModel
@State private var historyListViewModel: HistoryListViewModel?
//@ObservedObject var startCallViewModel: StartCallViewModel
//@ObservedObject var startConversationViewModel: StartConversationViewModel
@ -70,6 +68,7 @@ struct ContentView: View {
@State var isShowDeleteAllHistoryPopup = false
@State var isShowEditContactFragment = false
@State var isShowEditContactFragmentInContactDetails = false
@State var isShowEditContactFragmentAddress = ""
@State var isShowStartCallFragment = false
@State var isShowStartConversationFragment = false
@State var isShowDismissPopup = false
@ -174,13 +173,12 @@ struct ContentView: View {
.frame(height: geometry.size.height/4)
ZStack {
/*
if historyListViewModel.missedCallsCount > 0 {
if historyListViewModel != nil && historyListViewModel!.missedCallsCount > 0 {
VStack {
HStack {
Text(
historyListViewModel.missedCallsCount < 99
? String(historyListViewModel.missedCallsCount)
historyListViewModel!.missedCallsCount < 99
? String(historyListViewModel!.missedCallsCount)
: "99+"
)
.foregroundStyle(.white)
@ -194,17 +192,14 @@ struct ContentView: View {
.padding(.bottom, 30)
.padding(.leading, 30)
}
*/
Button(action: {
sharedMainViewModel.changeIndexView(indexViewInt: 1)
sharedMainViewModel.indexDisplayedFriend = nil
sharedMainViewModel.displayedFriend = nil
sharedMainViewModel.displayedConversation = nil
sharedMainViewModel.displayedMeeting = nil
/*
if historyListViewModel.missedCallsCount > 0 {
historyListViewModel.resetMissedCallsCount()
if historyListViewModel != nil && historyListViewModel!.missedCallsCount > 0 {
historyListViewModel!.resetMissedCallsCount()
}
*/
}, label: {
VStack {
Image("phone")
@ -249,7 +244,7 @@ struct ContentView: View {
*/
Button(action: {
sharedMainViewModel.changeIndexView(indexViewInt: 2)
sharedMainViewModel.indexDisplayedFriend = nil
sharedMainViewModel.displayedFriend = nil
sharedMainViewModel.displayedCall = nil
sharedMainViewModel.displayedMeeting = nil
}, label: {
@ -275,7 +270,7 @@ struct ContentView: View {
Button(action: {
sharedMainViewModel.changeIndexView(indexViewInt: 3)
sharedMainViewModel.indexDisplayedFriend = nil
sharedMainViewModel.displayedFriend = nil
sharedMainViewModel.displayedCall = nil
sharedMainViewModel.displayedConversation = nil
}, label: {
@ -437,7 +432,7 @@ struct ContentView: View {
Menu {
if sharedMainViewModel.indexView == 0 {
Button {
sharedMainViewModel.indexDisplayedFriend = nil
sharedMainViewModel.displayedFriend = nil
isMenuOpen = false
magicSearch.allContact = true
magicSearch.searchForContacts(
@ -456,7 +451,7 @@ struct ContentView: View {
}
Button {
sharedMainViewModel.indexDisplayedFriend = nil
sharedMainViewModel.displayedFriend = nil
isMenuOpen = false
magicSearch.allContact = false
magicSearch.searchForContacts(
@ -523,8 +518,8 @@ struct ContentView: View {
magicSearch.currentFilter = ""
magicSearch.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
} else if sharedMainViewModel.indexView == 1 {
//historyListViewModel.resetFilterCallLogs()
} else if sharedMainViewModel.indexView == 1 && historyListViewModel != nil {
historyListViewModel!.resetFilterCallLogs()
} else if sharedMainViewModel.indexView == 2 {
//conversationsListViewModel.resetFilterConversations()
} else if sharedMainViewModel.indexView == 3 {
@ -570,10 +565,10 @@ struct ContentView: View {
magicSearch.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
} else if sharedMainViewModel.indexView == 1 {
if text.isEmpty {
//historyListViewModel.resetFilterCallLogs()
} else {
//historyListViewModel.filterCallLogs(filter: text)
if text.isEmpty && historyListViewModel != nil {
historyListViewModel!.resetFilterCallLogs()
} else if historyListViewModel != nil {
historyListViewModel!.filterCallLogs(filter: text)
}
} else if sharedMainViewModel.indexView == 2 {
if text.isEmpty {
@ -612,8 +607,8 @@ struct ContentView: View {
magicSearch.currentFilter = newValue
magicSearch.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
} else if sharedMainViewModel.indexView == 1 {
//historyListViewModel.filterCallLogs(filter: text)
} else if sharedMainViewModel.indexView == 1 && historyListViewModel != nil {
historyListViewModel!.filterCallLogs(filter: text)
} else if sharedMainViewModel.indexView == 2 {
//conversationsListViewModel.filterConversations(filter: text)
} else if sharedMainViewModel.indexView == 3 {
@ -676,33 +671,38 @@ struct ContentView: View {
}
}
} else if sharedMainViewModel.indexView == 1 {
//TODO a changer
NavigationView {
ZStack(alignment: .bottomTrailing) {
if let historyListVM = historyListViewModel {
HistoryView(
isShowStartCallFragment: $isShowStartCallFragment,
isShowEditContactFragment: $isShowEditContactFragment,
text: $text,
isShowEditContactFragmentAddress: $isShowEditContactFragmentAddress
)
.environmentObject(historyListVM)
.roundedCorner(25, corners: [.topRight, .topLeft])
.shadow(
color: (orientation == .landscapeLeft
|| orientation == .landscapeRight
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
? .white.opacity(0.0)
: .black.opacity(0.2),
radius: 25
)
} else {
NavigationView {
VStack {
Spacer()
ProgressView()
.controlSize(.large)
Spacer()
}
.onAppear {
historyListViewModel = HistoryListViewModel()
}
}
}
.navigationViewStyle(.stack)
/*
HistoryView(
historyListViewModel: historyListViewModel,
historyViewModel: historyViewModel,
contactsListViewModel: contactsListViewModel,
editContactViewModel: editContactViewModel,
index: $index,
isShowStartCallFragment: $isShowStartCallFragment,
isShowEditContactFragment: $isShowEditContactFragment,
text: $text
)
.roundedCorner(25, corners: [.topRight, .topLeft])
.shadow(
color: (orientation == .landscapeLeft
|| orientation == .landscapeRight
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
? .white.opacity(0.0)
: .black.opacity(0.2),
radius: 25
)
*/
} else if sharedMainViewModel.indexView == 2 {
//TODO a changer
NavigationView {
@ -809,13 +809,12 @@ struct ContentView: View {
Spacer()
ZStack {
/*
if historyListViewModel.missedCallsCount > 0 {
if historyListViewModel != nil && historyListViewModel!.missedCallsCount > 0 {
VStack {
HStack {
Text(
historyListViewModel.missedCallsCount < 99
? String(historyListViewModel.missedCallsCount)
historyListViewModel!.missedCallsCount < 99
? String(historyListViewModel!.missedCallsCount)
: "99+"
)
.foregroundStyle(.white)
@ -829,17 +828,14 @@ struct ContentView: View {
.padding(.bottom, 30)
.padding(.leading, 30)
}
*/
Button(action: {
sharedMainViewModel.changeIndexView(indexViewInt: 1)
sharedMainViewModel.indexDisplayedFriend = nil
sharedMainViewModel.displayedFriend = nil
sharedMainViewModel.displayedConversation = nil
sharedMainViewModel.displayedMeeting = nil
/*
if historyListViewModel.missedCallsCount > 0 {
historyListViewModel.resetMissedCallsCount()
if historyListViewModel != nil && historyListViewModel!.missedCallsCount > 0 {
historyListViewModel!.resetMissedCallsCount()
}
*/
}, label: {
VStack {
Image("phone")
@ -886,7 +882,7 @@ struct ContentView: View {
*/
Button(action: {
sharedMainViewModel.changeIndexView(indexViewInt: 2)
sharedMainViewModel.indexDisplayedFriend = nil
sharedMainViewModel.displayedFriend = nil
sharedMainViewModel.displayedCall = nil
sharedMainViewModel.displayedMeeting = nil
}, label: {
@ -913,7 +909,7 @@ struct ContentView: View {
Spacer()
Button(action: {
sharedMainViewModel.changeIndexView(indexViewInt: 3)
sharedMainViewModel.indexDisplayedFriend = nil
sharedMainViewModel.displayedFriend = nil
sharedMainViewModel.displayedCall = nil
sharedMainViewModel.displayedConversation = nil
}, label: {
@ -947,7 +943,7 @@ struct ContentView: View {
}
}
if sharedMainViewModel.indexDisplayedFriend != nil || sharedMainViewModel.displayedCall != nil || sharedMainViewModel.displayedConversation != nil ||
if sharedMainViewModel.displayedFriend != nil || sharedMainViewModel.displayedCall != nil || sharedMainViewModel.displayedConversation != nil ||
sharedMainViewModel.displayedMeeting != nil {
HStack(spacing: 0) {
Spacer()
@ -958,7 +954,7 @@ struct ContentView: View {
? (geometry.size.width/100*40) + 75
: 0
)
if sharedMainViewModel.indexView == 0 && sharedMainViewModel.indexDisplayedFriend != nil && contactsManager.avatarListModel.count > sharedMainViewModel.indexDisplayedFriend! {
if sharedMainViewModel.indexView == 0 && sharedMainViewModel.displayedFriend != nil {
ContactFragment(
isShowDeletePopup: $isShowDeleteContactPopup,
isShowDismissPopup: $isShowDismissPopup,
@ -967,7 +963,7 @@ struct ContentView: View {
isShowEditContactFragmentInContactDetails: $isShowEditContactFragmentInContactDetails
)
.environmentObject(contactsListViewModel!)
.environmentObject(contactsManager.avatarListModel[sharedMainViewModel.indexDisplayedFriend!])
.environmentObject(sharedMainViewModel.displayedFriend!)
.frame(maxWidth: .infinity)
.background(Color.gray100)
.ignoresSafeArea(.keyboard)
@ -1077,12 +1073,14 @@ struct ContentView: View {
if isShowEditContactFragment {
EditContactFragment(
isShowEditContactFragment: $isShowEditContactFragment,
isShowDismissPopup: $isShowDismissPopup
isShowDismissPopup: $isShowDismissPopup,
isShowEditContactFragmentAddress: isShowEditContactFragmentAddress
)
.zIndex(3)
.transition(.opacity.combined(with: .move(edge: .bottom)))
.onAppear {
sharedMainViewModel.indexDisplayedFriend = nil
sharedMainViewModel.displayedFriend = nil
isShowEditContactFragmentAddress = ""
}
}
@ -1143,35 +1141,23 @@ struct ContentView: View {
*/
if isShowDeleteContactPopup {
PopupView(isShowPopup: $isShowDeleteContactPopup,
title: Text(String(format: String(localized: "contact_dialog_delete_title"),contactsListViewModel!.selectedFriend != nil
? contactsListViewModel!.selectedFriend!.name!
: (sharedMainViewModel.indexDisplayedFriend != nil
? contactsManager.lastSearch[sharedMainViewModel.indexDisplayedFriend!].friend!.name!
: "Error Name"))),
content: Text("contact_dialog_delete_message"),
titleFirstButton: Text("dialog_cancel"),
actionFirstButton: {
self.isShowDeleteContactPopup.toggle()},
titleSecondButton: Text("dialog_ok"),
actionSecondButton: {
if contactsListViewModel!.selectedFriendToDelete != nil {
if sharedMainViewModel.indexDisplayedFriend != nil {
withAnimation {
sharedMainViewModel.indexDisplayedFriend = nil
}
}
contactsListViewModel!.selectedFriendToDelete!.remove()
} else if sharedMainViewModel.indexDisplayedFriend != nil {
let tmpIndex = sharedMainViewModel.indexDisplayedFriend
withAnimation {
sharedMainViewModel.indexDisplayedFriend = nil
}
contactsManager.lastSearch[tmpIndex!].friend!.remove()
}
magicSearch.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
self.isShowDeleteContactPopup.toggle()
PopupView(
isShowPopup: $isShowDeleteContactPopup,
title: Text(
String(
format: String(localized: "contact_dialog_delete_title"),
contactsListViewModel!.selectedFriend?.name
?? (SharedMainViewModel.shared.displayedFriend!.name ?? "Unknown Contact")
)
),
content: Text("contact_dialog_delete_message"),
titleFirstButton: Text("dialog_cancel"),
actionFirstButton: {
self.isShowDeleteContactPopup.toggle()},
titleSecondButton: Text("dialog_ok"),
actionSecondButton: {
self.contactsListViewModel!.deleteSelectedContact()
self.isShowDeleteContactPopup.toggle()
})
.background(.black.opacity(0.65))
.zIndex(3)
@ -1179,11 +1165,10 @@ struct ContentView: View {
self.isShowDeleteContactPopup.toggle()
}
.onAppear {
contactsListViewModel!.selectedFriendToDelete = contactsListViewModel!.selectedFriend
self.contactsListViewModel!.changeSelectedFriendToDelete()
}
}
/*
if isShowDeleteAllHistoryPopup {
PopupView(isShowPopup: $isShowDeleteContactPopup,
title: Text("history_dialog_delete_all_call_logs_title"),
@ -1191,11 +1176,15 @@ struct ContentView: View {
titleFirstButton: Text("dialog_cancel"),
actionFirstButton: {
self.isShowDeleteAllHistoryPopup.toggle()
historyListViewModel.callLogsAddressToDelete = ""
if historyListViewModel != nil {
historyListViewModel!.callLogsAddressToDelete = ""
}
},
titleSecondButton: Text("dialog_ok"),
actionSecondButton: {
historyListViewModel.removeCallLogs()
if historyListViewModel != nil {
historyListViewModel!.removeCallLogs()
}
self.isShowDeleteAllHistoryPopup.toggle()
sharedMainViewModel.displayedCall = nil
@ -1208,7 +1197,6 @@ struct ContentView: View {
self.isShowDeleteAllHistoryPopup.toggle()
}
}
*/
if isShowDismissPopup {
PopupView(isShowPopup: $isShowDismissPopup,
@ -1232,13 +1220,13 @@ struct ContentView: View {
}
}
if isShowSipAddressesPopup {
if isShowSipAddressesPopup && sharedMainViewModel.displayedFriend != nil {
SipAddressesPopup(
isShowSipAddressesPopup: $isShowSipAddressesPopup,
isShowSipAddressesPopupType: $isShowSipAddressesPopupType
)
.environmentObject(contactsListViewModel!)
.environmentObject(contactsManager.avatarListModel[sharedMainViewModel.indexDisplayedFriend != nil ? sharedMainViewModel.indexDisplayedFriend! : 0])
.environmentObject(sharedMainViewModel.displayedFriend!)
.background(.black.opacity(0.65))
.zIndex(3)
.onTapGesture {
@ -1253,7 +1241,7 @@ struct ContentView: View {
.zIndex(3)
.onDisappear {
if contactsListViewModel.displayedConversation != nil {
sharedMainViewModel.indexDisplayedFriend = nil
sharedMainViewModel.displayedFriend = nil
sharedMainViewModel.displayedCall = nil
sharedMainViewModel.changeIndexView(indexViewInt: 2)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
@ -1443,7 +1431,9 @@ struct ContentView: View {
}
.onReceive(contactLoaded) { _ in
//conversationsListViewModel.updateChatRoomsList()
//historyListViewModel.refreshHistoryAvatarModel()
if historyListViewModel != nil {
historyListViewModel!.refreshHistoryAvatarModel()
}
}
.onReceive(contactAdded) { address in
//conversationsListViewModel.updateChatRoom(address: address)
@ -1463,7 +1453,7 @@ struct ContentView: View {
}
}
.onRotate { newOrientation in
if (sharedMainViewModel.indexDisplayedFriend != nil || sharedMainViewModel.displayedCall != nil || sharedMainViewModel.displayedConversation != nil) && searchIsActive {
if (sharedMainViewModel.displayedFriend != nil || sharedMainViewModel.displayedCall != nil || sharedMainViewModel.displayedConversation != nil) && searchIsActive {
self.focusedField = false
} else if searchIsActive {
self.focusedField = true
@ -1499,8 +1489,6 @@ class NavigationManager: ObservableObject {
#Preview {
ContentView(
//historyViewModel: HistoryViewModel(),
//historyListViewModel: HistoryListViewModel(),
//startCallViewModel: StartCallViewModel(),
//startConversationViewModel: StartConversationViewModel(),
//callViewModel: CallViewModel(),

View file

@ -349,13 +349,13 @@ struct ConversationInfoFragment: View {
action: {
let addressConv = participantConversationModel.address
let friendIndex = contactsManager.lastSearch.firstIndex(
let friendIndex = contactsManager.avatarListModel.first(
where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressConv})})
if friendIndex != nil {
withAnimation {
SharedMainViewModel.shared.displayedConversation = nil
indexPage = 0
SharedMainViewModel.shared.indexDisplayedFriend = friendIndex
SharedMainViewModel.shared.displayedFriend = friendIndex
}
} else {
withAnimation {
@ -529,13 +529,13 @@ struct ConversationInfoFragment: View {
let addressConv = conversationViewModel.participantConversationModel.first?.address ?? ""
let friendIndex = contactsManager.lastSearch.firstIndex(
let friendIndex = contactsManager.avatarListModel.first(
where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressConv})})
if friendIndex != nil {
withAnimation {
SharedMainViewModel.shared.displayedConversation = nil
indexPage = 0
SharedMainViewModel.shared.indexDisplayedFriend = friendIndex
SharedMainViewModel.shared.displayedFriend = friendIndex
}
} else {
withAnimation {

View file

@ -30,7 +30,6 @@ struct HistoryContactFragment: View {
@ObservedObject private var telecomManager = TelecomManager.shared
@ObservedObject var contactAvatarModel: ContactAvatarModel
@ObservedObject var historyViewModel: HistoryViewModel
@ObservedObject var historyListViewModel: HistoryListViewModel
@ObservedObject var contactsListViewModel: ContactsListViewModel
@ObservedObject var editContactViewModel: EditContactViewModel
@ -82,14 +81,14 @@ struct HistoryContactFragment: View {
let addressCall = SharedMainViewModel.shared.displayedCall!.addressFriend!.address
if addressCall != nil {
let friendIndex = contactsManager.lastSearch.firstIndex(
let friendIndex = contactsManager.avatarListModel.first(
where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressCall!.asStringUriOnly()})})
if friendIndex != nil {
withAnimation {
SharedMainViewModel.shared.displayedCall = nil
indexPage = 0
SharedMainViewModel.shared.indexDisplayedFriend = friendIndex
SharedMainViewModel.shared.displayedFriend = friendIndex
}
}
}
@ -450,7 +449,6 @@ struct HistoryContactFragment: View {
#Preview {
HistoryContactFragment(
contactAvatarModel: ContactAvatarModel(friend: nil, name: "", address: "", withPresence: false),
historyViewModel: HistoryViewModel(),
historyListViewModel: HistoryListViewModel(),
contactsListViewModel: ContactsListViewModel(),
editContactViewModel: EditContactViewModel(),

View file

@ -22,43 +22,32 @@ import SwiftUI
struct HistoryFragment: View {
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
@ObservedObject var historyListViewModel: HistoryListViewModel
@ObservedObject var historyViewModel: HistoryViewModel
@ObservedObject var contactsListViewModel: ContactsListViewModel
@ObservedObject var editContactViewModel: EditContactViewModel
@EnvironmentObject var historyListViewModel: HistoryListViewModel
@State private var showingSheet = false
@Binding var index: Int
@Binding var isShowEditContactFragment: Bool
@Binding var text: String
@Binding var isShowEditContactFragmentAddress: String
var body: some View {
ZStack {
if #available(iOS 16.0, *), idiom != .pad {
HistoryListFragment(historyListViewModel: historyListViewModel, historyViewModel: historyViewModel, showingSheet: $showingSheet, text: $text)
HistoryListFragment(showingSheet: $showingSheet, text: $text)
.sheet(isPresented: $showingSheet) {
HistoryListBottomSheet(
historyViewModel: historyViewModel,
contactsListViewModel: contactsListViewModel,
editContactViewModel: editContactViewModel,
historyListViewModel: historyListViewModel,
showingSheet: $showingSheet,
index: $index,
isShowEditContactFragment: $isShowEditContactFragment
isShowEditContactFragment: $isShowEditContactFragment,
isShowEditContactFragmentAddress: $isShowEditContactFragmentAddress
)
.presentationDetents([.fraction(0.2)])
}
} else {
HistoryListFragment(historyListViewModel: historyListViewModel, historyViewModel: historyViewModel, showingSheet: $showingSheet, text: $text)
HistoryListFragment(showingSheet: $showingSheet, text: $text)
.halfSheet(showSheet: $showingSheet) {
HistoryListBottomSheet(
historyViewModel: historyViewModel,
contactsListViewModel: contactsListViewModel,
editContactViewModel: editContactViewModel,
historyListViewModel: historyListViewModel,
showingSheet: $showingSheet,
index: $index,
isShowEditContactFragment: $isShowEditContactFragment
isShowEditContactFragment: $isShowEditContactFragment,
isShowEditContactFragmentAddress: $isShowEditContactFragmentAddress
)
} onDismiss: {}
}
@ -68,12 +57,8 @@ struct HistoryFragment: View {
#Preview {
HistoryFragment(
historyListViewModel: HistoryListViewModel(),
historyViewModel: HistoryViewModel(),
contactsListViewModel: ContactsListViewModel(),
editContactViewModel: EditContactViewModel(),
index: .constant(1),
isShowEditContactFragment: .constant(false),
text: .constant("")
text: .constant(""),
isShowEditContactFragmentAddress: .constant("")
)
}

View file

@ -27,17 +27,15 @@ struct HistoryListBottomSheet: View {
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
@ObservedObject var contactsManager = ContactsManager.shared
@ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared
@ObservedObject var historyViewModel: HistoryViewModel
@ObservedObject var contactsListViewModel: ContactsListViewModel
@ObservedObject var editContactViewModel: EditContactViewModel
@ObservedObject var historyListViewModel: HistoryListViewModel
@EnvironmentObject var historyListViewModel: HistoryListViewModel
@State private var orientation = UIDevice.current.orientation
@Binding var showingSheet: Bool
@Binding var index: Int
@Binding var isShowEditContactFragment: Bool
@Binding var isShowEditContactFragmentAddress: String
var body: some View {
VStack(alignment: .leading) {
@ -74,30 +72,28 @@ struct HistoryListBottomSheet: View {
dismiss()
}
index = 0
sharedMainViewModel.changeIndexView(indexViewInt: 0)
if historyViewModel.selectedCall != nil && historyViewModel.selectedCall!.addressFriend != nil {
let addressCall = historyViewModel.selectedCall!.address
if historyListViewModel.selectedCall != nil && historyListViewModel.selectedCall!.addressFriend != nil {
let addressCall = historyListViewModel.selectedCall!.address
let friendIndex = contactsManager.lastSearch.firstIndex(where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressCall})})
let friendIndex = contactsManager.avatarListModel.first(where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressCall})})
if friendIndex != nil {
withAnimation {
SharedMainViewModel.shared.indexDisplayedFriend = friendIndex
SharedMainViewModel.shared.displayedFriend = friendIndex
}
}
} else if historyViewModel.selectedCall != nil {
let addressCall = historyViewModel.selectedCall!.address
} else if historyListViewModel.selectedCall != nil {
let addressCall = historyListViewModel.selectedCall!.address
withAnimation {
isShowEditContactFragment.toggle()
editContactViewModel.sipAddresses.removeAll()
editContactViewModel.sipAddresses.append(String(addressCall.dropFirst(4)))
editContactViewModel.sipAddresses.append("")
isShowEditContactFragmentAddress = String(addressCall.dropFirst(4))
}
}
} label: {
HStack {
if historyViewModel.selectedCall != nil && historyViewModel.selectedCall!.addressFriend != nil {
if historyListViewModel.selectedCall != nil && historyListViewModel.selectedCall!.addressFriend != nil {
Image("user-circle")
.renderingMode(.template)
.resizable()
@ -130,14 +126,14 @@ struct HistoryListBottomSheet: View {
.frame(maxWidth: .infinity)
Button {
if historyViewModel.selectedCall != nil && historyViewModel.selectedCall!.isOutgoing {
if historyListViewModel.selectedCall != nil && historyListViewModel.selectedCall!.isOutgoing {
UIPasteboard.general.setValue(
historyViewModel.selectedCall!.address.dropFirst(4),
historyListViewModel.selectedCall!.address.dropFirst(4),
forPasteboardType: UTType.plainText.identifier
)
} else {
UIPasteboard.general.setValue(
historyViewModel.selectedCall!.address.dropFirst(4),
historyListViewModel.selectedCall!.address.dropFirst(4),
forPasteboardType: UTType.plainText.identifier
)
}
@ -180,8 +176,8 @@ struct HistoryListBottomSheet: View {
.frame(maxWidth: .infinity)
Button {
if historyViewModel.selectedCall != nil {
historyListViewModel.removeCallLog(historyModel: historyViewModel.selectedCall!)
if historyListViewModel.selectedCall != nil {
historyListViewModel.removeCallLog(historyModel: historyListViewModel.selectedCall!)
}
if #available(iOS 16.0, *) {
@ -224,12 +220,8 @@ struct HistoryListBottomSheet: View {
#Preview {
HistoryListBottomSheet(
historyViewModel: HistoryViewModel(),
contactsListViewModel: ContactsListViewModel(),
editContactViewModel: EditContactViewModel(),
historyListViewModel: HistoryListViewModel(),
showingSheet: .constant(false),
index: .constant(1),
isShowEditContactFragment: .constant(false)
isShowEditContactFragment: .constant(false),
isShowEditContactFragmentAddress: .constant("")
)
}

View file

@ -27,8 +27,7 @@ struct HistoryListFragment: View {
@ObservedObject var contactsManager = ContactsManager.shared
@ObservedObject private var telecomManager = TelecomManager.shared
@ObservedObject var historyListViewModel: HistoryListViewModel
@ObservedObject var historyViewModel: HistoryViewModel
@EnvironmentObject var historyListViewModel: HistoryListViewModel
@Binding var showingSheet: Bool
@Binding var text: String
@ -36,110 +35,8 @@ struct HistoryListFragment: View {
var body: some View {
VStack {
List {
ForEach(0..<historyListViewModel.callLogs.count, id: \.self) { index in
HStack {
HStack {
if !historyListViewModel.callLogs[index].isConf {
if historyListViewModel.callLogs[index].avatarModel != nil {
Avatar(contactAvatarModel: historyListViewModel.callLogs[index].avatarModel!, avatarSize: 50)
} else {
if !historyListViewModel.callLogs[index].addressName.isEmpty {
Image(uiImage: contactsManager.textToImage(
firstName: historyListViewModel.callLogs[index].addressName,
lastName: historyListViewModel.callLogs[index].addressName.components(separatedBy: " ").count > 1
? historyListViewModel.callLogs[index].addressName.components(separatedBy: " ")[1]
: ""))
.resizable()
.frame(width: 50, height: 50)
.clipShape(Circle())
} else {
VStack {
Image("profil-picture-default")
.renderingMode(.template)
.resizable()
.frame(width: 28, height: 28)
.foregroundStyle(Color.grayMain2c600)
}
.frame(width: 50, height: 50)
.background(Color.grayMain2c200)
.clipShape(Circle())
}
}
} else {
VStack {
Image("users-three-square")
.renderingMode(.template)
.resizable()
.frame(width: 28, height: 28)
.foregroundStyle(Color.grayMain2c600)
}
.frame(width: 50, height: 50)
.background(Color.grayMain2c200)
.clipShape(Circle())
}
VStack(spacing: 0) {
Spacer()
if !historyListViewModel.callLogs[index].isConf {
Text(historyListViewModel.callLogs[index].addressName)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
} else {
Text(historyListViewModel.callLogs[index].subject)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
}
HStack {
Image(historyListViewModel.getCallIconResId(callStatus: historyListViewModel.callLogs[index].status, isOutgoing: historyListViewModel.callLogs[index].isOutgoing))
.resizable()
.frame(
width: historyListViewModel.getCallIconResId(callStatus: historyListViewModel.callLogs[index].status, isOutgoing: historyListViewModel.callLogs[index].isOutgoing).contains("rejected") ? 12 : 8,
height: historyListViewModel.getCallIconResId(callStatus: historyListViewModel.callLogs[index].status, isOutgoing: historyListViewModel.callLogs[index].isOutgoing).contains("rejected") ? 6 : 8)
Text(historyListViewModel.getCallTime(startDate: historyListViewModel.callLogs[index].startDate))
.default_text_style_300(styleSize: 12)
.frame(maxWidth: .infinity, alignment: .leading)
Spacer()
}
Spacer()
}
if !historyListViewModel.callLogs[index].isConf {
Image("phone")
.resizable()
.frame(width: 25, height: 25)
.padding(.all, 10)
.padding(.trailing, 5)
.highPriorityGesture(
TapGesture()
.onEnded { _ in
withAnimation {
doCall(index: index)
SharedMainViewModel.shared.displayedCall = nil
}
}
)
}
}
}
.frame(height: 50)
.buttonStyle(.borderless)
.listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 20))
.listRowSeparator(.hidden)
.background(.white)
.onTapGesture {
withAnimation {
SharedMainViewModel.shared.displayedCall = historyListViewModel.callLogs[index]
}
}
.onLongPressGesture(minimumDuration: 0.2) {
historyViewModel.selectedCall = historyListViewModel.callLogs[index]
showingSheet.toggle()
}
ForEach(historyListViewModel.callLogs) { historyModel in
HistoryRow(historyModel: historyModel, showingSheet: $showingSheet)
}
}
.safeAreaInset(edge: .top, content: {
@ -169,14 +66,131 @@ struct HistoryListFragment: View {
.navigationTitle("")
.navigationBarHidden(true)
}
}
struct HistoryRow: View {
@ObservedObject var contactsManager = ContactsManager.shared
@ObservedObject private var telecomManager = TelecomManager.shared
func doCall(index: Int) {
telecomManager.doCallOrJoinConf(address: historyListViewModel.callLogs[index].addressLinphone)
@EnvironmentObject var historyListViewModel: HistoryListViewModel
@ObservedObject var historyModel: HistoryModel
@Binding var showingSheet: Bool
var body: some View {
HStack {
HStack {
if !historyModel.isConf {
if historyModel.avatarModel != nil {
Avatar(contactAvatarModel: historyModel.avatarModel!, avatarSize: 50)
} else {
if !historyModel.addressName.isEmpty {
Image(uiImage: contactsManager.textToImage(
firstName: historyModel.addressName,
lastName: historyModel.addressName.components(separatedBy: " ").count > 1
? historyModel.addressName.components(separatedBy: " ")[1]
: ""))
.resizable()
.frame(width: 50, height: 50)
.clipShape(Circle())
} else {
VStack {
Image("profil-picture-default")
.renderingMode(.template)
.resizable()
.frame(width: 28, height: 28)
.foregroundStyle(Color.grayMain2c600)
}
.frame(width: 50, height: 50)
.background(Color.grayMain2c200)
.clipShape(Circle())
}
}
} else {
VStack {
Image("users-three-square")
.renderingMode(.template)
.resizable()
.frame(width: 28, height: 28)
.foregroundStyle(Color.grayMain2c600)
}
.frame(width: 50, height: 50)
.background(Color.grayMain2c200)
.clipShape(Circle())
}
VStack(spacing: 0) {
Spacer()
if !historyModel.isConf {
Text(historyModel.addressName)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
} else {
Text(historyModel.subject)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
}
HStack {
Image(historyListViewModel.getCallIconResId(callStatus: historyModel.status, isOutgoing: historyModel.isOutgoing))
.resizable()
.frame(
width: historyListViewModel.getCallIconResId(callStatus: historyModel.status, isOutgoing: historyModel.isOutgoing).contains("rejected") ? 12 : 8,
height: historyListViewModel.getCallIconResId(callStatus: historyModel.status, isOutgoing: historyModel.isOutgoing).contains("rejected") ? 6 : 8)
Text(historyListViewModel.getCallTime(startDate: historyModel.startDate))
.default_text_style_300(styleSize: 12)
.frame(maxWidth: .infinity, alignment: .leading)
Spacer()
}
Spacer()
}
if !historyModel.isConf {
Image("phone")
.resizable()
.frame(width: 25, height: 25)
.padding(.all, 10)
.padding(.trailing, 5)
.highPriorityGesture(
TapGesture()
.onEnded { _ in
withAnimation {
doCall(historyModel: historyModel)
SharedMainViewModel.shared.displayedCall = nil
}
}
)
}
}
}
.frame(height: 50)
.buttonStyle(.borderless)
.listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 20))
.listRowSeparator(.hidden)
.background(.white)
.onTapGesture {
withAnimation {
SharedMainViewModel.shared.displayedCall = historyModel
}
}
.onLongPressGesture(minimumDuration: 0.2) {
historyListViewModel.selectedCall = historyModel
showingSheet.toggle()
}
}
func doCall(historyModel: HistoryModel) {
telecomManager.doCallOrJoinConf(address: historyModel.addressLinphone)
}
}
#Preview {
HistoryListFragment(historyListViewModel: HistoryListViewModel(), historyViewModel: HistoryViewModel(), showingSheet: .constant(false), text: .constant(""))
HistoryListFragment(showingSheet: .constant(false), text: .constant(""))
}
// swiftlint:enable line_length

View file

@ -22,27 +22,20 @@ import linphonesw
struct HistoryView: View {
@ObservedObject var historyListViewModel: HistoryListViewModel
@ObservedObject var historyViewModel: HistoryViewModel
@ObservedObject var contactsListViewModel: ContactsListViewModel
@ObservedObject var editContactViewModel: EditContactViewModel
@EnvironmentObject var historyListViewModel: HistoryListViewModel
@Binding var index: Int
@Binding var isShowStartCallFragment: Bool
@Binding var isShowEditContactFragment: Bool
@Binding var text: String
@Binding var isShowEditContactFragmentAddress: String
var body: some View {
NavigationView {
ZStack(alignment: .bottomTrailing) {
HistoryFragment(
historyListViewModel: historyListViewModel,
historyViewModel: historyViewModel,
contactsListViewModel: contactsListViewModel,
editContactViewModel: editContactViewModel,
index: $index,
isShowEditContactFragment: $isShowEditContactFragment,
text: $text
text: $text,
isShowEditContactFragmentAddress: $isShowEditContactFragmentAddress
)
Button {
@ -72,12 +65,8 @@ struct HistoryView: View {
#Preview {
HistoryFragment(
historyListViewModel: HistoryListViewModel(),
historyViewModel: HistoryViewModel(),
contactsListViewModel: ContactsListViewModel(),
editContactViewModel: EditContactViewModel(),
index: .constant(1),
isShowEditContactFragment: .constant(false),
text: .constant("")
text: .constant(""),
isShowEditContactFragmentAddress: .constant("")
)
}

View file

@ -20,15 +20,17 @@
import Foundation
import linphonesw
class HistoryModel: ObservableObject {
class HistoryModel: ObservableObject, Identifiable {
private var coreContext = CoreContext.shared
static let TAG = "[History Model]"
let id = UUID()
var callLog: CallLog
var id: String
@Published var callLogId: String
@Published var subject: String
@Published var isConf: Bool
@Published var addressLinphone: Address
@ -43,7 +45,7 @@ class HistoryModel: ObservableObject {
init(callLog: CallLog) {
self.callLog = callLog
self.id = ""
self.callLogId = ""
self.subject = ""
self.isConf = false
@ -88,7 +90,7 @@ class HistoryModel: ObservableObject {
DispatchQueue.main.async {
self.callLog = callLogTmp
self.id = idTmp
self.callLogId = idTmp
self.subject = subjectTmp
self.isConf = isConfTmp
@ -111,34 +113,30 @@ class HistoryModel: ObservableObject {
}
func refreshAvatarModel() {
coreContext.doOnCoreQueue { _ in
guard let address = (self.callLog.dir == .Outgoing ? self.callLog.toAddress : self.callLog.fromAddress) else {
DispatchQueue.main.async {
self.avatarModel = ContactAvatarModel(friend: nil, name: self.addressName, address: self.address, withPresence: false)
}
return
guard let address = (self.callLog.dir == .Outgoing ? self.callLog.toAddress : self.callLog.fromAddress) else {
DispatchQueue.main.async {
self.avatarModel = ContactAvatarModel(friend: nil, name: self.addressName, address: self.address, withPresence: false)
}
return
}
let addressFriendTmp = ContactsManager.shared.getFriendWithAddress(address: address)
if let addressFriendTmp = addressFriendTmp {
let addressNameTmp = self.addressName
let addressFriendTmp = ContactsManager.shared.getFriendWithAddress(address: address)
if let addressFriendTmp = addressFriendTmp {
let avatarModelTmp = ContactsManager.shared.avatarListModel.first(where: {
guard let friend = $0.friend else { return false }
return friend.name == addressFriendTmp.name && friend.address?.asStringUriOnly() == addressFriendTmp.address?.asStringUriOnly()
}) ?? ContactAvatarModel(friend: nil, name: self.addressName, address: self.address, withPresence: false)
DispatchQueue.main.async {
self.addressFriend = addressFriendTmp
let addressNameTmp = self.addressName
let avatarModelTmp = ContactsManager.shared.avatarListModel.first(where: {
guard let friend = $0.friend else { return false }
return friend.name == addressFriendTmp.name && friend.address?.asStringUriOnly() == addressFriendTmp.address?.asStringUriOnly()
}) ?? ContactAvatarModel(friend: nil, name: self.addressName, address: self.address, withPresence: false)
DispatchQueue.main.async {
self.addressFriend = addressFriendTmp
self.addressName = addressFriendTmp.name ?? addressNameTmp
self.avatarModel = avatarModelTmp
}
} else {
DispatchQueue.main.async {
self.avatarModel = ContactAvatarModel(friend: nil, name: self.addressName, address: self.address, withPresence: false)
}
self.addressName = addressFriendTmp.name ?? addressNameTmp
self.avatarModel = avatarModelTmp
}
} else {
DispatchQueue.main.async {
self.avatarModel = ContactAvatarModel(friend: nil, name: self.addressName, address: self.address, withPresence: false)
}
}
}

View file

@ -33,6 +33,8 @@ class HistoryListViewModel: ObservableObject {
@Published var missedCallsCount: Int = 0
@Published var selectedCall: HistoryModel?
init() {
computeCallLogsList()
updateMissedCallsCount()
@ -247,8 +249,10 @@ class HistoryListViewModel: ObservableObject {
}
func refreshHistoryAvatarModel() {
callLogs.forEach { historyModel in
historyModel.refreshAvatarModel()
coreContext.doOnCoreQueue { _ in
self.callLogs.forEach { historyModel in
historyModel.refreshAvatarModel()
}
}
}
}

View file

@ -95,16 +95,14 @@ class AccountProfileViewModel: ObservableObject {
let accountDisplayName = CoreContext.shared.accounts[self.accountModelIndex!].account.displayName()
DispatchQueue.main.async {
CoreContext.shared.accounts[self.accountModelIndex!].avatarModel = ContactAvatarModel(
friend: nil,
name: displayNameTmp.isEmpty ? accountDisplayName : displayNameTmp,
address: contactAddressTmp,
withPresence: false
)
self.dialPlanValueSelected = dialPlanValueSelectedTmp
}
CoreContext.shared.accounts[self.accountModelIndex!].avatarModel = ContactAvatarModel(
friend: nil,
name: displayNameTmp.isEmpty ? accountDisplayName : displayNameTmp,
address: contactAddressTmp,
withPresence: false
)
self.dialPlanValueSelected = dialPlanValueSelectedTmp
}
}
}

View file

@ -30,7 +30,7 @@ class SharedMainViewModel: ObservableObject {
@Published var defaultAvatar: URL?
@Published var indexView: Int = 0
@Published var indexDisplayedFriend: Int?
@Published var displayedFriend: ContactAvatarModel?
@Published var displayedCall: HistoryModel?
@Published var displayedConversation: ConversationModel?
@Published var displayedMeeting: MeetingModel?

View file

@ -37,8 +37,11 @@ struct Avatar: View {
}
var body: some View {
if contactAvatarModel.friend != nil && contactAvatarModel.friend!.photo != nil {
AsyncImage(url: ContactsManager.shared.getImagePath(friendPhotoPath: contactAvatarModel.friend!.photo!)) { image in
if let photoPath = contactAvatarModel.friend?.photo {
let uniqueUrl = ContactsManager.shared.getImagePath(friendPhotoPath: photoPath)
let finalUrl = uniqueUrl.appendingQueryItem("v", value: UUID().uuidString)
AsyncImage(url: finalUrl) { image in
switch image {
case .empty:
ProgressView()

View file

@ -25,8 +25,15 @@ extension URL {
components?.scheme = value
return components?.url
}
var resourceSpecifier: String {
let nrl: NSURL = self as NSURL
return nrl.resourceSpecifier ?? self.absoluteString
}
func appendingQueryItem(_ name: String, value: String) -> URL {
guard var components = URLComponents(url: self, resolvingAgainstBaseURL: true) else { return self }
components.queryItems = (components.queryItems ?? []) + [URLQueryItem(name: name, value: value)]
return components.url ?? self
}
}

View file

@ -91,13 +91,14 @@ final class MagicSearchSingleton: ObservableObject {
}
}
self.contactsManager.avatarListModel.forEach { contactAvatarModel in
contactAvatarModel.removeFriendDelegate()
}
DispatchQueue.main.async {
self.contactsManager.lastSearch = sortedLastSearch
self.contactsManager.lastSearchSuggestions = lastSearchSuggestions
self.contactsManager.avatarListModel.forEach { contactAvatarModel in
contactAvatarModel.removeFriendDelegate()
}
self.contactsManager.avatarListModel.removeAll()
self.contactsManager.avatarListModel += addedAvatarListModel

View file

@ -24,7 +24,7 @@ import linphonesw
struct ShareSheet: UIViewControllerRepresentable {
typealias Callback = (_ activityType: UIActivity.ActivityType?, _ completed: Bool, _ returnedItems: [Any]?, _ error: Error?) -> Void
let friendToShare: Friend
let friendToShare: ContactAvatarModel
var activityItems: [Any] = []
let applicationActivities: [UIActivity]? = nil
let excludedActivityTypes: [UIActivity.ActivityType]? = nil
@ -34,25 +34,23 @@ struct ShareSheet: UIViewControllerRepresentable {
let directoryURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first
if directoryURL != nil {
if friendToShare.name != nil {
let filename = friendToShare.name!.replacingOccurrences(of: " ", with: "")
let fileURL = directoryURL!
.appendingPathComponent(filename)
.appendingPathExtension("vcf")
if friendToShare.vcard != nil {
try? friendToShare.vcard!.asVcard4String().write(to: fileURL, atomically: false, encoding: String.Encoding.utf8)
let controller = UIActivityViewController(
activityItems: [fileURL],
applicationActivities: applicationActivities
)
controller.excludedActivityTypes = excludedActivityTypes
controller.completionWithItemsHandler = callback
return controller
}
}
let filename = friendToShare.name.replacingOccurrences(of: " ", with: "")
let fileURL = directoryURL!
.appendingPathComponent(filename)
.appendingPathExtension("vcf")
if let vCard = friendToShare.vcard {
try? vCard.asVcard4String().write(to: fileURL, atomically: false, encoding: String.Encoding.utf8)
let controller = UIActivityViewController(
activityItems: [fileURL],
applicationActivities: applicationActivities
)
controller.excludedActivityTypes = excludedActivityTypes
controller.completionWithItemsHandler = callback
return controller
}
}
let controller = UIActivityViewController(

View file

@ -86,7 +86,6 @@
D71FCA812AE14CFC00D2E43E /* ContactsListFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71FCA802AE14CFC00D2E43E /* ContactsListFragment.swift */; };
D71FCA832AE14D6E00D2E43E /* ContactFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71FCA822AE14D6E00D2E43E /* ContactFragment.swift */; };
D720E6AD2BAD822000DDFD87 /* ParticipantModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D720E6AC2BAD822000DDFD87 /* ParticipantModel.swift */; };
D72250632ADE9615008FB426 /* HistoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72250622ADE9615008FB426 /* HistoryViewModel.swift */; };
D72250692ADFBF2D008FB426 /* SideMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72250682ADFBF2D008FB426 /* SideMenu.swift */; };
D72343302ACEFEF8009AA24E /* QrCodeScannerFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D723432F2ACEFEF8009AA24E /* QrCodeScannerFragment.swift */; };
D72343322ACEFF58009AA24E /* QRScannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72343312ACEFF58009AA24E /* QRScannerController.swift */; };
@ -298,7 +297,6 @@
D71FCA802AE14CFC00D2E43E /* ContactsListFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsListFragment.swift; sourceTree = "<group>"; };
D71FCA822AE14D6E00D2E43E /* ContactFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactFragment.swift; sourceTree = "<group>"; };
D720E6AC2BAD822000DDFD87 /* ParticipantModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantModel.swift; sourceTree = "<group>"; };
D72250622ADE9615008FB426 /* HistoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewModel.swift; sourceTree = "<group>"; };
D72250682ADFBF2D008FB426 /* SideMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenu.swift; sourceTree = "<group>"; };
D723432F2ACEFEF8009AA24E /* QrCodeScannerFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QrCodeScannerFragment.swift; sourceTree = "<group>"; };
D72343312ACEFF58009AA24E /* QRScannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScannerController.swift; sourceTree = "<group>"; };
@ -685,7 +683,6 @@
D72250612ADE95E4008FB426 /* ViewModel */ = {
isa = PBXGroup;
children = (
D72250622ADE9615008FB426 /* HistoryViewModel.swift */,
D732A9142B04C7FE00DB42BA /* HistoryListViewModel.swift */,
D726E43E2B19E56F0083C415 /* StartCallViewModel.swift */,
);
@ -1241,7 +1238,6 @@
D783028F2D414847009CCB60 /* DebugFragment.swift in Sources */,
66162A202BDFC2F900DCE913 /* AddParticipantsViewModel.swift in Sources */,
D732A91B2B061BD900DB42BA /* HistoryListBottomSheet.swift in Sources */,
D72250632ADE9615008FB426 /* HistoryViewModel.swift in Sources */,
D78E06302BEA6A4A00CE3783 /* ChangeLayoutBottomSheet.swift in Sources */,
66E56BC92BA4A6D7006CE56F /* MeetingsListViewModel.swift in Sources */,
D726E4392B16440C0083C415 /* ContactAvatarModel.swift in Sources */,