forked from mirrors/linphone-iphone
Add history call list
This commit is contained in:
parent
ae1ea15558
commit
ce9f6c454c
17 changed files with 978 additions and 100 deletions
|
|
@ -29,6 +29,10 @@
|
|||
D72343362AD037AF009AA24E /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72343352AD037AF009AA24E /* ToastView.swift */; };
|
||||
D72992392ADD7F68003AF125 /* HistoryContactFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72992382ADD7F68003AF125 /* HistoryContactFragment.swift */; };
|
||||
D732A9092AFD235500DB42BA /* ShareSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A9082AFD235500DB42BA /* ShareSheetController.swift */; };
|
||||
D732A90F2B04C3B400DB42BA /* HistoryFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A90E2B04C3B400DB42BA /* HistoryFragment.swift */; };
|
||||
D732A9132B04C7A300DB42BA /* HistoryListFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A9122B04C7A300DB42BA /* HistoryListFragment.swift */; };
|
||||
D732A9152B04C7FE00DB42BA /* HistoryListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A9142B04C7FE00DB42BA /* HistoryListViewModel.swift */; };
|
||||
D732A91B2B061BD900DB42BA /* HistoryListBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A91A2B061BD900DB42BA /* HistoryListBottomSheet.swift */; };
|
||||
D748BF2C2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */; };
|
||||
D748BF2E2ACD82E7004844EB /* ThirdPartySipAccountWarningFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D748BF2D2ACD82E7004844EB /* ThirdPartySipAccountWarningFragment.swift */; };
|
||||
D74C9CF82ACACECE0021626A /* WelcomePage1Fragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74C9CF72ACACECE0021626A /* WelcomePage1Fragment.swift */; };
|
||||
|
|
@ -95,6 +99,10 @@
|
|||
D72343352AD037AF009AA24E /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.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>"; };
|
||||
D732A90E2B04C3B400DB42BA /* HistoryFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryFragment.swift; sourceTree = "<group>"; };
|
||||
D732A9122B04C7A300DB42BA /* HistoryListFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryListFragment.swift; sourceTree = "<group>"; };
|
||||
D732A9142B04C7FE00DB42BA /* HistoryListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryListViewModel.swift; sourceTree = "<group>"; };
|
||||
D732A91A2B061BD900DB42BA /* HistoryListBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryListBottomSheet.swift; sourceTree = "<group>"; };
|
||||
D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThirdPartySipAccountLoginFragment.swift; sourceTree = "<group>"; };
|
||||
D748BF2D2ACD82E7004844EB /* ThirdPartySipAccountWarningFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThirdPartySipAccountWarningFragment.swift; sourceTree = "<group>"; };
|
||||
D74C9CF72ACACECE0021626A /* WelcomePage1Fragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomePage1Fragment.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -267,6 +275,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
D72250622ADE9615008FB426 /* HistoryViewModel.swift */,
|
||||
D732A9142B04C7FE00DB42BA /* HistoryListViewModel.swift */,
|
||||
);
|
||||
path = ViewModel;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -275,6 +284,9 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
D72992382ADD7F68003AF125 /* HistoryContactFragment.swift */,
|
||||
D732A90E2B04C3B400DB42BA /* HistoryFragment.swift */,
|
||||
D732A9122B04C7A300DB42BA /* HistoryListFragment.swift */,
|
||||
D732A91A2B061BD900DB42BA /* HistoryListBottomSheet.swift */,
|
||||
);
|
||||
path = Fragments;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -510,12 +522,14 @@
|
|||
D750D3392AD3E6EE00EC99C5 /* PopupLoadingView.swift in Sources */,
|
||||
D7E6D0492AE933AD00A57AAF /* FavoriteContactsListFragment.swift in Sources */,
|
||||
D706BA822ADD72D100278F45 /* DeviceRotationViewModifier.swift in Sources */,
|
||||
D732A9132B04C7A300DB42BA /* HistoryListFragment.swift in Sources */,
|
||||
D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */,
|
||||
D7EAACCF2AD6ED8000AA6A8A /* PermissionsFragment.swift in Sources */,
|
||||
D777DBB32AE12C5900565A99 /* ContactsManager.swift in Sources */,
|
||||
D7C3650A2AF001C300FE6142 /* EditContactFragment.swift in Sources */,
|
||||
D7A03FBD2ACC2DB60081A588 /* ContactsView.swift in Sources */,
|
||||
D719ABCF2ABC779A00B41C10 /* AccountLoginViewModel.swift in Sources */,
|
||||
D732A90F2B04C3B400DB42BA /* HistoryFragment.swift in Sources */,
|
||||
D78290BB2ADD40B2004AA85C /* ContactViewModel.swift in Sources */,
|
||||
D72992392ADD7F68003AF125 /* HistoryContactFragment.swift in Sources */,
|
||||
D7B5066D2AEFA9B900CEB4E9 /* ContactInnerFragment.swift in Sources */,
|
||||
|
|
@ -523,9 +537,11 @@
|
|||
D7C48DF42AFA66F900D938CB /* EditContactController.swift in Sources */,
|
||||
D74C9D012ACB098C0021626A /* PermissionManager.swift in Sources */,
|
||||
D7702EF22AC7205000557C00 /* WelcomeView.swift in Sources */,
|
||||
D732A9152B04C7FE00DB42BA /* HistoryListViewModel.swift in Sources */,
|
||||
D71FCA7F2AE1397200D2E43E /* ContactsListViewModel.swift in Sources */,
|
||||
D71FCA812AE14CFC00D2E43E /* ContactsListFragment.swift in Sources */,
|
||||
D719ABB72ABC67BF00B41C10 /* LinphoneApp.swift in Sources */,
|
||||
D732A91B2B061BD900DB42BA /* HistoryListBottomSheet.swift in Sources */,
|
||||
D72250632ADE9615008FB426 /* HistoryViewModel.swift in Sources */,
|
||||
D7E6D0512AEBDBD500A57AAF /* ContactsListBottomSheet.swift in Sources */,
|
||||
D7A2EDD62AC18115005D90FC /* SharedMainViewModel.swift in Sources */,
|
||||
|
|
|
|||
21
Linphone/Assets.xcassets/trash-simple-red.imageset/Contents.json
vendored
Normal file
21
Linphone/Assets.xcassets/trash-simple-red.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "trash-simple-red.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
3
Linphone/Assets.xcassets/trash-simple-red.imageset/trash-simple-red.svg
vendored
Normal file
3
Linphone/Assets.xcassets/trash-simple-red.imageset/trash-simple-red.svg
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.3333 5.76923H4.66667C4.48986 5.76923 4.32029 5.84217 4.19526 5.972C4.07024 6.10184 4 6.27793 4 6.46154C4 6.64515 4.07024 6.82124 4.19526 6.95107C4.32029 7.08091 4.48986 7.15385 4.66667 7.15385H5.33333V19.6154C5.33333 19.9826 5.47381 20.3348 5.72386 20.5945C5.97391 20.8541 6.31304 21 6.66667 21H17.3333C17.687 21 18.0261 20.8541 18.2761 20.5945C18.5262 20.3348 18.6667 19.9826 18.6667 19.6154V7.15385H19.3333C19.5101 7.15385 19.6797 7.08091 19.8047 6.95107C19.9298 6.82124 20 6.64515 20 6.46154C20 6.27793 19.9298 6.10184 19.8047 5.972C19.6797 5.84217 19.5101 5.76923 19.3333 5.76923ZM17.3333 19.6154H6.66667V7.15385H17.3333V19.6154ZM8 3.69231C8 3.5087 8.07024 3.33261 8.19526 3.20277C8.32029 3.07294 8.48986 3 8.66667 3H15.3333C15.5101 3 15.6797 3.07294 15.8047 3.20277C15.9298 3.33261 16 3.5087 16 3.69231C16 3.87592 15.9298 4.05201 15.8047 4.18184C15.6797 4.31168 15.5101 4.38462 15.3333 4.38462H8.66667C8.48986 4.38462 8.32029 4.31168 8.19526 4.18184C8.07024 4.05201 8 3.87592 8 3.69231Z" fill="#DD5F5F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -271,7 +271,7 @@ final class ContactsManager {
|
|||
}
|
||||
}
|
||||
|
||||
func getFriend(contact: Contact) -> Friend? {
|
||||
func getFriendWithContact(contact: Contact) -> Friend? {
|
||||
if friendList != nil {
|
||||
let friend = friendList!.friends.first(where: {$0.nativeUri == contact.identifier})
|
||||
return friend
|
||||
|
|
@ -279,6 +279,18 @@ final class ContactsManager {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func getFriendWithAddress(address: Address) -> Friend? {
|
||||
if friendList != nil {
|
||||
var friend = friendList!.friends.first(where: {$0.addresses.contains(where: {$0.asStringUriOnly() == address.asStringUriOnly()})})
|
||||
if friend == nil {
|
||||
friend = linphoneFriendList!.friends.first(where: {$0.addresses.contains(where: {$0.asStringUriOnly() == address.asStringUriOnly()})})
|
||||
}
|
||||
return friend
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PhoneNumber {
|
||||
|
|
|
|||
|
|
@ -36,8 +36,13 @@ struct LinphoneApp: App {
|
|||
AssistantView(sharedMainViewModel: sharedMainViewModel)
|
||||
.toast(isShowing: $coreContext.toastMessage)
|
||||
} else if coreContext.defaultAccount != nil {
|
||||
ContentView(contactViewModel: ContactViewModel(), editContactViewModel: EditContactViewModel(), historyViewModel: HistoryViewModel())
|
||||
.toast(isShowing: $coreContext.toastMessage)
|
||||
ContentView(
|
||||
contactViewModel: ContactViewModel(),
|
||||
editContactViewModel: EditContactViewModel(),
|
||||
historyViewModel: HistoryViewModel(),
|
||||
historyListViewModel: HistoryListViewModel()
|
||||
)
|
||||
.toast(isShowing: $coreContext.toastMessage)
|
||||
}
|
||||
} else {
|
||||
SplashScreen(isActive: $isActive)
|
||||
|
|
|
|||
|
|
@ -104,9 +104,15 @@
|
|||
},
|
||||
"Add a picture" : {
|
||||
|
||||
},
|
||||
"Add the contact" : {
|
||||
|
||||
},
|
||||
"Add to favourites" : {
|
||||
|
||||
},
|
||||
"All calls will be removed from the history." : {
|
||||
|
||||
},
|
||||
"All contacts" : {
|
||||
|
||||
|
|
@ -172,6 +178,9 @@
|
|||
},
|
||||
"Copy number" : {
|
||||
|
||||
},
|
||||
"Copy SIP address" : {
|
||||
|
||||
},
|
||||
"D'accord" : {
|
||||
|
||||
|
|
@ -187,6 +196,9 @@
|
|||
},
|
||||
"Delete %@?" : {
|
||||
|
||||
},
|
||||
"Delete all history" : {
|
||||
|
||||
},
|
||||
"Delete this contact" : {
|
||||
|
||||
|
|
@ -199,6 +211,9 @@
|
|||
},
|
||||
"Display Name" : {
|
||||
|
||||
},
|
||||
"Do you really want to delete all calls history?" : {
|
||||
|
||||
},
|
||||
"Domain" : {
|
||||
|
||||
|
|
@ -293,7 +308,7 @@
|
|||
"Next" : {
|
||||
|
||||
},
|
||||
"No calls for the moment..." : {
|
||||
"No call for the moment..." : {
|
||||
|
||||
},
|
||||
"No contacts for the moment..." : {
|
||||
|
|
@ -372,6 +387,9 @@
|
|||
},
|
||||
"See all" : {
|
||||
|
||||
},
|
||||
"See contact" : {
|
||||
|
||||
},
|
||||
"See Linphone contact" : {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,21 @@
|
|||
//
|
||||
// ContactInnerActionsFragment.swift
|
||||
// Linphone
|
||||
//
|
||||
// Created by Benoît Martins on 09/11/2023.
|
||||
//
|
||||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
|
||||
|
|
|
|||
|
|
@ -42,10 +42,10 @@ struct ContactsFragment: View {
|
|||
showingSheet: $showingSheet,
|
||||
showShareSheet: $showShareSheet
|
||||
)
|
||||
.presentationDetents([.fraction(0.2)])
|
||||
.presentationDetents([.fraction(0.2)])
|
||||
}
|
||||
.sheet(isPresented: $showShareSheet) {
|
||||
ShareSheet(friendToShare: contactViewModel.selectedFriendToShare!)
|
||||
ShareSheet(friendToShare: contactViewModel.selectedFriendToShare!)
|
||||
.presentationDetents([.medium])
|
||||
.edgesIgnoringSafeArea(.bottom)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,10 +20,7 @@
|
|||
import SwiftUI
|
||||
import linphonesw
|
||||
|
||||
struct ContactsInnerFragment: View {
|
||||
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
|
||||
struct ContactsInnerFragment: View {
|
||||
@ObservedObject var magicSearch = MagicSearchSingleton.shared
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
|
||||
|
|
@ -76,16 +73,6 @@ struct ContactsInnerFragment: View {
|
|||
ContactsListFragment(contactViewModel: contactViewModel, contactsListViewModel: ContactsListViewModel(), showingSheet: $showingSheet)
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
.onChange(of: scenePhase) { newPhase in
|
||||
if newPhase == .active {
|
||||
ContactsManager.shared.fetchContacts()
|
||||
print("Active")
|
||||
} else if newPhase == .inactive {
|
||||
print("Inactive")
|
||||
} else if newPhase == .background {
|
||||
print("Background")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,18 +17,23 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// swiftlint:disable type_body_length
|
||||
import SwiftUI
|
||||
import linphonesw
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
|
||||
@ObservedObject private var coreContext = CoreContext.shared
|
||||
|
||||
var contactManager = ContactsManager.shared
|
||||
var magicSearch = MagicSearchSingleton.shared
|
||||
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
@ObservedObject var editContactViewModel: EditContactViewModel
|
||||
@ObservedObject var historyViewModel: HistoryViewModel
|
||||
@ObservedObject private var coreContext = CoreContext.shared
|
||||
@ObservedObject var historyListViewModel: HistoryListViewModel
|
||||
|
||||
@State var index = 0
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
|
|
@ -38,7 +43,8 @@ struct ContentView: View {
|
|||
@State private var text = ""
|
||||
@FocusState private var focusedField: Bool
|
||||
@State var isMenuOpen = false
|
||||
@State var isShowDeletePopup = false
|
||||
@State var isShowDeleteContactPopup = false
|
||||
@State var isShowDeleteAllHistoryPopup = false
|
||||
@State var isShowEditContactFragment = false
|
||||
@State var isShowDismissPopup = false
|
||||
|
||||
|
|
@ -134,34 +140,50 @@ struct ContentView: View {
|
|||
}
|
||||
|
||||
Menu {
|
||||
Button {
|
||||
isMenuOpen = false
|
||||
magicSearch.allContact = true
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} label: {
|
||||
HStack {
|
||||
Text("See all")
|
||||
Spacer()
|
||||
if magicSearch.allContact {
|
||||
Image("green-check")
|
||||
.resizable()
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
if index == 0 {
|
||||
Button {
|
||||
isMenuOpen = false
|
||||
magicSearch.allContact = true
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} label: {
|
||||
HStack {
|
||||
Text("See all")
|
||||
Spacer()
|
||||
if magicSearch.allContact {
|
||||
Image("green-check")
|
||||
.resizable()
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
isMenuOpen = false
|
||||
magicSearch.allContact = false
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} label: {
|
||||
HStack {
|
||||
Text("See Linphone contact")
|
||||
Spacer()
|
||||
if !magicSearch.allContact {
|
||||
Image("green-check")
|
||||
|
||||
Button {
|
||||
isMenuOpen = false
|
||||
magicSearch.allContact = false
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} label: {
|
||||
HStack {
|
||||
Text("See Linphone contact")
|
||||
Spacer()
|
||||
if !magicSearch.allContact {
|
||||
Image("green-check")
|
||||
.resizable()
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button(role: .destructive) {
|
||||
isMenuOpen = false
|
||||
isShowDeleteAllHistoryPopup.toggle()
|
||||
//historyListViewModel.removeCallLogs()
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Delete all history")
|
||||
Spacer()
|
||||
Image("trash-simple-red")
|
||||
.resizable()
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
}
|
||||
|
|
@ -193,9 +215,14 @@ struct ContentView: View {
|
|||
}
|
||||
|
||||
text = ""
|
||||
magicSearch.currentFilter = ""
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
|
||||
if index == 0 {
|
||||
magicSearch.currentFilter = ""
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} else {
|
||||
historyListViewModel.resetFilterCallLogs()
|
||||
}
|
||||
} label: {
|
||||
Image("caret-left")
|
||||
.renderingMode(.template)
|
||||
|
|
@ -226,9 +253,13 @@ struct ContentView: View {
|
|||
self.focusedField = true
|
||||
}
|
||||
.onChange(of: text) { newValue in
|
||||
magicSearch.currentFilter = newValue
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
if index == 0 {
|
||||
magicSearch.currentFilter = newValue
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} else {
|
||||
historyListViewModel.filterCallLogs(filter: text)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TextEditor(text: Binding(
|
||||
|
|
@ -281,10 +312,17 @@ struct ContentView: View {
|
|||
historyViewModel: historyViewModel,
|
||||
editContactViewModel: editContactViewModel,
|
||||
isShowEditContactFragment: $isShowEditContactFragment,
|
||||
isShowDeletePopup: $isShowDeletePopup
|
||||
isShowDeletePopup: $isShowDeleteContactPopup
|
||||
)
|
||||
} else if self.index == 1 {
|
||||
HistoryView()
|
||||
HistoryView(
|
||||
historyListViewModel: historyListViewModel,
|
||||
historyViewModel: historyViewModel,
|
||||
contactViewModel: contactViewModel,
|
||||
editContactViewModel: editContactViewModel,
|
||||
index: $index,
|
||||
isShowEditContactFragment: $isShowEditContactFragment
|
||||
)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth:
|
||||
|
|
@ -367,7 +405,7 @@ struct ContentView: View {
|
|||
}
|
||||
}
|
||||
|
||||
if contactViewModel.indexDisplayedFriend != nil || !historyViewModel.historyTitle.isEmpty {
|
||||
if contactViewModel.indexDisplayedFriend != nil || historyViewModel.indexDisplayedCall != nil {
|
||||
HStack(spacing: 0) {
|
||||
Spacer()
|
||||
.frame(maxWidth:
|
||||
|
|
@ -381,12 +419,12 @@ struct ContentView: View {
|
|||
ContactFragment(
|
||||
contactViewModel: contactViewModel,
|
||||
editContactViewModel: editContactViewModel,
|
||||
isShowDeletePopup: $isShowDeletePopup,
|
||||
isShowDeletePopup: $isShowDeleteContactPopup,
|
||||
isShowDismissPopup: $isShowDismissPopup
|
||||
)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color.gray100)
|
||||
.ignoresSafeArea(.keyboard)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color.gray100)
|
||||
.ignoresSafeArea(.keyboard)
|
||||
} else if self.index == 1 {
|
||||
HistoryContactFragment()
|
||||
.frame(maxWidth: .infinity)
|
||||
|
|
@ -433,25 +471,25 @@ struct ContentView: View {
|
|||
isShowEditContactFragment: $isShowEditContactFragment,
|
||||
isShowDismissPopup: $isShowDismissPopup
|
||||
)
|
||||
.zIndex(3)
|
||||
.transition(.move(edge: .bottom))
|
||||
.onAppear {
|
||||
contactViewModel.indexDisplayedFriend = nil
|
||||
}
|
||||
.zIndex(3)
|
||||
.transition(.move(edge: .bottom))
|
||||
.onAppear {
|
||||
contactViewModel.indexDisplayedFriend = nil
|
||||
}
|
||||
}
|
||||
|
||||
if isShowDeletePopup {
|
||||
PopupView(sharedMainViewModel: SharedMainViewModel(), isShowPopup: $isShowDeletePopup,
|
||||
if isShowDeleteContactPopup {
|
||||
PopupView(sharedMainViewModel: SharedMainViewModel(), isShowPopup: $isShowDeleteContactPopup,
|
||||
title: Text(
|
||||
contactViewModel.selectedFriend != nil
|
||||
? "Delete \(contactViewModel.selectedFriend!.name!)?"
|
||||
: (contactViewModel.indexDisplayedFriend != nil
|
||||
? "Delete \(magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.name!)?"
|
||||
: "Error Name")),
|
||||
contactViewModel.selectedFriend != nil
|
||||
? "Delete \(contactViewModel.selectedFriend!.name!)?"
|
||||
: (contactViewModel.indexDisplayedFriend != nil
|
||||
? "Delete \(magicSearch.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.name!)?"
|
||||
: "Error Name")),
|
||||
content: Text("This contact will be deleted definitively."),
|
||||
titleFirstButton: Text("Cancel"),
|
||||
actionFirstButton: {
|
||||
self.isShowDeletePopup.toggle()},
|
||||
self.isShowDeleteContactPopup.toggle()},
|
||||
titleSecondButton: Text("Ok"),
|
||||
actionSecondButton: {
|
||||
if contactViewModel.selectedFriendToDelete != nil {
|
||||
|
|
@ -470,18 +508,37 @@ struct ContentView: View {
|
|||
}
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
self.isShowDeletePopup.toggle()
|
||||
self.isShowDeleteContactPopup.toggle()
|
||||
})
|
||||
.background(.black.opacity(0.65))
|
||||
.zIndex(3)
|
||||
.onTapGesture {
|
||||
self.isShowDeletePopup.toggle()
|
||||
self.isShowDeleteContactPopup.toggle()
|
||||
}
|
||||
.onAppear {
|
||||
contactViewModel.selectedFriendToDelete = contactViewModel.selectedFriend
|
||||
}
|
||||
}
|
||||
|
||||
if isShowDeleteAllHistoryPopup {
|
||||
PopupView(sharedMainViewModel: SharedMainViewModel(), isShowPopup: $isShowDeleteContactPopup,
|
||||
title: Text("Do you really want to delete all calls history?"),
|
||||
content: Text("All calls will be removed from the history."),
|
||||
titleFirstButton: Text("Cancel"),
|
||||
actionFirstButton: {
|
||||
self.isShowDeleteAllHistoryPopup.toggle()},
|
||||
titleSecondButton: Text("Ok"),
|
||||
actionSecondButton: {
|
||||
historyListViewModel.removeCallLogs()
|
||||
self.isShowDeleteAllHistoryPopup.toggle()
|
||||
})
|
||||
.background(.black.opacity(0.65))
|
||||
.zIndex(3)
|
||||
.onTapGesture {
|
||||
self.isShowDeleteAllHistoryPopup.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
if isShowDismissPopup {
|
||||
PopupView(sharedMainViewModel: SharedMainViewModel(), isShowPopup: $isShowDismissPopup,
|
||||
title: Text("Don’t save modifications?"),
|
||||
|
|
@ -524,13 +581,23 @@ struct ContentView: View {
|
|||
}
|
||||
}
|
||||
.onRotate { newOrientation in
|
||||
if (contactViewModel.indexDisplayedFriend != nil || !historyViewModel.historyTitle.isEmpty) && searchIsActive {
|
||||
if (contactViewModel.indexDisplayedFriend != nil || historyViewModel.indexDisplayedCall != nil) && searchIsActive {
|
||||
self.focusedField = false
|
||||
} else if searchIsActive {
|
||||
self.focusedField = true
|
||||
}
|
||||
orientation = newOrientation
|
||||
}
|
||||
.onChange(of: scenePhase) { newPhase in
|
||||
if newPhase == .active {
|
||||
ContactsManager.shared.fetchContacts()
|
||||
print("Active")
|
||||
} else if newPhase == .inactive {
|
||||
print("Inactive")
|
||||
} else if newPhase == .background {
|
||||
print("Background")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openMenu() {
|
||||
|
|
@ -541,5 +608,11 @@ struct ContentView: View {
|
|||
}
|
||||
|
||||
#Preview {
|
||||
ContentView(contactViewModel: ContactViewModel(), editContactViewModel: EditContactViewModel(), historyViewModel: HistoryViewModel())
|
||||
ContentView(
|
||||
contactViewModel: ContactViewModel(),
|
||||
editContactViewModel: EditContactViewModel(),
|
||||
historyViewModel: HistoryViewModel(),
|
||||
historyListViewModel: HistoryListViewModel()
|
||||
)
|
||||
}
|
||||
// swiftlint:enable type_body_length
|
||||
|
|
|
|||
92
Linphone/UI/Main/History/Fragments/HistoryFragment.swift
Normal file
92
Linphone/UI/Main/History/Fragments/HistoryFragment.swift
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct HistoryFragment: View {
|
||||
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
|
||||
|
||||
@ObservedObject var historyListViewModel: HistoryListViewModel
|
||||
@ObservedObject var historyViewModel: HistoryViewModel
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
@ObservedObject var editContactViewModel: EditContactViewModel
|
||||
|
||||
@State private var showingSheet = false
|
||||
@Binding var index: Int
|
||||
@Binding var isShowEditContactFragment: Bool
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
if #available(iOS 16.0, *) {
|
||||
if idiom != .pad {
|
||||
HistoryListFragment(historyListViewModel: historyListViewModel, historyViewModel: historyViewModel, showingSheet: $showingSheet)
|
||||
.sheet(isPresented: $showingSheet) {
|
||||
HistoryListBottomSheet(
|
||||
historyViewModel: historyViewModel,
|
||||
contactViewModel: contactViewModel,
|
||||
editContactViewModel: editContactViewModel,
|
||||
historyListViewModel: historyListViewModel,
|
||||
showingSheet: $showingSheet,
|
||||
index: $index,
|
||||
isShowEditContactFragment: $isShowEditContactFragment
|
||||
)
|
||||
.presentationDetents([.fraction(0.2)])
|
||||
}
|
||||
} else {
|
||||
HistoryListFragment(historyListViewModel: historyListViewModel, historyViewModel: historyViewModel, showingSheet: $showingSheet)
|
||||
.halfSheet(showSheet: $showingSheet) {
|
||||
HistoryListBottomSheet(
|
||||
historyViewModel: historyViewModel,
|
||||
contactViewModel: contactViewModel,
|
||||
editContactViewModel: editContactViewModel,
|
||||
historyListViewModel: historyListViewModel,
|
||||
showingSheet: $showingSheet,
|
||||
index: $index,
|
||||
isShowEditContactFragment: $isShowEditContactFragment
|
||||
)
|
||||
} onDismiss: {}
|
||||
}
|
||||
} else {
|
||||
HistoryListFragment(historyListViewModel: historyListViewModel, historyViewModel: historyViewModel, showingSheet: $showingSheet)
|
||||
.halfSheet(showSheet: $showingSheet) {
|
||||
HistoryListBottomSheet(
|
||||
historyViewModel: historyViewModel,
|
||||
contactViewModel: contactViewModel,
|
||||
editContactViewModel: editContactViewModel,
|
||||
historyListViewModel: historyListViewModel,
|
||||
showingSheet: $showingSheet,
|
||||
index: $index,
|
||||
isShowEditContactFragment: $isShowEditContactFragment
|
||||
)
|
||||
} onDismiss: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
HistoryFragment(
|
||||
historyListViewModel: HistoryListViewModel(),
|
||||
historyViewModel: HistoryViewModel(),
|
||||
contactViewModel: ContactViewModel(),
|
||||
editContactViewModel: EditContactViewModel(),
|
||||
index: .constant(1),
|
||||
isShowEditContactFragment: .constant(false)
|
||||
)
|
||||
}
|
||||
240
Linphone/UI/Main/History/Fragments/HistoryListBottomSheet.swift
Normal file
240
Linphone/UI/Main/History/Fragments/HistoryListBottomSheet.swift
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
struct HistoryListBottomSheet: View {
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
|
||||
|
||||
@ObservedObject var historyViewModel: HistoryViewModel
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
@ObservedObject var editContactViewModel: EditContactViewModel
|
||||
@ObservedObject var historyListViewModel: HistoryListViewModel
|
||||
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
|
||||
@Binding var showingSheet: Bool
|
||||
@Binding var index: Int
|
||||
@Binding var isShowEditContactFragment: Bool
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
if idiom != .pad && (orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) {
|
||||
Spacer()
|
||||
HStack {
|
||||
Spacer()
|
||||
Button("Close") {
|
||||
if #available(iOS 16.0, *) {
|
||||
showingSheet.toggle()
|
||||
} else {
|
||||
showingSheet.toggle()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.trailing)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
Button {
|
||||
|
||||
if #available(iOS 16.0, *) {
|
||||
if idiom != .pad {
|
||||
showingSheet.toggle()
|
||||
} else {
|
||||
showingSheet.toggle()
|
||||
dismiss()
|
||||
}
|
||||
} else {
|
||||
showingSheet.toggle()
|
||||
dismiss()
|
||||
}
|
||||
|
||||
index = 0
|
||||
|
||||
if ContactsManager.shared.getFriendWithAddress(
|
||||
address: historyViewModel.selectedCall != nil && historyViewModel.selectedCall!.dir == .Outgoing
|
||||
? historyViewModel.selectedCall!.toAddress!
|
||||
: historyViewModel.selectedCall!.fromAddress!
|
||||
) != nil {
|
||||
let addressCall = historyViewModel.selectedCall != nil && historyViewModel.selectedCall!.dir == .Outgoing
|
||||
? historyViewModel.selectedCall!.toAddress!
|
||||
: historyViewModel.selectedCall!.fromAddress!
|
||||
|
||||
let friendIndex = MagicSearchSingleton.shared.lastSearch.firstIndex(where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressCall.asStringUriOnly()})})
|
||||
if friendIndex != nil {
|
||||
withAnimation {
|
||||
contactViewModel.indexDisplayedFriend = friendIndex
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let addressCall = historyViewModel.selectedCall != nil && historyViewModel.selectedCall!.dir == .Outgoing
|
||||
? historyViewModel.selectedCall!.toAddress!
|
||||
: historyViewModel.selectedCall!.fromAddress!
|
||||
|
||||
withAnimation {
|
||||
isShowEditContactFragment.toggle()
|
||||
editContactViewModel.sipAddresses.removeAll()
|
||||
editContactViewModel.sipAddresses.append(String(addressCall.asStringUriOnly().dropFirst(4)))
|
||||
editContactViewModel.sipAddresses.append("")
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
if ContactsManager.shared.getFriendWithAddress(
|
||||
address: historyViewModel.selectedCall != nil && historyViewModel.selectedCall!.dir == .Outgoing
|
||||
? historyViewModel.selectedCall!.toAddress!
|
||||
: historyViewModel.selectedCall!.fromAddress!
|
||||
) != nil {
|
||||
Image("user-circle")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.grayMain2c500)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
Text("See contact")
|
||||
.default_text_style(styleSize: 16)
|
||||
Spacer()
|
||||
} else {
|
||||
Image("plus-circle")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.grayMain2c500)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
Text("Add the contact")
|
||||
.default_text_style(styleSize: 16)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
}
|
||||
.padding(.horizontal, 30)
|
||||
.background(Color.gray100)
|
||||
|
||||
VStack {
|
||||
Divider()
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
Button {
|
||||
if historyViewModel.selectedCall != nil && historyViewModel.selectedCall!.dir == .Outgoing {
|
||||
UIPasteboard.general.setValue(
|
||||
historyViewModel.selectedCall!.toAddress!.asStringUriOnly().dropFirst(4),
|
||||
forPasteboardType: UTType.plainText.identifier
|
||||
)
|
||||
} else {
|
||||
UIPasteboard.general.setValue(
|
||||
historyViewModel.selectedCall!.fromAddress!.asStringUriOnly().dropFirst(4),
|
||||
forPasteboardType: UTType.plainText.identifier
|
||||
)
|
||||
}
|
||||
|
||||
if #available(iOS 16.0, *) {
|
||||
if idiom != .pad {
|
||||
showingSheet.toggle()
|
||||
} else {
|
||||
showingSheet.toggle()
|
||||
dismiss()
|
||||
}
|
||||
} else {
|
||||
showingSheet.toggle()
|
||||
dismiss()
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Image("copy")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.grayMain2c500)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
Text("Copy SIP address")
|
||||
.default_text_style(styleSize: 16)
|
||||
Spacer()
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
}
|
||||
.padding(.horizontal, 30)
|
||||
.background(Color.gray100)
|
||||
|
||||
VStack {
|
||||
Divider()
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
Button {
|
||||
CoreContext.shared.doOnCoreQueue { core in
|
||||
if historyViewModel.selectedCall != nil {
|
||||
core.removeCallLog(callLog: historyViewModel.selectedCall!)
|
||||
historyListViewModel.removeCallLog(callLog: historyViewModel.selectedCall!)
|
||||
}
|
||||
}
|
||||
|
||||
if #available(iOS 16.0, *) {
|
||||
if idiom != .pad {
|
||||
showingSheet.toggle()
|
||||
} else {
|
||||
showingSheet.toggle()
|
||||
dismiss()
|
||||
}
|
||||
} else {
|
||||
showingSheet.toggle()
|
||||
dismiss()
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Image("trash-simple")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.redDanger500)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
Text("Delete")
|
||||
.foregroundStyle(Color.redDanger500)
|
||||
.default_text_style(styleSize: 16)
|
||||
Spacer()
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
}
|
||||
.padding(.horizontal, 30)
|
||||
.background(Color.gray100)
|
||||
|
||||
}
|
||||
.background(Color.gray100)
|
||||
.frame(maxWidth: .infinity)
|
||||
.onRotate { newOrientation in
|
||||
orientation = newOrientation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
HistoryListBottomSheet(
|
||||
historyViewModel: HistoryViewModel(),
|
||||
contactViewModel: ContactViewModel(),
|
||||
editContactViewModel: EditContactViewModel(),
|
||||
historyListViewModel: HistoryListViewModel(),
|
||||
showingSheet: .constant(false),
|
||||
index: .constant(1),
|
||||
isShowEditContactFragment: .constant(false)
|
||||
)
|
||||
}
|
||||
232
Linphone/UI/Main/History/Fragments/HistoryListFragment.swift
Normal file
232
Linphone/UI/Main/History/Fragments/HistoryListFragment.swift
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import linphonesw
|
||||
|
||||
struct HistoryListFragment: View {
|
||||
|
||||
@ObservedObject var historyListViewModel: HistoryListViewModel
|
||||
@ObservedObject var historyViewModel: HistoryViewModel
|
||||
|
||||
@Binding var showingSheet: Bool
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
List {
|
||||
ForEach(0..<historyListViewModel.callLogs.count, id: \.self) { index in
|
||||
Button {
|
||||
} label: {
|
||||
HStack {
|
||||
let fromAddressFriend = ContactsManager.shared.getFriendWithAddress(address: historyListViewModel.callLogs[index].fromAddress!)
|
||||
let toAddressFriend = ContactsManager.shared.getFriendWithAddress(address: historyListViewModel.callLogs[index].toAddress!)
|
||||
|
||||
if historyListViewModel.callLogs[index].dir == .Incoming && fromAddressFriend != nil && fromAddressFriend!.photo != nil && !fromAddressFriend!.photo!.isEmpty {
|
||||
AsyncImage(url:
|
||||
ContactsManager.shared.getImagePath(
|
||||
friendPhotoPath: fromAddressFriend!.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()
|
||||
}
|
||||
}
|
||||
} else if historyListViewModel.callLogs[index].dir == .Outgoing && toAddressFriend != nil && toAddressFriend!.photo != nil && !toAddressFriend!.photo!.isEmpty {
|
||||
AsyncImage(url:
|
||||
ContactsManager.shared.getImagePath(
|
||||
friendPhotoPath: toAddressFriend!.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()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if historyListViewModel.callLogs[index].dir == .Outgoing && historyListViewModel.callLogs[index].toAddress != nil {
|
||||
if historyListViewModel.callLogs[index].toAddress!.displayName != nil {
|
||||
Image(uiImage: ContactsManager.shared.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]
|
||||
: ""))
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
|
||||
} else {
|
||||
Image(uiImage: ContactsManager.shared.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]
|
||||
: ""))
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
|
||||
} else if historyListViewModel.callLogs[index].fromAddress != nil {
|
||||
if historyListViewModel.callLogs[index].fromAddress!.displayName != nil {
|
||||
Image(uiImage: ContactsManager.shared.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]
|
||||
: ""))
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
} else {
|
||||
Image(uiImage: ContactsManager.shared.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]
|
||||
: ""))
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VStack(spacing: 0) {
|
||||
Spacer()
|
||||
|
||||
let fromAddressFriend = ContactsManager.shared.getFriendWithAddress(address: historyListViewModel.callLogs[index].fromAddress!)
|
||||
let toAddressFriend = ContactsManager.shared.getFriendWithAddress(address: historyListViewModel.callLogs[index].toAddress!)
|
||||
|
||||
if historyListViewModel.callLogs[index].dir == .Incoming && fromAddressFriend != nil {
|
||||
Text(fromAddressFriend!.name!)
|
||||
.default_text_style(styleSize: 14)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.lineLimit(1)
|
||||
} else if historyListViewModel.callLogs[index].dir == .Outgoing && toAddressFriend != nil {
|
||||
Text(toAddressFriend!.name!)
|
||||
.default_text_style(styleSize: 14)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.lineLimit(1)
|
||||
} else {
|
||||
if historyListViewModel.callLogs[index].dir == .Outgoing && historyListViewModel.callLogs[index].toAddress != nil {
|
||||
Text(historyListViewModel.callLogs[index].toAddress!.displayName != nil
|
||||
? historyListViewModel.callLogs[index].toAddress!.displayName!
|
||||
: historyListViewModel.callLogs[index].toAddress!.username!)
|
||||
.default_text_style(styleSize: 14)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.lineLimit(1)
|
||||
} else if historyListViewModel.callLogs[index].fromAddress != nil {
|
||||
Text(historyListViewModel.callLogs[index].fromAddress!.displayName != nil
|
||||
? historyListViewModel.callLogs[index].fromAddress!.displayName!
|
||||
: historyListViewModel.callLogs[index].fromAddress!.username!)
|
||||
.default_text_style(styleSize: 14)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Image(historyListViewModel.getCallIconResId(callStatus: historyListViewModel.callLogs[index].status, callDir: historyListViewModel.callLogs[index].dir))
|
||||
.resizable()
|
||||
.frame(
|
||||
width: historyListViewModel.getCallIconResId(callStatus: historyListViewModel.callLogs[index].status, callDir: historyListViewModel.callLogs[index].dir).contains("rejected") ? 12 : 8,
|
||||
height: historyListViewModel.getCallIconResId(callStatus: historyListViewModel.callLogs[index].status, callDir: historyListViewModel.callLogs[index].dir).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()
|
||||
}
|
||||
|
||||
Image("phone")
|
||||
.resizable()
|
||||
.frame(width: 25, height: 25)
|
||||
.padding(.trailing, 5)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
.listRowInsets(EdgeInsets(top: 5, leading: 20, bottom: 5, trailing: 20))
|
||||
.listRowSeparator(.hidden)
|
||||
.simultaneousGesture(
|
||||
LongPressGesture()
|
||||
.onEnded { _ in
|
||||
historyViewModel.selectedCall = historyListViewModel.callLogs[index]
|
||||
showingSheet.toggle()
|
||||
}
|
||||
)
|
||||
.highPriorityGesture(
|
||||
TapGesture()
|
||||
.onEnded { _ in
|
||||
withAnimation {
|
||||
//historyViewModel.indexDisplayedCall = index
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.overlay(
|
||||
VStack {
|
||||
if historyListViewModel.callLogs.isEmpty {
|
||||
Spacer()
|
||||
Image("illus-belledonne")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.clipped()
|
||||
.padding(.all)
|
||||
Text("No call for the moment...")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
Spacer()
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.padding(.all)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
HistoryListFragment(historyListViewModel: HistoryListViewModel(), historyViewModel: HistoryViewModel(), showingSheet: .constant(false))
|
||||
}
|
||||
|
|
@ -21,22 +21,37 @@ import SwiftUI
|
|||
|
||||
struct HistoryView: View {
|
||||
|
||||
@ObservedObject var historyListViewModel: HistoryListViewModel
|
||||
@ObservedObject var historyViewModel: HistoryViewModel
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
@ObservedObject var editContactViewModel: EditContactViewModel
|
||||
|
||||
@Binding var index: Int
|
||||
@Binding var isShowEditContactFragment: Bool
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
VStack(spacing: 0) {
|
||||
VStack {
|
||||
Spacer()
|
||||
Image("illus-belledonne")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.clipped()
|
||||
.padding(.all)
|
||||
Text("No calls for the moment...")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
Spacer()
|
||||
Spacer()
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
HistoryFragment(
|
||||
historyListViewModel: historyListViewModel,
|
||||
historyViewModel: historyViewModel,
|
||||
contactViewModel: contactViewModel,
|
||||
editContactViewModel: editContactViewModel,
|
||||
index: $index,
|
||||
isShowEditContactFragment: $isShowEditContactFragment
|
||||
)
|
||||
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
Image("phone-plus")
|
||||
.padding()
|
||||
.background(.white)
|
||||
.clipShape(Circle())
|
||||
.shadow(color: .black.opacity(0.2), radius: 4)
|
||||
|
||||
}
|
||||
.padding(.all)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
|
|
@ -44,5 +59,12 @@ struct HistoryView: View {
|
|||
}
|
||||
|
||||
#Preview {
|
||||
HistoryView()
|
||||
HistoryFragment(
|
||||
historyListViewModel: HistoryListViewModel(),
|
||||
historyViewModel: HistoryViewModel(),
|
||||
contactViewModel: ContactViewModel(),
|
||||
editContactViewModel: EditContactViewModel(),
|
||||
index: .constant(1),
|
||||
isShowEditContactFragment: .constant(false)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
142
Linphone/UI/Main/History/ViewModel/HistoryListViewModel.swift
Normal file
142
Linphone/UI/Main/History/ViewModel/HistoryListViewModel.swift
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import linphonesw
|
||||
|
||||
class HistoryListViewModel: ObservableObject {
|
||||
|
||||
private var coreContext = CoreContext.shared
|
||||
|
||||
@Published var callLogs: [CallLog] = []
|
||||
var callLogsTmp: [CallLog] = []
|
||||
|
||||
init() {
|
||||
computeCallLogsList()
|
||||
}
|
||||
|
||||
func computeCallLogsList() {
|
||||
coreContext.doOnCoreQueue { core in
|
||||
let account = core.defaultAccount
|
||||
let logs = account?.callLogs != nil ? account!.callLogs : core.callLogs
|
||||
|
||||
self.callLogs.removeAll()
|
||||
self.callLogsTmp.removeAll()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
logs.forEach { log in
|
||||
self.callLogs.append(log)
|
||||
self.callLogsTmp.append(log)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getCallIconResId(callStatus: Call.Status, callDir: Call.Dir) -> String {
|
||||
switch callStatus {
|
||||
case Call.Status.Missed:
|
||||
if callDir == .Outgoing {
|
||||
"outgoing-call-missed"
|
||||
} else {
|
||||
"incoming-call-missed"
|
||||
}
|
||||
|
||||
case Call.Status.Success:
|
||||
if callDir == .Outgoing {
|
||||
"outgoing-call"
|
||||
} else {
|
||||
"incoming-call"
|
||||
}
|
||||
|
||||
default:
|
||||
if callDir == .Outgoing {
|
||||
"outgoing-call-rejected"
|
||||
} else {
|
||||
"incoming-call-rejected"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 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 "Yesterday " + 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 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 formatter.string(from: myNSDate)
|
||||
}
|
||||
}
|
||||
|
||||
func filterCallLogs(filter: String) {
|
||||
callLogs.removeAll()
|
||||
callLogsTmp.forEach { callLog in
|
||||
if callLog.dir == .Outgoing && callLog.toAddress != nil {
|
||||
if callLog.toAddress!.username != nil && callLog.toAddress!.username!.contains(filter) {
|
||||
callLogs.append(callLog)
|
||||
} else if callLog.toAddress!.displayName != nil && callLog.toAddress!.displayName!.contains(filter) {
|
||||
callLogs.append(callLog)
|
||||
}
|
||||
} else if callLog.fromAddress != nil {
|
||||
if callLog.fromAddress!.username != nil && callLog.fromAddress!.username!.contains(filter) {
|
||||
callLogs.append(callLog)
|
||||
} else if callLog.fromAddress!.displayName != nil && callLog.fromAddress!.displayName!.contains(filter) {
|
||||
callLogs.append(callLog)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func resetFilterCallLogs() {
|
||||
callLogs = callLogsTmp
|
||||
}
|
||||
|
||||
func removeCallLogs() {
|
||||
coreContext.doOnCoreQueue { core in
|
||||
let account = core.defaultAccount
|
||||
if account != nil {
|
||||
account!.clearCallLogs()
|
||||
} else {
|
||||
core.clearCallLogs()
|
||||
}
|
||||
self.callLogs.removeAll()
|
||||
self.callLogsTmp.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
func removeCallLog(callLog: CallLog) {
|
||||
let index = self.callLogs.firstIndex(where: {$0.callId == callLog.callId})
|
||||
self.callLogs.remove(at: index!)
|
||||
|
||||
let indexTmp = self.callLogsTmp.firstIndex(where: {$0.callId == callLog.callId})
|
||||
self.callLogsTmp.remove(at: index!)
|
||||
}
|
||||
}
|
||||
|
|
@ -18,10 +18,13 @@
|
|||
*/
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
class HistoryViewModel: ObservableObject {
|
||||
|
||||
@Published var historyTitle: String = ""
|
||||
@Published var indexDisplayedCall: Int?
|
||||
|
||||
var selectedCall: CallLog?
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ struct EditContactView: UIViewControllerRepresentable {
|
|||
name: cnc.givenName + cnc.familyName + String(Int.random(in: 1...1000)) + ((imageThumbnail == nil) ? "-default" : ""),
|
||||
contact: newContact,
|
||||
linphoneFriend: false,
|
||||
existingFriend: ContactsManager.shared.getFriend(contact: newContact))
|
||||
existingFriend: ContactsManager.shared.getFriendWithContact(contact: newContact))
|
||||
|
||||
MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue