Add Account Profile

This commit is contained in:
benoit.martins 2024-12-03 17:55:01 +01:00
parent 2e4b0dc1e4
commit 8cd335e649
10 changed files with 1473 additions and 1070 deletions

View file

@ -164,6 +164,8 @@
D7D24D182AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D122AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf */; };
D7DA67622ACCB2FA00E95002 /* LoginFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA67612ACCB2FA00E95002 /* LoginFragment.swift */; };
D7DA67642ACCB31700E95002 /* ProfileModeFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */; };
D7DC096F2CFA1D7600A6D47C /* AccountProfileFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DC096E2CFA1D7400A6D47C /* AccountProfileFragment.swift */; };
D7DC09712CFDBF9A00A6D47C /* AccountProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DC09702CFDBF8300A6D47C /* AccountProfileViewModel.swift */; };
D7E2E69F2CE356C90080DA0D /* PopupViewWithTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E2E69E2CE356C90080DA0D /* PopupViewWithTextField.swift */; };
D7E6ADF32B9875C20009A2BC /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E6ADF22B9875C20009A2BC /* Message.swift */; };
D7E6ADF52B9876ED0009A2BC /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E6ADF42B9876ED0009A2BC /* Attachment.swift */; };
@ -358,6 +360,8 @@
D7D24D122AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-ExtraBold.ttf"; sourceTree = "<group>"; };
D7DA67612ACCB2FA00E95002 /* LoginFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginFragment.swift; sourceTree = "<group>"; };
D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModeFragment.swift; sourceTree = "<group>"; };
D7DC096E2CFA1D7400A6D47C /* AccountProfileFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountProfileFragment.swift; sourceTree = "<group>"; };
D7DC09702CFDBF8300A6D47C /* AccountProfileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountProfileViewModel.swift; sourceTree = "<group>"; };
D7E2E69E2CE356C90080DA0D /* PopupViewWithTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupViewWithTextField.swift; sourceTree = "<group>"; };
D7E6ADF22B9875C20009A2BC /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = "<group>"; };
D7E6ADF42B9876ED0009A2BC /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = "<group>"; };
@ -602,6 +606,7 @@
D7A03FBE2ACC2E010081A588 /* History */,
66E56BC52BA45E49006CE56F /* Meetings */,
66D382032CEB7DB80063E1C5 /* Models */,
D7DC096A2CFA192200A6D47C /* Settings */,
D7A2EDD42AC180FE005D90FC /* Viewmodel */,
D719ABB82ABC67BF00B41C10 /* ContentView.swift */,
);
@ -909,6 +914,39 @@
path = Fragments;
sourceTree = "<group>";
};
D7DC096A2CFA192200A6D47C /* Settings */ = {
isa = PBXGroup;
children = (
D7DC096B2CFA192F00A6D47C /* Fragments */,
D7DC096C2CFA193B00A6D47C /* Models */,
D7DC096D2CFA194600A6D47C /* ViewModel */,
);
path = Settings;
sourceTree = "<group>";
};
D7DC096B2CFA192F00A6D47C /* Fragments */ = {
isa = PBXGroup;
children = (
D7DC096E2CFA1D7400A6D47C /* AccountProfileFragment.swift */,
);
path = Fragments;
sourceTree = "<group>";
};
D7DC096C2CFA193B00A6D47C /* Models */ = {
isa = PBXGroup;
children = (
);
path = Models;
sourceTree = "<group>";
};
D7DC096D2CFA194600A6D47C /* ViewModel */ = {
isa = PBXGroup;
children = (
D7DC09702CFDBF8300A6D47C /* AccountProfileViewModel.swift */,
);
path = ViewModel;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -1132,6 +1170,7 @@
D7C2DA1D2CA44DE400A2441B /* EventModel.swift in Sources */,
D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */,
D70A26F22B7F5D95006CC8FC /* ConversationFragment.swift in Sources */,
D7DC096F2CFA1D7600A6D47C /* AccountProfileFragment.swift in Sources */,
D717A10E2CEB772300849D92 /* ShareSheetController.swift in Sources */,
66C491FD2B24D36500CEA16D /* AudioRouteUtils.swift in Sources */,
D70959F12B8DF3EC0014AC0B /* ConversationModel.swift in Sources */,
@ -1211,6 +1250,7 @@
D7CEE0382B7A214F00FD79B7 /* ConversationsListViewModel.swift in Sources */,
D74C9CF82ACACECE0021626A /* WelcomePage1Fragment.swift in Sources */,
66E56BCE2BA9A1F8006CE56F /* MeetingModel.swift in Sources */,
D7DC09712CFDBF9A00A6D47C /* AccountProfileViewModel.swift in Sources */,
D7E6D0552AEBFCCE00A57AAF /* ContactsInnerFragment.swift in Sources */,
D72343362AD037AF009AA24E /* ToastView.swift in Sources */,
D7FB55112AD447FD00A5AB15 /* RegisterFragment.swift in Sources */,

View file

@ -144,6 +144,7 @@ struct LinphoneApp: App {
@State private var meetingsListViewModel: MeetingsListViewModel?
@State private var meetingViewModel: MeetingViewModel?
@State private var conversationForwardMessageViewModel: ConversationForwardMessageViewModel?
@State private var accountProfileViewModel: AccountProfileViewModel?
var body: some Scene {
WindowGroup {
@ -175,7 +176,8 @@ struct LinphoneApp: App {
&& conversationViewModel != nil
&& meetingsListViewModel != nil
&& meetingViewModel != nil
&& conversationForwardMessageViewModel != nil {
&& conversationForwardMessageViewModel != nil
&& accountProfileViewModel != nil {
ContentView(
contactViewModel: contactViewModel!,
editContactViewModel: editContactViewModel!,
@ -189,7 +191,8 @@ struct LinphoneApp: App {
conversationViewModel: conversationViewModel!,
meetingsListViewModel: meetingsListViewModel!,
meetingViewModel: meetingViewModel!,
conversationForwardMessageViewModel: conversationForwardMessageViewModel!
conversationForwardMessageViewModel: conversationForwardMessageViewModel!,
accountProfileViewModel: accountProfileViewModel!
)
.environmentObject(navigationManager)
.onAppear {
@ -201,6 +204,8 @@ struct LinphoneApp: App {
// Notify the app to navigate to the chat room
navigationManager.openChatRoom(callId: callId, peerAddr: peerAddr, localAddr: localAddr)
}
accountProfileViewModel!.setAvatarModel()
}
.onOpenURL { url in
URIHandler.handleURL(url: url)
@ -226,6 +231,7 @@ struct LinphoneApp: App {
meetingsListViewModel = MeetingsListViewModel()
meetingViewModel = MeetingViewModel()
conversationForwardMessageViewModel = ConversationForwardMessageViewModel()
accountProfileViewModel = AccountProfileViewModel()
}.onOpenURL { url in
URIHandler.handleURL(url: url)
}

View file

@ -2533,6 +2533,23 @@
}
}
},
"drawer_menu_manage_account" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Manage the profile"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Paramètres du profil"
}
}
}
},
"drawer_menu_no_account_configured_yet" : {
"extractionState" : "manual",
"localizations" : {
@ -2882,6 +2899,23 @@
}
}
},
"manage_account_title" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Manage account"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Votre compte"
}
}
}
},
"Marquer comme non lu" : {
},

