This commit is contained in:
Benoit Martins 2024-01-09 17:29:51 +01:00 committed by QuentinArguillere
parent 1ddf2602b9
commit 04dbce540c
24 changed files with 474 additions and 282 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View file

@ -1,6 +1,7 @@
{
"images" : [
{
"filename" : "1024.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"

View file

@ -38,7 +38,7 @@ final class CoreContext: ObservableObject {
private var mCore: Core!
private var mIterateSuscription: AnyCancellable?
private var mCoreSuscriptions = Set<AnyCancellable?>()
private init() {
do {
try initialiseCore()
@ -68,17 +68,17 @@ final class CoreContext: ObservableObject {
Factory.Instance.logCollectionPath = configDir
Factory.Instance.enableLogCollection(state: LogCollectionState.Enabled)
let url = NSURL(fileURLWithPath: configDir)
if let pathComponent = url.appendingPathComponent("linphonerc") {
let filePath = pathComponent.path
let fileManager = FileManager.default
if !fileManager.fileExists(atPath: filePath) {
let path = Bundle.main.path(forResource: "linphonerc-default", ofType: nil)
if path != nil {
try? FileManager.default.copyItem(at: NSURL(fileURLWithPath: path!) as URL, to: pathComponent)
}
}
}
let url = NSURL(fileURLWithPath: configDir)
if let pathComponent = url.appendingPathComponent("linphonerc") {
let filePath = pathComponent.path
let fileManager = FileManager.default
if !fileManager.fileExists(atPath: filePath) {
let path = Bundle.main.path(forResource: "linphonerc-default", ofType: nil)
if path != nil {
try? FileManager.default.copyItem(at: NSURL(fileURLWithPath: path!) as URL, to: pathComponent)
}
}
}
let config = try? Factory.Instance.createConfigWithFactory(
path: "\(configDir)/linphonerc",
@ -87,7 +87,7 @@ final class CoreContext: ObservableObject {
if config != nil {
self.mCore = try? Factory.Instance.createCoreWithConfig(config: config!, systemContext: nil)
}
self.mCore.autoIterateEnabled = false
self.mCore.callkitEnabled = true
self.mCore.pushNotificationEnabled = true
@ -113,11 +113,14 @@ final class CoreContext: ObservableObject {
NSLog("New configuration state is \(cbVal.status) = \(cbVal.message)\n")
if cbVal.status == Config.ConfiguringState.Successful {
ToastViewModel.shared.toastMessage = "Successful"
ToastViewModel.shared.displayToast.toggle()
} else {
ToastViewModel.shared.toastMessage = "Failed"
ToastViewModel.shared.displayToast.toggle()
}
ToastViewModel.shared.displayToast = true
}
/*
else {
ToastViewModel.shared.toastMessage = "Failed"
ToastViewModel.shared.displayToast = true
}
*/
})
self.mCoreSuscriptions.insert(self.mCore.publisher?.onAccountRegistrationStateChanged?.postOnMainQueue { (cbVal: (core: Core, account: Account, state: RegistrationState, message: String)) in
@ -135,7 +138,7 @@ final class CoreContext: ObservableObject {
self.loggingInProgress = true
} else {
ToastViewModel.shared.toastMessage = "Registration failed"
ToastViewModel.shared.displayToast.toggle()
ToastViewModel.shared.displayToast = true
self.loggingInProgress = false
self.loggedIn = false
}
@ -166,9 +169,9 @@ final class CoreContext: ObservableObject {
cbValue.info,
forPasteboardType: UTType.plainText.identifier
)
ToastViewModel.shared.toastMessage = "Success_copied_into_clipboard"
ToastViewModel.shared.displayToast.toggle()
ToastViewModel.shared.displayToast = true
}
})

View file

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>NSCameraUsageDescription</key>
<string>Camera usage is required for video VOIP calls</string>
<key>NSMicrophoneUsageDescription</key>

View file

@ -38,8 +38,14 @@ struct LinphoneApp: App {
if !sharedMainViewModel.welcomeViewDisplayed {
WelcomeView()
} else if coreContext.defaultAccount == nil || sharedMainViewModel.displayProfileMode {
AssistantView()
ZStack {
AssistantView()
ToastView()
.zIndex(3)
}
} else if coreContext.defaultAccount != nil
&& coreContext.loggedIn
&& contactViewModel != nil
&& editContactViewModel != nil
&& historyViewModel != nil

View file

@ -120,10 +120,10 @@ class TelecomManager: ObservableObject {
}
}
func doCallWithCore(addr: Address) {
func doCallWithCore(addr: Address, isVideo: Bool) {
CoreContext.shared.doOnCoreQueue { core in
do {
try self.startCallCallKit(core: core, addr: addr, isSas: false, isVideo: false, isConference: false)
try self.startCallCallKit(core: core, addr: addr, isSas: false, isVideo: isVideo, isConference: false)
} catch {
Log.error("[TelecomManager] unable to create address for a new outgoing call : \(addr) \(error) ")
}

View file

@ -63,6 +63,8 @@ struct LoginFragment: View {
TextField("username", text: $accountLoginViewModel.username)
.default_text_style(styleSize: 15)
.disableAutocorrection(true)
.autocapitalization(.none)
.disabled(coreContext.loggedIn)
.frame(height: 25)
.padding(.horizontal, 20)
@ -90,6 +92,8 @@ struct LoginFragment: View {
} else {
TextField("password", text: $accountLoginViewModel.passwd)
.default_text_style(styleSize: 15)
.disableAutocorrection(true)
.autocapitalization(.none)
.frame(height: 25)
.focused($isPasswordFocused)
}
@ -287,6 +291,8 @@ struct LoginFragment: View {
.background(.black.opacity(0.65))
}
}
.navigationTitle("")
.navigationBarHidden(true)
}
.navigationViewStyle(StackNavigationViewStyle())
}

View file

@ -65,8 +65,11 @@ struct RegisterFragment: View {
}
}
}
.navigationTitle("")
.navigationBarHidden(true)
}
.navigationViewStyle(StackNavigationViewStyle())
.navigationTitle("")
.navigationBarHidden(true)
}
}

View file

@ -81,6 +81,8 @@ struct ThirdPartySipAccountLoginFragment: View {
TextField("username", text: $accountLoginViewModel.username)
.default_text_style(styleSize: 15)
.disableAutocorrection(true)
.autocapitalization(.none)
.disabled(coreContext.loggedIn)
.frame(height: 25)
.padding(.horizontal, 20)
@ -108,6 +110,8 @@ struct ThirdPartySipAccountLoginFragment: View {
} else {
TextField("password", text: $accountLoginViewModel.passwd)
.default_text_style(styleSize: 15)
.disableAutocorrection(true)
.autocapitalization(.none)
.frame(height: 25)
.focused($isPasswordFocused)
}
@ -139,6 +143,8 @@ struct ThirdPartySipAccountLoginFragment: View {
TextField("sip.linphone.org", text: $accountLoginViewModel.domain)
.default_text_style(styleSize: 15)
.disableAutocorrection(true)
.autocapitalization(.none)
.disabled(coreContext.loggedIn)
.frame(height: 25)
.padding(.horizontal, 20)
@ -158,6 +164,8 @@ struct ThirdPartySipAccountLoginFragment: View {
TextField("Display Name", text: $accountLoginViewModel.displayName)
.default_text_style(styleSize: 15)
.disableAutocorrection(true)
.autocapitalization(.none)
.disabled(coreContext.loggedIn)
.frame(height: 25)
.padding(.horizontal, 20)
@ -204,8 +212,6 @@ struct ThirdPartySipAccountLoginFragment: View {
Button(action: {
self.accountLoginViewModel.login()
accountLoginViewModel.domain = "sip.linphone.org"
accountLoginViewModel.transportType = "TLS"
}, label: {
Text(coreContext.loggedIn ? "Log out" : "assistant_account_login")
.default_text_style_white_600(styleSize: 20)

View file

@ -171,8 +171,11 @@ struct ThirdPartySipAccountWarningFragment: View {
.frame(minHeight: geometry.size.height)
}
}
.navigationTitle("")
.navigationBarHidden(true)
}
.navigationViewStyle(StackNavigationViewStyle())
.navigationTitle("")
.navigationBarHidden(true)
}
}

View file

@ -45,7 +45,7 @@ class AccountLoginViewModel: ObservableObject {
core.loadConfigFromXml(xmlUri: assistantLinphone)
}
}
// Get the transport protocol to use.
// TLS is strongly recommended
// Only use UDP if you don't have the choice
@ -106,6 +106,9 @@ class AccountLoginViewModel: ObservableObject {
self.coreContext.defaultAccount = account
}
self.domain = "sip.linphone.org"
self.transportType = "TLS"
} catch { NSLog(error.localizedDescription) }
}
}

