Side menu

This commit is contained in:
Christophe Deschamps 2024-06-17 10:28:39 +00:00
parent 9e314aa205
commit dfcd501dc3
15 changed files with 825 additions and 109 deletions

View file

@ -36,6 +36,11 @@
66FBFC4A2B83BD3300BC6AB1 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FE2B24D4AC00CEA16D /* FileUtils.swift */; };
66FBFC4B2B83BD7B00BC6AB1 /* CoreExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FA2B24D32600CEA16D /* CoreExtension.swift */; };
C60E8F192C0F649200A06DB8 /* UIApplicationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60E8F182C0F649200A06DB8 /* UIApplicationExtension.swift */; };
C62817282C1B389700DBA646 /* SideMenuAccountRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62817272C1B389700DBA646 /* SideMenuAccountRow.swift */; };
C628172E2C1C3A3600DBA646 /* AccountExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C628172D2C1C3A3600DBA646 /* AccountExtension.swift */; };
C62817302C1C3DCC00DBA646 /* AccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C628172F2C1C3DCC00DBA646 /* AccountModel.swift */; };
C62817322C1C400A00DBA646 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62817312C1C400A00DBA646 /* StringExtension.swift */; };
C62817342C1C7C7400DBA646 /* HelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62817332C1C7C7400DBA646 /* HelpView.swift */; };
C67586AE2C09F23C002E77BF /* URLExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C67586AD2C09F23C002E77BF /* URLExtension.swift */; };
C67586B02C09F247002E77BF /* URIHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C67586AF2C09F247002E77BF /* URIHandler.swift */; };
C67586B52C09F617002E77BF /* SingleSignOnManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C67586B22C09F617002E77BF /* SingleSignOnManager.swift */; };
@ -43,6 +48,8 @@
C6A5A9432C10B5ED0070FEA4 /* DecodableExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A5A9422C10B5ED0070FEA4 /* DecodableExtension.swift */; };
C6A5A9452C10B6270070FEA4 /* OIDAuthStateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A5A9442C10B6270070FEA4 /* OIDAuthStateExtension.swift */; };
C6A5A9482C10B6A30070FEA4 /* AuthState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A5A9472C10B6A30070FEA4 /* AuthState.swift */; };
C6DC4E3D2C199C4E009096FD /* BundleExtenion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DC4E3C2C199C4E009096FD /* BundleExtenion.swift */; };
C6DC4E3F2C19C289009096FD /* SideMenuEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DC4E3E2C19C289009096FD /* SideMenuEntry.swift */; };
D706BA822ADD72D100278F45 /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D706BA812ADD72D100278F45 /* DeviceRotationViewModifier.swift */; };
D70959F12B8DF3EC0014AC0B /* ConversationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D70959F02B8DF3EC0014AC0B /* ConversationModel.swift */; };
D70A26EE2B7CF60B006CC8FC /* ConversationsListBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D70A26ED2B7CF60B006CC8FC /* ConversationsListBottomSheet.swift */; };
@ -201,6 +208,11 @@
66E56BCD2BA9A1F8006CE56F /* MeetingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingModel.swift; sourceTree = "<group>"; };
66F626B12BCEBB86003E2DEC /* AddParticipantsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddParticipantsFragment.swift; sourceTree = "<group>"; };
C60E8F182C0F649200A06DB8 /* UIApplicationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplicationExtension.swift; sourceTree = "<group>"; };
C62817272C1B389700DBA646 /* SideMenuAccountRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuAccountRow.swift; sourceTree = "<group>"; };
C628172D2C1C3A3600DBA646 /* AccountExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountExtension.swift; sourceTree = "<group>"; };
C628172F2C1C3DCC00DBA646 /* AccountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountModel.swift; sourceTree = "<group>"; };
C62817312C1C400A00DBA646 /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = "<group>"; };
C62817332C1C7C7400DBA646 /* HelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpView.swift; sourceTree = "<group>"; };
C67586AD2C09F23C002E77BF /* URLExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLExtension.swift; sourceTree = "<group>"; };
C67586AF2C09F247002E77BF /* URIHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URIHandler.swift; sourceTree = "<group>"; };
C67586B22C09F617002E77BF /* SingleSignOnManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleSignOnManager.swift; sourceTree = "<group>"; };
@ -208,6 +220,8 @@
C6A5A9422C10B5ED0070FEA4 /* DecodableExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecodableExtension.swift; sourceTree = "<group>"; };
C6A5A9442C10B6270070FEA4 /* OIDAuthStateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDAuthStateExtension.swift; sourceTree = "<group>"; };
C6A5A9472C10B6A30070FEA4 /* AuthState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthState.swift; sourceTree = "<group>"; };
C6DC4E3C2C199C4E009096FD /* BundleExtenion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleExtenion.swift; sourceTree = "<group>"; };
C6DC4E3E2C19C289009096FD /* SideMenuEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuEntry.swift; sourceTree = "<group>"; };
D706BA812ADD72D100278F45 /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = "<group>"; };
D70959F02B8DF3EC0014AC0B /* ConversationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationModel.swift; sourceTree = "<group>"; };
D70A26ED2B7CF60B006CC8FC /* ConversationsListBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationsListBottomSheet.swift; sourceTree = "<group>"; };
@ -368,6 +382,9 @@
C60E8F182C0F649200A06DB8 /* UIApplicationExtension.swift */,
C6A5A9402C10B5D50070FEA4 /* EncodableExtension.swift */,
C6A5A9422C10B5ED0070FEA4 /* DecodableExtension.swift */,
C6DC4E3C2C199C4E009096FD /* BundleExtenion.swift */,
C628172D2C1C3A3600DBA646 /* AccountExtension.swift */,
C62817312C1C400A00DBA646 /* StringExtension.swift */,
);
path = Extensions;
sourceTree = "<group>";
@ -624,6 +641,9 @@
D750D3382AD3E6EE00EC99C5 /* PopupLoadingView.swift */,
D706BA812ADD72D100278F45 /* DeviceRotationViewModifier.swift */,
D72250682ADFBF2D008FB426 /* SideMenu.swift */,
C62817332C1C7C7400DBA646 /* HelpView.swift */,
C6DC4E3E2C19C289009096FD /* SideMenuEntry.swift */,
C62817272C1B389700DBA646 /* SideMenuAccountRow.swift */,
D7E6D04C2AEBD77600A57AAF /* CustomBottomSheet.swift */,
);
path = Fragments;
@ -724,6 +744,7 @@
66162A1F2BDFC2F900DCE913 /* AddParticipantsViewModel.swift */,
D7A2EDD52AC18115005D90FC /* SharedMainViewModel.swift */,
D796F1FF2B0BB61A0041115F /* ToastViewModel.swift */,
C628172F2C1C3DCC00DBA646 /* AccountModel.swift */,
);
path = Viewmodel;
sourceTree = "<group>";
@ -1014,6 +1035,7 @@
D70A26F22B7F5D95006CC8FC /* ConversationFragment.swift in Sources */,
66C491FD2B24D36500CEA16D /* AudioRouteUtils.swift in Sources */,
D70959F12B8DF3EC0014AC0B /* ConversationModel.swift in Sources */,
C6DC4E3D2C199C4E009096FD /* BundleExtenion.swift in Sources */,
D7EAACCF2AD6ED8000AA6A8A /* PermissionsFragment.swift in Sources */,
D777DBB32AE12C5900565A99 /* ContactsManager.swift in Sources */,
D720E6AD2BAD822000DDFD87 /* ParticipantModel.swift in Sources */,
@ -1027,6 +1049,7 @@
D732A90F2B04C3B400DB42BA /* HistoryFragment.swift in Sources */,
D79622342B1DFE600037EACD /* DialerBottomSheet.swift in Sources */,
C67586B02C09F247002E77BF /* URIHandler.swift in Sources */,
C62817282C1B389700DBA646 /* SideMenuAccountRow.swift in Sources */,
C60E8F192C0F649200A06DB8 /* UIApplicationExtension.swift in Sources */,
D78290BB2ADD40B2004AA85C /* ContactViewModel.swift in Sources */,
C67586B52C09F617002E77BF /* SingleSignOnManager.swift in Sources */,
@ -1037,9 +1060,12 @@
6613A0AE2BAEB7DF008923A4 /* MeetingFragment.swift in Sources */,
D7B5066D2AEFA9B900CEB4E9 /* ContactInnerFragment.swift in Sources */,
D7E6D04D2AEBD77600A57AAF /* CustomBottomSheet.swift in Sources */,
C62817302C1C3DCC00DBA646 /* AccountModel.swift in Sources */,
D73449992BC6932A00778C56 /* MeetingWaitingRoomFragment.swift in Sources */,
D7C48DF42AFA66F900D938CB /* EditContactController.swift in Sources */,
C628172E2C1C3A3600DBA646 /* AccountExtension.swift in Sources */,
66C491FF2B24D4AC00CEA16D /* FileUtils.swift in Sources */,
C62817322C1C400A00DBA646 /* StringExtension.swift in Sources */,
D74C9D012ACB098C0021626A /* PermissionManager.swift in Sources */,
D7B99E992B29B39000BE7BF2 /* CallViewModel.swift in Sources */,
D7702EF22AC7205000557C00 /* WelcomeView.swift in Sources */,
@ -1099,6 +1125,7 @@
D7D1698C2AE66FA500109A5C /* MagicSearchSingleton.swift in Sources */,
D74DA0122C047F0700A8561D /* HistoryModel.swift in Sources */,
D72250692ADFBF2D008FB426 /* SideMenu.swift in Sources */,
C6DC4E3F2C19C289009096FD /* SideMenuEntry.swift in Sources */,
D7CEE0352B7A210300FD79B7 /* ConversationsView.swift in Sources */,
D717071E2AC5922E0037746F /* ColorExtension.swift in Sources */,
D71968922B86369D00DF4459 /* ChatBubbleView.swift in Sources */,
@ -1108,6 +1135,7 @@
D74C9CFC2ACACF370021626A /* WelcomePage3Fragment.swift in Sources */,
C6A5A9412C10B5D50070FEA4 /* EncodableExtension.swift in Sources */,
D719ABCC2ABC769C00B41C10 /* AssistantView.swift in Sources */,
C62817342C1C7C7400DBA646 /* HelpView.swift in Sources */,
D78E062C2BEA69BC00CE3783 /* CallStatisticsSheetBottomSheet.swift in Sources */,
D7C365082AEFAB7F00FE6142 /* ContactListBottomSheet.swift in Sources */,
D7E6D04B2AE9347D00A57AAF /* FavoriteContactsListViewModel.swift in Sources */,