View file

@ -34,8 +34,8 @@ host=
port=443
[misc]
log_collection_upload_server_url=https://www.linphone.org:444/lft.php
file_transfer_server_url=https://www.linphone.org:444/lft.php
log_collection_upload_server_url=https://files.linphone.org:443/http-file-transfer-server/hft.php
file_transfer_server_url=https://files.linphone.org:443/http-file-transfer-server/hft.php
version_check_url_root=https://www.linphone.org/releases
max_calls=10
conference_layout=1

File diff suppressed because it is too large Load diff

View file

@ -28,6 +28,7 @@ struct SideMenu: View {
let menuClose: () -> Void
let safeAreaInsets: EdgeInsets
@Binding var isShowLoginFragment: Bool
@Binding var isShowAccountProfileFragment: Bool
@State private var showHelp = false
var body: some View {
@ -66,7 +67,7 @@ struct SideMenu: View {
List {
ForEach(0..<CoreContext.shared.accounts.count, id: \.self) { index in
SideMenuAccountRow( model: CoreContext.shared.accounts[index])
SideMenuAccountRow( model: CoreContext.shared.accounts[index], isShowAccountProfileFragment: $isShowAccountProfileFragment)
.background()
.listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
.listRowSeparator(.hidden)
@ -174,7 +175,8 @@ struct SideMenu: View {
isOpen: true,
menuClose: {},
safeAreaInsets: geometry.safeAreaInsets,
isShowLoginFragment: $triggerNavigateToLogin
isShowLoginFragment: $triggerNavigateToLogin,
isShowAccountProfileFragment: .constant(false)
)
.ignoresSafeArea(.all)
.zIndex(2)

View file

@ -23,9 +23,12 @@ import UniformTypeIdentifiers
struct SideMenuAccountRow: View {
@ObservedObject var model: AccountModel
@State private var navigateToOption = false
@Binding var isShowAccountProfileFragment: Bool
var body: some View {
HStack {
Avatar(contactAvatarModel:
ContactAvatarModel(friend: nil,
name: model.displayName,
@ -69,12 +72,23 @@ struct SideMenuAccountRow: View {
.cornerRadius(50)
.frame(maxWidth: .infinity, alignment: .leading)
}
Image("dots-three-vertical")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c600)
.scaledToFit()
.frame(height: 30)
Menu {
Button {
withAnimation {
isShowAccountProfileFragment = true
}
} label: {
Label("drawer_menu_manage_account", systemImage: "arrow.right.circle")
}
} label: {
Image("dots-three-vertical")
.renderingMode(.template)
.resizable()
.foregroundColor(Color.gray)
.scaledToFit()
.frame(height: 30)
}
}
.frame(width: 64, alignment: .trailing)
.padding(.top, 12)

View file

@ -52,8 +52,3 @@ struct SideMenuShortcut: View {
}
}
}
#Preview {
SideMenuShortcut(shortcutModel: ShortcutModel(linkUrl: URL("link")!, name: "name", iconLinkUrl: URL("icon")!)
)
}

View file

@ -0,0 +1,258 @@
/*
* 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 AccountProfileFragment: View {
@ObservedObject var contactsManager = ContactsManager.shared
@ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared
@ObservedObject var accountProfileViewModel: AccountProfileViewModel
@Binding var isShowAccountProfileFragment: Bool
@State var detailIsOpen: Bool = true
@State private var showPhotoPicker = false
@State private var selectedImage: UIImage?
@State private var removedImage = false
var body: some View {
GeometryReader { geometry in
VStack(spacing: 1) {
Rectangle()
.foregroundColor(Color.orangeMain500)
.edgesIgnoringSafeArea(.top)
.frame(height: 0)
HStack {
Image("caret-left")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.orangeMain500)
.frame(width: 25, height: 25, alignment: .leading)
.padding(.all, 10)
.padding(.top, 4)
.padding(.leading, -10)
.onTapGesture {
withAnimation {
if isShowAccountProfileFragment {
isShowAccountProfileFragment = false
}
}
}
Text("manage_account_title")
.default_text_style_orange_800(styleSize: 16)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.top, 4)
.lineLimit(1)
Spacer()
}
.frame(maxWidth: .infinity)
.frame(height: 50)
.padding(.horizontal)
.padding(.bottom, 4)
.background(.white)
Spacer()
/*
ScrollView {
VStack(spacing: 0) {
VStack(spacing: 0) {
if #unavailable(iOS 16.0) {
Rectangle()
.foregroundColor(Color.gray100)
.frame(height: 7)
}
if accountProfileViewModel.avatarModel != nil {
VStack(spacing: 0) {
if accountProfileViewModel.avatarModel != nil
&& accountProfileViewModel.photoAvatarModel != nil
&& !accountProfileViewModel.photoAvatarModel!.isEmpty && selectedImage == nil && !removedImage {
Avatar(
contactAvatarModel: ContactAvatarModel(accountProfileViewModel.avatarModel!),
avatarSize: 100
)
} else if selectedImage == nil {
Image("profil-picture-default")
.resizable()
.frame(width: 100, height: 100)
.clipShape(Circle())
} else {
Image(uiImage: selectedImage!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.clipShape(Circle())
}
if accountProfileViewModel.avatarModel != nil
&& accountProfileViewModel.photoAvatarModel != nil
&& !accountProfileViewModel.photoAvatarModel!.isEmpty
&& (accountProfileViewModel.photoAvatarModel!.suffix(11) != "default.png" || selectedImage != nil)
&& !removedImage {
HStack {
Spacer()
Button(action: {
showPhotoPicker = true
}, label: {
HStack {
Image("pencil-simple")
.resizable()
.frame(width: 20, height: 20)
Text("manage_account_edit_picture")
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
}
})
.padding(.top, 10)
.padding(.trailing, 10)
.sheet(isPresented: $showPhotoPicker) {
PhotoPicker(filter: .images, limit: 1) { results in
PhotoPicker.convertToUIImageArray(fromResults: results) { imagesOrNil, errorOrNil in
if let error = errorOrNil {
print(error)
}
if let images = imagesOrNil {
if let first = images.first {
selectedImage = first
removedImage = false
}
}
}
}
.edgesIgnoringSafeArea(.all)
}
Button(action: {
removedImage = true
selectedImage = nil
}, label: {
HStack {
Image("trash-simple")
.resizable()
.frame(width: 20, height: 20)
Text("manage_account_remove_picture")
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
}
})
.padding(.top, 10)
Spacer()
}
} else {
Button(action: {
showPhotoPicker = true
}, label: {
HStack {
Image("camera")
.resizable()
.frame(width: 20, height: 20)
Text("manage_account_add_picture")
.foregroundStyle(Color.grayMain2c700)
.multilineTextAlignment(.center)
.default_text_style(styleSize: 14)
}
})
.padding(.top, 10)
.sheet(isPresented: $showPhotoPicker) {
PhotoPicker(filter: .images, limit: 1) { results in
PhotoPicker.convertToUIImageArray(fromResults: results) { imagesOrNil, errorOrNil in
if let error = errorOrNil {
print(error)
}
if let images = imagesOrNil {
if let first = images.first {
selectedImage = first
removedImage = false
}
}
}
}
.edgesIgnoringSafeArea(.all)
}
}
}
.frame(minHeight: 150)
.frame(maxWidth: .infinity)
.padding(.top, 10)
.padding(.bottom, 2)
.background(Color.gray100)
}
HStack(alignment: .center) {
Text("conversation_info_participants_list_title")
.default_text_style_800(styleSize: 18)
.frame(maxWidth: .infinity, alignment: .leading)
Spacer()
Image(detailIsOpen ? "caret-up" : "caret-down")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.grayMain2c600)
.frame(width: 25, height: 25, alignment: .leading)
.padding(.all, 10)
}
.padding(.top, 30)
.padding(.bottom, 10)
.padding(.horizontal, 20)
.background(Color.gray100)
.onTapGesture {
withAnimation {
detailIsOpen.toggle()
}
}
if detailIsOpen {
VStack(spacing: 0) {
}
.background(.white)
.cornerRadius(15)
.padding(.horizontal)
.zIndex(-1)
.transition(.move(edge: .top))
}
}
.frame(maxWidth: sharedMainViewModel.maxWidth)
}
.frame(maxWidth: .infinity)
.padding(.top, 2)
}
.background(Color.gray100)
*/
}
.background(Color.gray100)
}
.navigationTitle("")
.navigationBarHidden(true)
}
}

View file

@ -0,0 +1,40 @@
/*
* 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 AccountProfileViewModel: ObservableObject {
@Published var avatarModel: ContactAvatarModel?
@Published var photoAvatarModel: String?
init() {}
func setAvatarModel() {
CoreContext.shared.doOnCoreQueue { core in
if core.defaultAccount != nil {
let displayNameTmp = core.defaultAccount!.displayName()
let contactAddressTmp = core.defaultAccount!.contactAddress?.asStringUriOnly() ?? ""
DispatchQueue.main.async {
self.avatarModel = ContactAvatarModel(friend: nil, name: displayNameTmp, address: contactAddressTmp, withPresence: false)
}
}
}
}
}