View file

@ -427,18 +427,29 @@ struct CallView: View {
.presentationDetents([.fraction(0.3)])
.frame(maxHeight: .infinity)
}
} else {
innerView(geometry: geo)
}
}
}
@ViewBuilder
// swiftlint:disable:next cyclomatic_complexity
func innerView(geometry: GeometryProxy) -> some View {
VStack {
if !fullscreenVideo {
Rectangle()
.foregroundColor(Color.orangeMain500)
.edgesIgnoringSafeArea(.top)
.frame(height: 0)
if #available(iOS 16.0, *) {
Rectangle()
.foregroundColor(Color.orangeMain500)
.edgesIgnoringSafeArea(.top)
.frame(height: 0)
} else if idiom != .pad && !(orientation == .landscapeLeft || orientation == .landscapeRight
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) {
Rectangle()
.foregroundColor(Color.orangeMain500)
.edgesIgnoringSafeArea(.top)
.frame(height: 1)
}
HStack {
if callViewModel.direction == .Outgoing {
@ -465,13 +476,8 @@ struct CallView: View {
ZStack {
Text(callViewModel.timeElapsed.convertDurationToString())
.onAppear {
callViewModel.timeElapsed = 0
startDate = Date.now
}
.onReceive(callViewModel.timer) { firedDate in
callViewModel.timeElapsed = Int(firedDate.timeIntervalSince(startDate))
}
.foregroundStyle(.white)
.if(callViewModel.isPaused || telecomManager.isPausedByRemote) { view in
@ -646,6 +652,10 @@ struct CallView: View {
callViewModel.timeElapsed = Int(firedDate.timeIntervalSince(startDate))
}
.onDisappear {
callViewModel.timeElapsed = 0
startDate = Date.now
}
.padding(.top)
.foregroundStyle(.white)
@ -695,16 +705,101 @@ struct CallView: View {
if !fullscreenVideo {
if telecomManager.callStarted {
if telecomManager.callStarted && idiom != .pad && !(orientation == .landscapeLeft || orientation == .landscapeRight
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) {
HStack(spacing: 12) {
HStack {
if #available(iOS 16.0, *) {
if telecomManager.callStarted && idiom != .pad && !(orientation == .landscapeLeft || orientation == .landscapeRight
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) {
HStack(spacing: 12) {
HStack {
}
.frame(height: 60)
}
.frame(height: 60)
.padding(.horizontal, 25)
.padding(.top, 20)
} else {
HStack(spacing: 12) {
Button {
callViewModel.terminateCall()
} label: {
Image("phone-disconnect")
.renderingMode(.template)
.resizable()
.foregroundStyle(.white)
.frame(width: 32, height: 32)
}
.frame(width: 90, height: 60)
.background(Color.redDanger500)
.cornerRadius(40)
Spacer()
Button {
callViewModel.toggleVideo()
} label: {
Image(callViewModel.cameraDisplayed ? "video-camera" : "video-camera-slash")
.renderingMode(.template)
.resizable()
.foregroundStyle((callViewModel.isPaused || telecomManager.isPausedByRemote) ? Color.gray500 : .white)
.frame(width: 32, height: 32)
}
.frame(width: 60, height: 60)
.background((callViewModel.isPaused || telecomManager.isPausedByRemote) ? Color.gray600 : Color.gray500)
.cornerRadius(40)
.disabled(callViewModel.isPaused || telecomManager.isPausedByRemote)
Button {
callViewModel.toggleMuteMicrophone()
} label: {
Image(callViewModel.micMutted ? "microphone-slash" : "microphone")
.renderingMode(.template)
.resizable()
.foregroundStyle(callViewModel.micMutted ? .black : .white)
.frame(width: 32, height: 32)
}
.frame(width: 60, height: 60)
.background(callViewModel.micMutted ? .white : Color.gray500)
.cornerRadius(40)
Button {
if AVAudioSession.sharedInstance().availableInputs != nil
&& !AVAudioSession.sharedInstance().availableInputs!.filter({ $0.portType.rawValue.contains("Bluetooth") }).isEmpty {
hideButtonsSheet = true
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
audioRouteSheet = true
}
} else {
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.sharedInstance().currentRoute.outputs.filter({ $0.portType.rawValue == "Speaker" }).isEmpty ? .speaker : .none)
} catch _ {
}
}
} label: {
Image(imageAudioRoute)
.renderingMode(.template)
.resizable()
.foregroundStyle(.white)
.frame(width: 32, height: 32)
.onAppear(perform: getAudioRouteImage)
.onReceive(pub) { _ in
self.getAudioRouteImage()
}
}
.frame(width: 60, height: 60)
.background(Color.gray500)
.cornerRadius(40)
}
.frame(height: geometry.size.height * 0.15)
.padding(.horizontal, 20)
.padding(.top, -6)
}
.padding(.horizontal, 25)
.padding(.top, 20)
} else {
HStack(spacing: 12) {
Button {
@ -726,7 +821,7 @@ struct CallView: View {
Button {
callViewModel.toggleVideo()
} label: {
Image("video-camera")
Image(callViewModel.cameraDisplayed ? "video-camera" : "video-camera-slash")
.renderingMode(.template)
.resizable()
.foregroundStyle((callViewModel.isPaused || telecomManager.isPausedByRemote) ? Color.gray500 : .white)
@ -753,12 +848,32 @@ struct CallView: View {
.cornerRadius(40)
Button {
if AVAudioSession.sharedInstance().availableInputs != nil
&& !AVAudioSession.sharedInstance().availableInputs!.filter({ $0.portType.rawValue.contains("Bluetooth") }).isEmpty {
hideButtonsSheet = true
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
audioRouteSheet = true
}
} else {
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.sharedInstance().currentRoute.outputs.filter({ $0.portType.rawValue == "Speaker" }).isEmpty ? .speaker : .none)
} catch _ {
}
}
} label: {
Image("speaker-high")
Image(imageAudioRoute)
.renderingMode(.template)
.resizable()
.foregroundStyle(.white)
.frame(width: 32, height: 32)
.onAppear(perform: getAudioRouteImage)
.onReceive(pub) { _ in
self.getAudioRouteImage()
}
}
.frame(width: 60, height: 60)

View file

@ -41,8 +41,10 @@ struct ContactsView: View {
}
} label: {
Image("user-plus")
.renderingMode(.template)
.foregroundStyle(.white)
.padding()
.background(.white)
.background(Color.orangeMain500)
.clipShape(Circle())
.shadow(color: .black.opacity(0.2), radius: 4)

View file

@ -90,7 +90,7 @@ struct ContactInnerActionsFragment: View {
.onTapGesture {
withAnimation {
telecomManager.doCallWithCore(
addr: contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.addresses[index]
addr: contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.addresses[index], isVideo: false
)
}
}
@ -272,7 +272,9 @@ struct ContactInnerActionsFragment: View {
Button {
if contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend != nil {
contactViewModel.objectWillChange.send()
contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.edit()
contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.starred.toggle()
contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.done()
}
} label: {
HStack {

View file

@ -86,19 +86,19 @@ struct ContactInnerFragment: View {
contactViewModel: contactViewModel,
isShowEditContactFragment: .constant(false),
isShowDismissPopup: $isShowDismissPopup)) {
Image("pencil-simple")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.orangeMain500)
.frame(width: 25, height: 25, alignment: .leading)
.padding(.top, 2)
}
.simultaneousGesture(
TapGesture().onEnded {
editContactViewModel.selectedEditFriend = contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend
editContactViewModel.resetValues()
Image("pencil-simple")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.orangeMain500)
.frame(width: 25, height: 25, alignment: .leading)
.padding(.top, 2)
}
)
.simultaneousGesture(
TapGesture().onEnded {
editContactViewModel.selectedEditFriend = contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend
editContactViewModel.resetValues()
}
)
}
}
.frame(maxWidth: .infinity)
@ -132,10 +132,10 @@ struct ContactInnerFragment: View {
.frame(maxWidth: .infinity)
.padding(.top, 10)
Text(contactAvatarModel.lastPresenceInfo)
Text(contactAvatarModel.lastPresenceInfo)
.foregroundStyle(contactAvatarModel.lastPresenceInfo == "Online"
? Color.greenSuccess500
: Color.orangeWarning600)
? Color.greenSuccess500
: Color.orangeWarning600)
.multilineTextAlignment(.center)
.default_text_style_300(styleSize: 12)
.frame(maxWidth: .infinity)
@ -151,7 +151,7 @@ struct ContactInnerFragment: View {
Spacer()
Button(action: {
telecomManager.doCallWithCore(addr: contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.address!)
telecomManager.doCallWithCore(addr: contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.address!, isVideo: false)
}, label: {
VStack {
HStack(alignment: .center) {
@ -180,7 +180,8 @@ struct ContactInnerFragment: View {
Image("chat-teardrop-text")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c600)
//.foregroundStyle(Color.grayMain2c600)
.foregroundStyle(Color.grayMain2c300)
.frame(width: 25, height: 25)
.onTapGesture {
withAnimation {
@ -200,7 +201,7 @@ struct ContactInnerFragment: View {
Spacer()
Button(action: {
telecomManager.doCallWithCore(addr: contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.address!, isVideo: true)
}, label: {
VStack {
HStack(alignment: .center) {
@ -209,11 +210,6 @@ struct ContactInnerFragment: View {
.resizable()
.foregroundStyle(Color.grayMain2c600)
.frame(width: 25, height: 25)
.onTapGesture {
withAnimation {
}
}
}
.padding(16)
.background(Color.grayMain2c200)
@ -229,7 +225,7 @@ struct ContactInnerFragment: View {
.padding(.top, 20)
.frame(maxWidth: .infinity)
.background(Color.gray100)
ContactInnerActionsFragment(
contactViewModel: contactViewModel,
editContactViewModel: editContactViewModel,

View file

@ -58,7 +58,9 @@ struct ContactsListBottomSheet: View {
Spacer()
Button {
if contactViewModel.selectedFriend != nil {
contactViewModel.selectedFriend!.edit()
contactViewModel.selectedFriend!.starred.toggle()
contactViewModel.selectedFriend!.done()
}
MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)

View file

@ -151,6 +151,7 @@ struct ContentView: View {
Menu {
if index == 0 {
Button {
contactViewModel.indexDisplayedFriend = nil
isMenuOpen = false
magicSearch.allContact = true
MagicSearchSingleton.shared.searchForContacts(
@ -168,6 +169,7 @@ struct ContentView: View {
}
Button {
contactViewModel.indexDisplayedFriend = nil
isMenuOpen = false
magicSearch.allContact = false
MagicSearchSingleton.shared.searchForContacts(
@ -282,9 +284,8 @@ struct ContentView: View {
text = newValue
}
))
.default_text_style_white_700(styleSize: 15)
.default_text_style_700(styleSize: 15)
.padding(.all, 6)
.accentColor(.white)
.focused($focusedField)
.onAppear {
self.focusedField = true
@ -671,10 +672,8 @@ struct ContentView: View {
}
}
// if sharedMainViewModel.displayToast {
ToastView()
.zIndex(3)
// }
}
}
.overlay {
@ -698,12 +697,14 @@ struct ContentView: View {
.onChange(of: scenePhase) { newPhase in
if newPhase == .active {
coreContext.onForeground()
/*
if !isShowStartCallFragment {
contactsManager.fetchContacts()
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
historyListViewModel.computeCallLogsList()
}
}
*/
print("Active")
} else if newPhase == .inactive {
print("Inactive")

View file

@ -275,7 +275,7 @@ struct DialerBottomSheet: View {
if !startCallViewModel.searchField.isEmpty {
do {
let address = try Factory.Instance.createAddress(addr: String("sip:" + startCallViewModel.searchField + "@" + startCallViewModel.domain))
telecomManager.doCallWithCore(addr: address)
telecomManager.doCallWithCore(addr: address, isVideo: false)
} catch {
}

View file

@ -32,15 +32,15 @@ struct HistoryContactFragment: View {
@ObservedObject var contactAvatarModel: ContactAvatarModel
@ObservedObject var historyViewModel: HistoryViewModel
@ObservedObject var historyListViewModel: HistoryListViewModel
@ObservedObject var contactViewModel: ContactViewModel
@ObservedObject var editContactViewModel: EditContactViewModel
@ObservedObject var historyListViewModel: HistoryListViewModel
@ObservedObject var contactViewModel: ContactViewModel
@ObservedObject var editContactViewModel: EditContactViewModel
@State var isMenuOpen = false
@Binding var isShowDeleteAllHistoryPopup: Bool
@Binding var isShowEditContactFragment: Bool
@Binding var indexPage: Int
@Binding var isShowEditContactFragment: Bool
@Binding var indexPage: Int
var body: some View {
NavigationView {
@ -72,25 +72,25 @@ struct HistoryContactFragment: View {
Spacer()
Menu {
let fromAddressFriend = historyViewModel.displayedCall != nil ? contactsManager.getFriendWithAddress(address: historyViewModel.displayedCall!.fromAddress!) : nil
let toAddressFriend = historyViewModel.displayedCall != nil ? contactsManager.getFriendWithAddress(address: historyViewModel.displayedCall!.toAddress!) : nil
let addressFriend = historyViewModel.displayedCall != nil ? (historyViewModel.displayedCall!.dir == .Incoming ? fromAddressFriend : toAddressFriend) : nil
let fromAddressFriend = historyViewModel.displayedCall != nil ? contactsManager.getFriendWithAddress(address: historyViewModel.displayedCall!.fromAddress!) : nil
let toAddressFriend = historyViewModel.displayedCall != nil ? contactsManager.getFriendWithAddress(address: historyViewModel.displayedCall!.toAddress!) : nil
let addressFriend = historyViewModel.displayedCall != nil ? (historyViewModel.displayedCall!.dir == .Incoming ? fromAddressFriend : toAddressFriend) : nil
Button {
isMenuOpen = false
if contactsManager.getFriendWithAddress(
address: historyViewModel.displayedCall != nil && historyViewModel.displayedCall!.dir == .Outgoing
? historyViewModel.displayedCall!.toAddress!
: historyViewModel.displayedCall!.fromAddress!
) != nil {
let addressCall = historyViewModel.displayedCall != nil && historyViewModel.displayedCall!.dir == .Outgoing
? historyViewModel.displayedCall!.toAddress!
: historyViewModel.displayedCall!.fromAddress!
let friendIndex = contactsManager.lastSearch.firstIndex(
where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressCall.asStringUriOnly()})})
if friendIndex != nil {
if contactsManager.getFriendWithAddress(
address: historyViewModel.displayedCall != nil && historyViewModel.displayedCall!.dir == .Outgoing
? historyViewModel.displayedCall!.toAddress!
: historyViewModel.displayedCall!.fromAddress!
) != nil {
let addressCall = historyViewModel.displayedCall != nil && historyViewModel.displayedCall!.dir == .Outgoing
? historyViewModel.displayedCall!.toAddress!
: historyViewModel.displayedCall!.fromAddress!
let friendIndex = contactsManager.lastSearch.firstIndex(
where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressCall.asStringUriOnly()})})
if friendIndex != nil {
withAnimation {
historyViewModel.displayedCall = nil
@ -98,28 +98,28 @@ struct HistoryContactFragment: View {
contactViewModel.indexDisplayedFriend = friendIndex
}
}
} else {
let addressCall = historyViewModel.displayedCall != nil && historyViewModel.displayedCall!.dir == .Outgoing
? historyViewModel.displayedCall!.toAddress!
: historyViewModel.displayedCall!.fromAddress!
withAnimation {
}
} else {
let addressCall = historyViewModel.displayedCall != nil && historyViewModel.displayedCall!.dir == .Outgoing
? historyViewModel.displayedCall!.toAddress!
: historyViewModel.displayedCall!.fromAddress!
withAnimation {
historyViewModel.displayedCall = nil
indexPage = 0
isShowEditContactFragment.toggle()
editContactViewModel.sipAddresses.removeAll()
editContactViewModel.sipAddresses.append(String(addressCall.asStringUriOnly().dropFirst(4)))
editContactViewModel.sipAddresses.removeAll()
editContactViewModel.sipAddresses.append(String(addressCall.asStringUriOnly().dropFirst(4)))
editContactViewModel.sipAddresses.append("")
}
}
}
}
} label: {
HStack {
Text(addressFriend != nil ? "See contact" : "Add to contacts")
Text(addressFriend != nil ? "See contact" : "Add to contacts")
Spacer()
Image(addressFriend != nil ? "user-circle" : "plus-circle")
Image(addressFriend != nil ? "user-circle" : "plus-circle")
.resizable()
.frame(width: 25, height: 25, alignment: .leading)
}
@ -127,18 +127,18 @@ struct HistoryContactFragment: View {
Button {
isMenuOpen = false
if historyViewModel.displayedCall != nil && historyViewModel.displayedCall!.dir == .Outgoing {
UIPasteboard.general.setValue(
historyViewModel.displayedCall!.toAddress!.asStringUriOnly().dropFirst(4),
forPasteboardType: UTType.plainText.identifier
)
} else {
UIPasteboard.general.setValue(
historyViewModel.displayedCall!.fromAddress!.asStringUriOnly().dropFirst(4),
forPasteboardType: UTType.plainText.identifier
)
}
if historyViewModel.displayedCall != nil && historyViewModel.displayedCall!.dir == .Outgoing {
UIPasteboard.general.setValue(
historyViewModel.displayedCall!.toAddress!.asStringUriOnly().dropFirst(4),
forPasteboardType: UTType.plainText.identifier
)
} else {
UIPasteboard.general.setValue(
historyViewModel.displayedCall!.fromAddress!.asStringUriOnly().dropFirst(4),
forPasteboardType: UTType.plainText.identifier
)
}
ToastViewModel.shared.toastMessage = "Success_copied_into_clipboard"
ToastViewModel.shared.displayToast.toggle()
@ -194,8 +194,12 @@ struct HistoryContactFragment: View {
ScrollView {
VStack(spacing: 0) {
VStack(spacing: 0) {
if #unavailable(iOS 16.0) {
Rectangle()
.foregroundColor(Color.gray100)
.frame(height: 7)
}
VStack(spacing: 0) {
let fromAddressFriend = historyViewModel.displayedCall != nil ? contactsManager.getFriendWithAddress(address: historyViewModel.displayedCall!.fromAddress!) : nil
let toAddressFriend = historyViewModel.displayedCall != nil ? contactsManager.getFriendWithAddress(address: historyViewModel.displayedCall!.toAddress!) : nil
let addressFriend = historyViewModel.displayedCall != nil ? (historyViewModel.displayedCall!.dir == .Incoming ? fromAddressFriend : toAddressFriend) : nil
@ -223,13 +227,13 @@ struct HistoryContactFragment: View {
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 10)
Text(historyViewModel.displayedCall!.toAddress!.asStringUriOnly())
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 5)
Text(historyViewModel.displayedCall!.toAddress!.asStringUriOnly())
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 5)
Text("")
.multilineTextAlignment(.center)
@ -252,13 +256,13 @@ struct HistoryContactFragment: View {
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 10)
Text(historyViewModel.displayedCall!.toAddress!.asStringUriOnly())
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 5)
Text(historyViewModel.displayedCall!.toAddress!.asStringUriOnly())
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 5)
Text("")
.multilineTextAlignment(.center)
@ -284,14 +288,14 @@ struct HistoryContactFragment: View {
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 10)
Text(historyViewModel.displayedCall!.fromAddress!.asStringUriOnly())
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 5)
Text(historyViewModel.displayedCall!.fromAddress!.asStringUriOnly())
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 5)
Text("")
.multilineTextAlignment(.center)
.default_text_style_300(styleSize: 12)
@ -313,14 +317,14 @@ struct HistoryContactFragment: View {
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 10)
Text(historyViewModel.displayedCall!.fromAddress!.asStringUriOnly())
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 5)
Text(historyViewModel.displayedCall!.fromAddress!.asStringUriOnly())
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 5)
Text("")
.multilineTextAlignment(.center)
.default_text_style_300(styleSize: 12)
@ -338,22 +342,22 @@ struct HistoryContactFragment: View {
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 10)
if historyViewModel.displayedCall!.dir == .Outgoing && historyViewModel.displayedCall!.toAddress != nil {
Text(historyViewModel.displayedCall!.toAddress!.asStringUriOnly())
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 5)
} else if historyViewModel.displayedCall!.fromAddress != nil {
Text(historyViewModel.displayedCall!.fromAddress!.asStringUriOnly())
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 5)
}
if historyViewModel.displayedCall!.dir == .Outgoing && historyViewModel.displayedCall!.toAddress != nil {
Text(historyViewModel.displayedCall!.toAddress!.asStringUriOnly())
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 5)
} else if historyViewModel.displayedCall!.fromAddress != nil {
Text(historyViewModel.displayedCall!.fromAddress!.asStringUriOnly())
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity)
.padding(.top, 5)
}
Text(contactAvatarModel.lastPresenceInfo)
.foregroundStyle(contactAvatarModel.lastPresenceInfo == "Online"
@ -369,21 +373,22 @@ struct HistoryContactFragment: View {
.frame(minHeight: 150)
.frame(maxWidth: .infinity)
.padding(.top, 10)
.padding(.bottom, 2)
.background(Color.gray100)
HStack {
Spacer()
Button(action: {
if historyViewModel.displayedCall!.dir == .Outgoing && historyViewModel.displayedCall!.toAddress != nil {
telecomManager.doCallWithCore(
addr: historyViewModel.displayedCall!.toAddress!
)
} else if historyViewModel.displayedCall!.dir == .Incoming && historyViewModel.displayedCall!.fromAddress != nil {
telecomManager.doCallWithCore(
addr: historyViewModel.displayedCall!.fromAddress!
)
}
if historyViewModel.displayedCall!.dir == .Outgoing && historyViewModel.displayedCall!.toAddress != nil {
telecomManager.doCallWithCore(
addr: historyViewModel.displayedCall!.toAddress!, isVideo: false
)
} else if historyViewModel.displayedCall!.dir == .Incoming && historyViewModel.displayedCall!.fromAddress != nil {
telecomManager.doCallWithCore(
addr: historyViewModel.displayedCall!.fromAddress!, isVideo: false
)
}
}, label: {
VStack {
HStack(alignment: .center) {
@ -399,6 +404,7 @@ struct HistoryContactFragment: View {
Text("Appel")
.default_text_style(styleSize: 14)
.frame(minWidth: 80)
}
})
@ -412,7 +418,8 @@ struct HistoryContactFragment: View {
Image("chat-teardrop-text")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c600)
//.foregroundStyle(Color.grayMain2c600)
.foregroundStyle(Color.grayMain2c300)
.frame(width: 25, height: 25)
.onTapGesture {
withAnimation {
@ -426,13 +433,22 @@ struct HistoryContactFragment: View {
Text("Message")
.default_text_style(styleSize: 14)
.frame(minWidth: 80)
}
})
Spacer()
Button(action: {
if historyViewModel.displayedCall!.dir == .Outgoing && historyViewModel.displayedCall!.toAddress != nil {
telecomManager.doCallWithCore(
addr: historyViewModel.displayedCall!.toAddress!, isVideo: true
)
} else if historyViewModel.displayedCall!.dir == .Incoming && historyViewModel.displayedCall!.fromAddress != nil {
telecomManager.doCallWithCore(
addr: historyViewModel.displayedCall!.fromAddress!, isVideo: true
)
}
}, label: {
VStack {
HStack(alignment: .center) {
@ -441,11 +457,6 @@ struct HistoryContactFragment: View {
.resizable()
.foregroundStyle(Color.grayMain2c600)
.frame(width: 25, height: 25)
.onTapGesture {
withAnimation {
}
}
}
.padding(16)
.background(Color.grayMain2c200)
@ -453,71 +464,75 @@ struct HistoryContactFragment: View {
Text("Video Call")
.default_text_style(styleSize: 14)
.frame(minWidth: 80)
}
})
Spacer()
}
.padding(.top, 20)
.padding(.bottom, 10)
.frame(maxWidth: .infinity)
.background(Color.gray100)
VStack(spacing: 0) {
let addressFriend = historyViewModel.displayedCall != nil
? (historyViewModel.displayedCall!.dir == .Incoming ? historyViewModel.displayedCall!.fromAddress!.asStringUriOnly()
: historyViewModel.displayedCall!.toAddress!.asStringUriOnly()) : nil
let callLogsFilter = historyListViewModel.callLogs.filter({ $0.dir == .Incoming
? $0.fromAddress!.asStringUriOnly() == addressFriend
: $0.toAddress!.asStringUriOnly() == addressFriend })
ForEach(0..<callLogsFilter.count, id: \.self) { index in
HStack {
VStack {
Image(historyListViewModel.getCallIconResId(callStatus: callLogsFilter[index].status, callDir: callLogsFilter[index].dir))
.resizable()
.frame(
width: historyListViewModel.getCallIconResId(
callStatus: callLogsFilter[index].status,
callDir: callLogsFilter[index].dir
).contains("rejected") ? 12 : 8,
height: historyListViewModel.getCallIconResId(
callStatus: callLogsFilter[index].status,
callDir: callLogsFilter[index].dir
).contains("rejected") ? 6 : 8)
.padding(.top, 5)
Spacer()
}
VStack {
Text(historyListViewModel.getCallText(
callStatus: callLogsFilter[index].status,
callDir: callLogsFilter[index].dir)
)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
Text(historyListViewModel.getCallTime(startDate: callLogsFilter[index].startDate))
.foregroundStyle(
callLogsFilter[index].status != .Success
? Color.redDanger500
: Color.grayMain2c600
)
.default_text_style_300(styleSize: 12)
.frame(maxWidth: .infinity, alignment: .leading)
}
VStack {
Spacer()
Text(callLogsFilter[index].duration.convertDurationToString())
.default_text_style_300(styleSize: 12)
Spacer()
}
}
.padding(.vertical, 15)
.padding(.horizontal, 20)
}
let addressFriend = historyViewModel.displayedCall != nil
? (historyViewModel.displayedCall!.dir == .Incoming ? historyViewModel.displayedCall!.fromAddress!.asStringUriOnly()
: historyViewModel.displayedCall!.toAddress!.asStringUriOnly()) : nil
let callLogsFilter = historyListViewModel.callLogs.filter({ $0.dir == .Incoming
? $0.fromAddress!.asStringUriOnly() == addressFriend
: $0.toAddress!.asStringUriOnly() == addressFriend })
ForEach(0..<callLogsFilter.count, id: \.self) { index in
HStack {
VStack {
Image(historyListViewModel.getCallIconResId(callStatus: callLogsFilter[index].status, callDir: callLogsFilter[index].dir))
.resizable()
.frame(
width: historyListViewModel.getCallIconResId(
callStatus: callLogsFilter[index].status,
callDir: callLogsFilter[index].dir
).contains("rejected") ? 12 : 8,
height: historyListViewModel.getCallIconResId(
callStatus: callLogsFilter[index].status,
callDir: callLogsFilter[index].dir
).contains("rejected") ? 6 : 8)
.padding(.top, 6)
Spacer()
}
VStack {
Text(historyListViewModel.getCallText(
callStatus: callLogsFilter[index].status,
callDir: callLogsFilter[index].dir)
)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
Text(historyListViewModel.getCallTime(startDate: callLogsFilter[index].startDate))
.foregroundStyle(
callLogsFilter[index].status != .Success
? Color.redDanger500
: Color.grayMain2c600
)
.default_text_style_300(styleSize: 12)
.frame(maxWidth: .infinity, alignment: .leading)
}
VStack {
Spacer()
Text(callLogsFilter[index].duration.convertDurationToString())
.default_text_style_300(styleSize: 12)
Spacer()
}
}
.padding(.vertical, 15)
.padding(.horizontal, 20)
.frame(maxHeight: 65)
}
}
.background(.white)
.cornerRadius(15)
@ -526,6 +541,7 @@ struct HistoryContactFragment: View {
.frame(maxWidth: sharedMainViewModel.maxWidth)
}
.frame(maxWidth: .infinity)
.padding(.top, 2)
}
.background(Color.gray100)
}
@ -540,16 +556,16 @@ struct HistoryContactFragment: View {
}
#Preview {
HistoryContactFragment(
HistoryContactFragment(
contactAvatarModel: ContactAvatarModel(friend: nil, withPresence: false),
historyViewModel: HistoryViewModel(),
historyListViewModel: HistoryListViewModel(),
contactViewModel: ContactViewModel(),
editContactViewModel: EditContactViewModel(),
isShowDeleteAllHistoryPopup: .constant(false),
isShowEditContactFragment: .constant(false),
indexPage: .constant(1)
)
historyListViewModel: HistoryListViewModel(),
contactViewModel: ContactViewModel(),
editContactViewModel: EditContactViewModel(),
isShowDeleteAllHistoryPopup: .constant(false),
isShowEditContactFragment: .constant(false),
indexPage: .constant(1)
)
}
// swiftlint:enable line_length

View file

@ -166,11 +166,11 @@ struct HistoryListFragment: View {
withAnimation {
if historyListViewModel.callLogs[index].dir == .Outgoing && historyListViewModel.callLogs[index].toAddress != nil {
telecomManager.doCallWithCore(
addr: historyListViewModel.callLogs[index].toAddress!
addr: historyListViewModel.callLogs[index].toAddress!, isVideo: false
)
} else if historyListViewModel.callLogs[index].fromAddress != nil {
telecomManager.doCallWithCore(
addr: historyListViewModel.callLogs[index].fromAddress!
addr: historyListViewModel.callLogs[index].fromAddress!, isVideo: false
)
}
historyViewModel.displayedCall = nil
@ -213,6 +213,8 @@ struct HistoryListFragment: View {
.padding(.all)
)
}
.navigationTitle("")
.navigationBarHidden(true)
}
}

View file

@ -90,6 +90,9 @@ struct StartCallFragment: View {
magicSearch.currentFilterSuggestions = newValue
magicSearch.searchForSuggestions()
}
.simultaneousGesture(TapGesture().onEnded {
showingDialer = false
})
HStack {
Button(action: {
@ -105,10 +108,18 @@ struct StartCallFragment: View {
if startCallViewModel.searchField.isEmpty {
Button(action: {
isSearchFieldFocused = false
DispatchQueue.global().asyncAfter(deadline: .now() + 0.2) {
showingDialer.toggle()
if !showingDialer {
isSearchFieldFocused = false
DispatchQueue.global().asyncAfter(deadline: .now() + 0.2) {
showingDialer = true
}
} else {
showingDialer = false
DispatchQueue.global().asyncAfter(deadline: .now() + 0.2) {
isSearchFieldFocused = true
}
}
}, label: {
Image(!showingDialer ? "dialer" : "keyboard")
@ -155,7 +166,7 @@ struct StartCallFragment: View {
.padding(.horizontal, 16)
}
ContactsListFragment(contactViewModel: ContactViewModel(), contactsListViewModel: ContactsListViewModel(), showingSheet: .constant(false), startCallFunc: { addr in
ContactsListFragment(contactViewModel: ContactViewModel(), contactsListViewModel: ContactsListViewModel(), showingSheet: .constant(false), startCallFunc: { addr in
showingDialer = false
DispatchQueue.global().asyncAfter(deadline: .now() + 0.2) {
@ -169,10 +180,10 @@ struct StartCallFragment: View {
withAnimation {
isShowStartCallFragment.toggle()
telecomManager.doCallWithCore(addr: addr)
telecomManager.doCallWithCore(addr: addr, isVideo: false)
}
})
.padding(.horizontal, 16)
.padding(.horizontal, 16)
HStack(alignment: .center) {
Text("Suggestions")
@ -208,6 +219,25 @@ struct StartCallFragment: View {
var suggestionsList: some View {
ForEach(0..<contactsManager.lastSearchSuggestions.count, id: \.self) { index in
Button {
showingDialer = false
DispatchQueue.global().asyncAfter(deadline: .now() + 0.2) {
magicSearch.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
}
startCallViewModel.searchField = ""
magicSearch.currentFilterSuggestions = ""
delayColorDismiss()
withAnimation {
isShowStartCallFragment.toggle()
if contactsManager.lastSearchSuggestions[index].address != nil {
telecomManager.doCallWithCore(
addr: contactsManager.lastSearchSuggestions[index].address!, isVideo: false
)
}
}
} label: {
HStack {
if index < contactsManager.lastSearchSuggestions.count
@ -237,27 +267,6 @@ struct StartCallFragment: View {
.foregroundStyle(Color.orangeMain500)
}
}
.onTapGesture {
showingDialer = false
DispatchQueue.global().asyncAfter(deadline: .now() + 0.2) {
magicSearch.searchForContacts(
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
}
startCallViewModel.searchField = ""
magicSearch.currentFilterSuggestions = ""
delayColorDismiss()
withAnimation {
isShowStartCallFragment.toggle()
if contactsManager.lastSearchSuggestions[index].address != nil {
telecomManager.doCallWithCore(
addr: contactsManager.lastSearchSuggestions[index].address!
)
}
}
}
.padding(.horizontal)
}
.buttonStyle(.borderless)

View file

@ -50,8 +50,10 @@ struct HistoryView: View {
}
} label: {
Image("phone-plus")
.renderingMode(.template)
.foregroundStyle(.white)
.padding()
.background(.white)
.background(Color.orangeMain500)
.clipShape(Circle())
.shadow(color: .black.opacity(0.2), radius: 4)

View file

@ -134,6 +134,8 @@ struct WelcomeView: View {
.frame(minHeight: geometry.size.height)
}
}
.navigationTitle("")
.navigationBarHidden(true)
}
.navigationViewStyle(StackNavigationViewStyle())
}

View file

@ -28,15 +28,25 @@ class PermissionManager: ObservableObject {
@Published var photoLibraryPermissionGranted = false
@Published var cameraPermissionGranted = false
@Published var contactsPermissionGranted = false
@Published var microphonePermissionGranted = false
private init() {}
func getPermissions() {
microphoneRequestPermission()
photoLibraryRequestPermission()
cameraRequestPermission()
contactsRequestPermission()
}
func microphoneRequestPermission() {
AVAudioSession.sharedInstance().requestRecordPermission({ granted in
DispatchQueue.main.async {
self.microphonePermissionGranted = granted
}
})
}
func photoLibraryRequestPermission() {
PHPhotoLibrary.requestAuthorization(for: .readWrite, handler: {status in
DispatchQueue.main.async {