BIN
Linphone/.DS_Store vendored

Binary file not shown.

View file

@ -40,6 +40,7 @@ final class CoreContext: ObservableObject {
@Published var loggingInProgress: Bool = false
@Published var hasDefaultAccount: Bool = false
@Published var coreIsStarted: Bool = false
@Published var accounts: [Account] = []
private var mCore: Core!
private var mIterateSuscription: AnyCancellable?
@ -143,6 +144,7 @@ final class CoreContext: ObservableObject {
if cbVal.state == GlobalState.On {
self.hasDefaultAccount = self.mCore.defaultAccount != nil ? true : false
self.coreIsStarted = true
self.accounts = self.mCore.accountList
} else if cbVal.state == GlobalState.Off {
self.hasDefaultAccount = false
self.coreIsStarted = false
@ -159,6 +161,7 @@ final class CoreContext: ObservableObject {
if cbVal.status == ConfiguringState.Successful {
ToastViewModel.shared.toastMessage = "Successful"
ToastViewModel.shared.displayToast = true
self.accounts = self.mCore.accountList
}
}
/*
@ -208,7 +211,7 @@ final class CoreContext: ObservableObject {
if cbVal.state == .Ok {
self.loggingInProgress = false
self.loggedIn = true
} else if cbVal.state == .Progress {
} else if cbVal.state == .Progress || cbVal.state == .Refreshing {
self.loggingInProgress = true
} else {
self.loggingInProgress = false
@ -279,6 +282,16 @@ final class CoreContext: ObservableObject {
}
})
self.mCoreSuscriptions.insert(self.mCore.publisher?.onAccountAdded?
.postOnMainQueue { _ in
self.accounts = self.mCore.accountList
})
self.mCoreSuscriptions.insert(self.mCore.publisher?.onAccountRemoved?
.postOnMainQueue { _ in
self.accounts = self.mCore.accountList
})
self.mIterateSuscription = Timer.publish(every: 0.02, on: .main, in: .common)
.autoconnect()
.receive(on: coreQueue)
@ -344,6 +357,10 @@ final class CoreContext: ObservableObject {
mCore.removeDelegate(delegate: delegate)
}
func getCorePublisher() -> CoreDelegatePublisher? {
return mCore.publisher
}
}
// swiftlint:enable large_tuple

View file

@ -276,7 +276,7 @@
"Chiffrement du média" : {
},
"Clear logs" : {
"Clear Logs" : {
},
"Close" : {
@ -365,6 +365,124 @@
},
"Dont save modifications?" : {
},
"drawer_menu_account_connection_status_cleared" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Disabled"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Désactivé"
}
}
}
},
"drawer_menu_account_connection_status_connected" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Connected"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Connecté"
}
}
}
},
"drawer_menu_account_connection_status_failed" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Error"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Erreur"
}
}
}
},
"drawer_menu_account_connection_status_progress" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Connecting…"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "En cours de connexion…"
}
}
}
},
"drawer_menu_account_connection_status_refreshing" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Refreshing ..."
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "En cours de rafraîchissement…"
}
}
}
},
"drawer_menu_add_account" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Add an account"
}
},
"fr" : {
"stringUnit" : {
"state" : "needs_review",
"value" : "Ajouter un compte"
}
}
}
},
"drawer_menu_no_account_configured_yet" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "No account configured yet"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Aucun compte configuré"
}
}
}
},
"Earpiece" : {
@ -433,6 +551,23 @@
},
"Headphones" : {
},
"help_title" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Help"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Aide"
}
}
}
},
"History has been deleted" : {
@ -490,6 +625,12 @@
"Key" : {
"extractionState" : "manual"
},
"Key 1" : {
"extractionState" : "manual"
},
"Key 2" : {
"extractionState" : "manual"
},
"Last name" : {
},
@ -647,6 +788,23 @@
},
"Record" : {
},
"recordings_title" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Recordings"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Enregistrements"
}
}
}
},
"Register" : {
@ -706,8 +864,25 @@
"Send invitations to participants" : {
},
"Send logs" : {
"Send Logs" : {
},
"settings_title" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Settings"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Paramètres"
}
}
}
},
"Share" : {
@ -747,6 +922,9 @@
},
"TCP" : {
},
"Temp Help" : {
},
"The user name or password is incorrects" : {

View file

@ -37,6 +37,10 @@ struct LoginFragment: View {
@State private var isLinkSIPActive = false
@State private var isLinkREGActive = false
var isShowBack = false
var onBackPressed: (() -> Void)?
var body: some View {
NavigationView {
ZStack {
@ -49,6 +53,31 @@ struct LoginFragment: View {
.scaledToFill()
.frame(width: geometry.size.width, height: 100)
.clipped()
if isShowBack {
VStack(alignment: .leading) {
HStack {
Image("caret-left")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c500)
.frame(width: 25, height: 25, alignment: .leading)
.padding(.all, 10)
.padding(.top, -75)
.padding(.leading, -10)
.onTapGesture {
withAnimation {
onBackPressed?()
}
}
Spacer()
}
.padding(.leading)
}
.frame(width: geometry.size.width)
}
Text("assistant_account_login")
.default_text_style_white_800(styleSize: 20)
.padding(.top, 20)

View file

@ -64,6 +64,7 @@ struct ContentView: View {
@State var fullscreenVideo = false
@State var isShowScheduleMeetingFragment = false
@State private var isShowLoginFragment: Bool = false
var body: some View {
let pub = NotificationCenter.default
@ -765,15 +766,30 @@ struct ContentView: View {
}
SideMenu(
callViewModel: callViewModel,
width: geometry.size.width / 5 * 4,
isOpen: self.sideMenuIsOpen,
menuClose: self.openMenu,
safeAreaInsets: geometry.safeAreaInsets
safeAreaInsets: geometry.safeAreaInsets,
isShowLoginFragment: $isShowLoginFragment
)
.ignoresSafeArea(.all)
.zIndex(2)
if isShowLoginFragment {
LoginFragment(
accountLoginViewModel: AccountLoginViewModel(),
isShowBack: true,
onBackPressed: {
withAnimation {
isShowLoginFragment.toggle()
}
})
.zIndex(3)
.transition(.move(edge: .bottom))
.onAppear {
}
}
if isShowEditContactFragment {
EditContactFragment(
editContactViewModel: editContactViewModel,

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2010-2024 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 Foundation
import linphonesw
class HelpView { // TODO (basic debug moved here until halp view is implemented)
static func sendLogs() {
CoreContext.shared.doOnCoreQueue { core in
core.uploadLogCollection()
}
}
static func clearLogs() {
CoreContext.shared.doOnCoreQueue { _ in
Core.resetLogCollection()
DispatchQueue.main.async {
ToastViewModel.shared.toastMessage = "Success_clear_logs"
ToastViewModel.shared.displayToast = true
}
}
}
static func logout() {
CoreContext.shared.doOnCoreQueue { core in
if core.defaultAccount != nil {
let authInfo = core.defaultAccount!.findAuthInfo()
if authInfo != nil {
Log.info("$TAG Found auth info for account, removing it")
core.removeAuthInfo(info: authInfo!)
} else {
Log.warn("$TAG Failed to find matching auth info for account")
}
core.removeAccount(account: core.defaultAccount!)
Log.info("$TAG Account has been removed")
DispatchQueue.main.async {
CoreContext.shared.hasDefaultAccount = false
CoreContext.shared.loggedIn = false
}
}
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2023 Belledonne Communications SARL.
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-iphone
*
@ -23,114 +23,165 @@ import UniformTypeIdentifiers
struct SideMenu: View {
@ObservedObject private var coreContext = CoreContext.shared
let width: CGFloat
let isOpen: Bool
let menuClose: () -> Void
let safeAreaInsets: EdgeInsets
@Binding var isShowLoginFragment: Bool
@State private var showHelp = false
@State private var selectedAccountIndex: Int = 0
@ObservedObject var callViewModel: CallViewModel
let width: CGFloat
let isOpen: Bool
let menuClose: () -> Void
let safeAreaInsets: EdgeInsets
var body: some View {
ZStack {
GeometryReader { _ in
EmptyView()
}
.background(Color.gray.opacity(0.3))
.opacity(self.isOpen ? 1.0 : 0.0)
.onTapGesture {
self.menuClose()
}
HStack {
List {
/*
Text("My Profile")
.frame(height: 40)
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color.white)
.onTapGesture {
print("My Profile")
self.menuClose()
}
*/
Text("Send logs")
.frame(height: 40)
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color.white)
.onTapGesture {
print("Send logs")
sendLogs()
self.menuClose()
}
Text("Clear logs")
.frame(height: 40)
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color.white)
.onTapGesture {
print("Clear logs")
clearLogs()
self.menuClose()
}
Text("Logout")
.frame(height: 40)
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color.white)
.onTapGesture {
print("Logout")
logout()
self.menuClose()
}
}
.frame(width: self.width - safeAreaInsets.leading)
.background(Color.white)
.offset(x: self.isOpen ? 0 : -self.width)
Spacer()
}
.padding(.leading, safeAreaInsets.leading)
.padding(.top, TelecomManager.shared.callInProgress ? 0 : safeAreaInsets.top)
.padding(.bottom, safeAreaInsets.bottom)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
func sendLogs() {
coreContext.doOnCoreQueue { core in
core.uploadLogCollection()
}
}
func clearLogs() {
coreContext.doOnCoreQueue { core in
Core.resetLogCollection()
DispatchQueue.main.async {
ToastViewModel.shared.toastMessage = "Success_clear_logs"
ToastViewModel.shared.displayToast = true
var body: some View {
ZStack {
GeometryReader { _ in
EmptyView()
}
}
}
func logout() {
coreContext.doOnCoreQueue { core in
if core.defaultAccount != nil {
let authInfo = core.defaultAccount!.findAuthInfo()
if authInfo != nil {
Log.info("$TAG Found auth info for account, removing it")
core.removeAuthInfo(info: authInfo!)
} else {
Log.warn("$TAG Failed to find matching auth info for account")
.background(.gray.opacity(0.3))
.opacity(self.isOpen ? 1.0 : 0.0)
.onTapGesture {
self.menuClose()
}
VStack {
VStack {
HStack {
Image("linphone")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.orangeMain500)
.frame(width: 32, height: 32)
.padding(10)
Text(Bundle.main.displayName)
.default_text_style_800(styleSize: 16)
Spacer()
Image("x")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c600)
.frame(width: 24, height: 24)
.padding(10)
}
.padding(.leading, 10)
.onTapGesture {
self.menuClose()
}
List {
ForEach(0..<CoreContext.shared.accounts.count, id: \.self) { index in
SideMenuAccountRow(
model: AccountModel(account: CoreContext.shared.accounts[0],
corePublisher: CoreContext.shared.getCorePublisher()),
backgroundColor: self.selectedAccountIndex == index ? Color.grayMain2c100 : .clear
)
.background()
.onTapGesture {
self.selectedAccountIndex = index
}
.listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
.listRowSeparator(.hidden)
}
}
.listStyle(.plain)
if CoreContext.shared.accounts.isEmpty {
Text("drawer_menu_no_account_configured_yet")
.text_style(fontSize: 16, fontWeight: 800, fontColor: Color.grayMain2c600)
.padding(.bottom, 30)
}
HStack {
Image("plus-circle")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.orangeMain500)
.frame(width: 20, height: 20)
Text("drawer_menu_add_account")
.default_text_style_orange_600(styleSize: 20)
.frame(height: 35)
}
.frame(maxWidth: .infinity)
.padding(.horizontal, 20)
.padding(.vertical, 10)
.cornerRadius(60)
.overlay(
RoundedRectangle(cornerRadius: 60)
.inset(by: 0.5)
.stroke(Color.orangeMain500, lineWidth: 1)
)
.padding(.leading, 16)
.padding(.trailing, 16)
.padding(.bottom, 23)
.background()
.onTapGesture {
self.menuClose()
withAnimation {
isShowLoginFragment = true
}
}
Rectangle()
.fill(Color.grayMain2c300)
.padding(.leading, 16)
.padding(.trailing, 16)
.frame(height: 1)
VStack(spacing: 19) {
SideMenuEntry(
iconName: "gear",
title: "settings_title"
)
SideMenuEntry(
iconName: "record-fill",
title: "recordings_title"
)
SideMenuEntry(
iconName: "question",
title: "help_title"
).onTapGesture {
showHelp = true
}
.confirmationDialog("Temp Help", isPresented: $showHelp, titleVisibility: .visible) {
Button("Send Logs") {
HelpView.sendLogs()
}
Button("Clear Logs") {
HelpView.clearLogs()
}
Button("Logout") {
HelpView.logout()
}
}
}
.padding(.bottom, safeAreaInsets.bottom + 13)
.padding(.top, 13)
.padding(.leading, 16)
.padding(.trailing, 16)
}
core.removeAccount(account: core.defaultAccount!)
Log.info("$TAG Account has been removed")
.frame(width: self.width - safeAreaInsets.leading)
.background(.white)
.offset(x: self.isOpen ? 0 : -self.width)
DispatchQueue.main.async {
coreContext.hasDefaultAccount = false
coreContext.loggedIn = false
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, safeAreaInsets.leading)
.padding(.top, TelecomManager.shared.callInProgress ? 0 : safeAreaInsets.top)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
#Preview {
GeometryReader { geometry in
@State var triggerNavigateToLogin: Bool = false
SideMenu(
width: geometry.size.width / 5 * 4,
isOpen: true,
menuClose: {},
safeAreaInsets: geometry.safeAreaInsets,
isShowLoginFragment: $triggerNavigateToLogin
)
.ignoresSafeArea(.all)
.zIndex(2)
}
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2010-2024 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
import UniformTypeIdentifiers
struct SideMenuAccountRow: View {
@ObservedObject var model: AccountModel
var backgroundColor: Color
var body: some View {
HStack {
Avatar(contactAvatarModel:
ContactAvatarModel(friend: nil,
name: model.account.displayName(),
address: model.account.params!.identityAddress!.asString(),
withPresence: true),
avatarSize: 45)
.padding(.leading, 6)
VStack {
Text(model.account.displayName())
.default_text_style_grey_400(styleSize: 14)
.lineLimit(1)
.frame(maxWidth: .infinity, alignment: .leading)
VStack {
Text(model.humanReadableRegistrationState)
.default_text_style_uncolored(styleSize: 12)
.foregroundStyle(model.registrationStateAssociatedUIColor)
}
.padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8))
.background(Color.grayMain2c200)
.cornerRadius(12)
.frame(height: 20)
.frame(maxWidth: .infinity, alignment: .leading)
.onTapGesture {
model.refreshRegiter()
}
}
.padding(.leading, 4)
Spacer()
HStack {
if model.notificationsCount > 0 {
Text(String(model.notificationsCount))
.foregroundStyle(.white)
.default_text_style(styleSize: 12)
.lineLimit(1)
.frame(width: 20, height: 20)
.background(Color.redDanger500)
.cornerRadius(50)
.frame(maxWidth: .infinity, alignment: .leading)
}
Image("dots-three-vertical")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c600)
.scaledToFit()
.frame(height: 30)
}
.frame(width: 64, alignment: .trailing)
.padding(.top, 12)
.padding(.bottom, 12)
}
.frame(height: 61)
.background(backgroundColor)
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2010-2024 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
import UniformTypeIdentifiers
struct SideMenuEntry: View {
var iconName: String
var title: String
var body: some View {
HStack {
Image(iconName)
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c500)
.frame(width: 20, height: 20)
Text(NSLocalizedString(title, comment: title))
.default_text_style_600(styleSize: 13)
.padding(.leading, 4)
Spacer()
Image("caret-right")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c600)
.frame(width: 20, height: 20)
}
.background()
}
}
#Preview {
SideMenuEntry(
iconName: "linphone",
title: "some text"
)
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of Linphone
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import Foundation
import linphonesw
import SwiftUI
class AccountModel: ObservableObject {
let account: Account
@Published var humanReadableRegistrationState: String = ""
@Published var registrationStateAssociatedUIColor: Color = .clear
@Published var notificationsCount: Int = 0
init(account: Account, corePublisher: CoreDelegatePublisher?) {
self.account = account
update()
account.publisher?.onRegistrationStateChanged?
.postOnMainQueue { _ in
self.update()
}
corePublisher?.onChatRoomRead?.postOnMainQueue(
receiveValue: { _ in
self.computeNotificationsCount()
})
corePublisher?.onMessagesReceived?.postOnMainQueue(
receiveValue: { _ in
self.computeNotificationsCount()
})
corePublisher?.onCallStateChanged?.postOnMainQueue(
receiveValue: { _ in
self.computeNotificationsCount()
})
}
func update() {
switch account.state {
case .Cleared, .None:
humanReadableRegistrationState = "drawer_menu_account_connection_status_cleared".localized()
registrationStateAssociatedUIColor = .orangeWarning600
case .Progress:
humanReadableRegistrationState = "drawer_menu_account_connection_status_progress".localized()
registrationStateAssociatedUIColor = .greenSuccess500
case .Failed:
humanReadableRegistrationState = "drawer_menu_account_connection_status_failed".localized()
registrationStateAssociatedUIColor = .redDanger500
case .Ok:
humanReadableRegistrationState = "drawer_menu_account_connection_status_connected".localized()
registrationStateAssociatedUIColor = .greenSuccess500
case .Refreshing:
humanReadableRegistrationState = "drawer_menu_account_connection_status_refreshing".localized()
registrationStateAssociatedUIColor = .grayMain2c500
}
computeNotificationsCount()
}
func computeNotificationsCount() {
notificationsCount = account.unreadChatMessageCount + account.missedCallsCount
}
func refreshRegiter() {
CoreContext.shared.doOnCoreQueue { _ in
self.account.refreshRegister()
}
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2010-2024 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 Foundation
import linphonesw
extension Account {
func displayName() -> String {
guard let address = params?.identityAddress else {
return ""
}
if address.displayName != nil && !address.displayName!.isEmpty {
return address.displayName!
}
if address.username != nil && !address.username!.isEmpty {
return address.username!
}
return address.asStringUriOnly()
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2010-2024 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 Foundation
extension Bundle {
var displayName: String {
return object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ?? "Linphone"
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2010-2024 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 Foundation
extension String {
func localized(comment: String? = nil) -> String {
return NSLocalizedString(self, comment: comment != nil ? comment! : self)
}
}

View file

@ -20,6 +20,18 @@
import Foundation
import SwiftUI
let cssWeightToFontWeightName = [
100: "UltraLight",
200: "Thin",
300: "Light",
400: "Regular",
500: "Medium",
600: "SemiBold",
700: "Bold",
800: "Heavy",
900: "Black"
]
extension View {
func default_text_style_300(styleSize: CGFloat) -> some View {
@ -141,4 +153,18 @@ extension View {
self.font(Font.custom("NotoSans-Medium", size: styleSize))
.foregroundStyle(Color.grayMain2c400)
}
func default_text_style_grey_400(styleSize: CGFloat) -> some View {
self.font(Font.custom("NotoSans", size: styleSize))
.foregroundStyle(Color.grayMain2c700)
}
func default_text_style_uncolored(styleSize: CGFloat) -> some View {
self.font(Font.custom("NotoSans-Light", size: styleSize))
}
func text_style(fontSize: CGFloat, fontWeight: Int, fontColor: Color) -> some View {
return self.font(Font.custom("NotoSans-"+cssWeightToFontWeightName[fontWeight]!, size: fontSize))
.foregroundStyle(fontColor)
}
}