123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- //
- // SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
- // SPDX-License-Identifier: GPL-3.0-or-later
- //
- import UIKit
- enum ProfileSection: Int {
- case kProfileSectionName = 0
- case kProfileSectionEmail
- case kProfileSectionPhoneNumber
- case kProfileSectionAddress
- case kProfileSectionWebsite
- case kProfileSectionTwitter
- case kProfileSectionSummary
- case kProfileSectionRemoveAccount
- }
- enum SummaryRow: Int {
- case kSummaryRowEmail = 0
- case kSummaryRowPhoneNumber
- case kSummaryRowAddress
- case kSummaryRowWebsite
- case kSummaryRowTwitter
- }
- @objcMembers
- class UserProfileTableViewController: UITableViewController, DetailedOptionsSelectorTableViewControllerDelegate, TOCropViewControllerDelegate {
- let kNameTextFieldTag = 99
- let kEmailTextFieldTag = 98
- let kPhoneTextFieldTag = 97
- let kAddressTextFieldTag = 96
- let kWebsiteTextFieldTag = 95
- let kTwitterTextFieldTag = 94
- let kAvatarScopeButtonTag = 93
- let iconConfiguration = UIImage.SymbolConfiguration(pointSize: 18)
- let iconHeaderConfiguration = UIImage.SymbolConfiguration(pointSize: 13)
- var account = TalkAccount()
- var isEditable = Bool()
- var waitingForModification = Bool()
- var editButton = UIBarButtonItem()
- var activeTextField: UITextField?
- var modifyingProfileView = UIActivityIndicatorView()
- var imagePicker: UIImagePickerController?
- var setPhoneAction = UIAlertAction()
- var phoneUtil = NBPhoneNumberUtil()
- var editableFields = NSArray()
- var showScopes = Bool()
- override func viewDidLoad() {
- super.viewDidLoad()
- self.navigationItem.title = NSLocalizedString("Profile", comment: "")
- self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: NCAppBranding.themeTextColor()]
- self.navigationController?.navigationBar.tintColor = NCAppBranding.themeTextColor()
- self.navigationController?.navigationBar.barTintColor = NCAppBranding.themeColor()
- self.tabBarController?.tabBar.tintColor = NCAppBranding.themeColor()
- let themeColor: UIColor = NCAppBranding.themeColor()
- let appearance = UINavigationBarAppearance()
- appearance.configureWithOpaqueBackground()
- appearance.backgroundColor = themeColor
- appearance.titleTextAttributes = [.foregroundColor: NCAppBranding.themeTextColor()]
- self.navigationItem.standardAppearance = appearance
- self.navigationItem.compactAppearance = appearance
- self.navigationItem.scrollEdgeAppearance = appearance
- self.tableView.tableHeaderView = self.avatarHeaderView()
- self.showEditButton()
- self.getUserProfileEditableFields()
- if let serverCapabilities = NCDatabaseManager.sharedInstance().serverCapabilities(forAccountId: account.accountId) {
- showScopes = serverCapabilities.accountPropertyScopesVersion2
- }
- modifyingProfileView = UIActivityIndicatorView()
- modifyingProfileView.color = NCAppBranding.themeTextColor()
- tableView.keyboardDismissMode = UIScrollView.KeyboardDismissMode.onDrag
- self.tableView.register(UINib(nibName: kTextInputTableViewCellNibName, bundle: nil), forCellReuseIdentifier: kTextInputCellIdentifier)
- NotificationCenter.default.addObserver(self, selector: #selector(userProfileImageUpdated), name: NSNotification.Name.NCUserProfileImageUpdated, object: nil)
- if navigationController?.viewControllers.first == self {
- let barButtonItem = UIBarButtonItem(title: nil, style: .plain, target: nil, action: nil)
- barButtonItem.primaryAction = UIAction(title: NSLocalizedString("Close", comment: ""), handler: { [unowned self] _ in
- self.dismiss(animated: true)
- })
- self.navigationItem.leftBarButtonItems = [barButtonItem]
- }
- }
- override func viewDidLayoutSubviews() {
- super.viewDidLayoutSubviews()
- // Workaround to fix label width
- guard let headerView = self.tableView.tableHeaderView as? AvatarEditView else {return}
- guard var labelFrame = headerView.nameLabel?.frame else {return}
- let padding: CGFloat = 16
- labelFrame.origin.x = padding
- labelFrame.size.width = self.tableView.bounds.size.width - padding * 2
- headerView.nameLabel?.frame = labelFrame
- }
- init(withAccount account: TalkAccount) {
- super.init(style: .insetGrouped)
- self.account = account
- }
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- // MARK: - Table view data source
- override func numberOfSections(in tableView: UITableView) -> Int {
- return self.getProfileSections().count
- }
- override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- let sections = self.getProfileSections()
- let profileSection = sections[section]
- if profileSection == ProfileSection.kProfileSectionSummary.rawValue {
- return self.rowsInSummarySection().count
- }
- return 1
- }
- override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
- let sections = self.getProfileSections()
- let profileSection = sections[section]
- switch profileSection {
- case ProfileSection.kProfileSectionName.rawValue,
- ProfileSection.kProfileSectionEmail.rawValue,
- ProfileSection.kProfileSectionPhoneNumber.rawValue,
- ProfileSection.kProfileSectionAddress.rawValue,
- ProfileSection.kProfileSectionWebsite.rawValue,
- ProfileSection.kProfileSectionTwitter.rawValue,
- ProfileSection.kProfileSectionRemoveAccount.rawValue:
- return 40
- case ProfileSection.kProfileSectionSummary.rawValue:
- return 20
- default:
- return 0
- }
- }
- override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
- let sections = self.getProfileSections()
- let profileSection = sections[section]
- let headerView = setupViewforHeaderInSection(profileSection: profileSection)
- if headerView.button.tag != 0 {
- return headerView
- }
- return nil
- }
- override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
- let sections = self.getProfileSections()
- let profileSection = sections[section]
- if profileSection == ProfileSection.kProfileSectionEmail.rawValue {
- return NSLocalizedString("For password reset and notifications", comment: "")
- }
- return nil
- }
- override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let section = self.getProfileSections()[indexPath.section]
- switch section {
- case ProfileSection.kProfileSectionName.rawValue:
- return textInputCellWith(text: account.userDisplayName, tag: kNameTextFieldTag, interactionEnabled: editableFields.contains(kUserProfileDisplayName),
- keyBoardType: nil, autocapitalizationType: .sentences, placeHolder: nil)
- case ProfileSection.kProfileSectionEmail.rawValue:
- return textInputCellWith(text: account.email, tag: kEmailTextFieldTag, interactionEnabled: editableFields.contains(kUserProfileEmail),
- keyBoardType: .emailAddress, autocapitalizationType: nil, placeHolder: NSLocalizedString("Your email address", comment: ""))
- case ProfileSection.kProfileSectionPhoneNumber.rawValue:
- let phoneNumber = try? phoneUtil.parse(account.phone, defaultRegion: nil)
- let text = (phoneNumber != nil) ? try? phoneUtil.format(phoneNumber, numberFormat: NBEPhoneNumberFormat.INTERNATIONAL) : nil
- return textInputCellWith(text: text, tag: kPhoneTextFieldTag, interactionEnabled: false,
- keyBoardType: .phonePad, autocapitalizationType: nil, placeHolder: NSLocalizedString("Your phone number", comment: ""))
- case ProfileSection.kProfileSectionAddress.rawValue:
- return textInputCellWith(text: account.address, tag: kAddressTextFieldTag, interactionEnabled: editableFields.contains(kUserProfileAddress),
- keyBoardType: nil, autocapitalizationType: .sentences, placeHolder: NSLocalizedString("Your postal address", comment: ""))
- case ProfileSection.kProfileSectionWebsite.rawValue:
- return textInputCellWith(text: account.website, tag: kWebsiteTextFieldTag, interactionEnabled: editableFields.contains(kUserProfileWebsite),
- keyBoardType: .URL, autocapitalizationType: nil, placeHolder: NSLocalizedString("Link https://…", comment: ""))
- case ProfileSection.kProfileSectionTwitter.rawValue:
- return textInputCellWith(text: account.twitter, tag: kTwitterTextFieldTag, interactionEnabled: editableFields.contains(kUserProfileTwitter),
- keyBoardType: .emailAddress, autocapitalizationType: nil, placeHolder: NSLocalizedString("Twitter handle @…", comment: ""))
- case ProfileSection.kProfileSectionSummary.rawValue:
- return summaryCellForRow(row: indexPath.row)
- case ProfileSection.kProfileSectionRemoveAccount.rawValue:
- let actionTitle = multiAccountEnabled.boolValue ? NSLocalizedString("Remove account", comment: "") : NSLocalizedString("Log out", comment: "")
- let actionImage = multiAccountEnabled.boolValue ?
- UIImage(systemName: "trash")?.applyingSymbolConfiguration(iconConfiguration) :
- UIImage(systemName: "arrow.right.square")?.applyingSymbolConfiguration(iconConfiguration)
- return actionCellWith(identifier: "RemoveAccountCellIdentifier", text: actionTitle, textColor: .systemRed, image: actionImage, tintColor: .systemRed)
- default:
- break
- }
- return UITableViewCell()
- }
- override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
- let sections = getProfileSections()
- let section = sections[indexPath.section]
- if section == ProfileSection.kProfileSectionRemoveAccount.rawValue {
- self.showLogoutConfirmationDialog()
- } else if section == ProfileSection.kProfileSectionPhoneNumber.rawValue {
- self.presentSetPhoneNumberDialog()
- }
- self.tableView.deselectRow(at: indexPath, animated: true)
- }
- }
- extension UserProfileTableViewController {
- // MARK: Header View Setup
- func setupViewForSection(headerView: inout HeaderWithButton, title: String, buttonTag: Int, enabled: Bool?, scopeForImage: String) {
- headerView.label.text = title.uppercased()
- headerView.button.tag = buttonTag
- if let enabled = enabled {
- headerView.button.isEnabled = enabled
- }
- headerView.button.setImage(self.imageForScope(scope: scopeForImage)?.applyingSymbolConfiguration(iconHeaderConfiguration), for: .normal)
- }
- func setupViewforHeaderInSection(profileSection: Int) -> HeaderWithButton {
- var headerView = HeaderWithButton()
- headerView.button.addTarget(self, action: #selector(showScopeSelectionDialog(_:)), for: .touchUpInside)
- var shouldEnableNameAndEmailScopeButton = false
- if let serverCapabilities = NCDatabaseManager.sharedInstance().serverCapabilities(forAccountId: account.accountId) {
- shouldEnableNameAndEmailScopeButton = serverCapabilities.accountPropertyScopesFederationEnabled ||
- serverCapabilities.accountPropertyScopesFederatedEnabled || serverCapabilities.accountPropertyScopesPublishedEnabled
- }
- switch profileSection {
- case ProfileSection.kProfileSectionName.rawValue:
- setupViewForSection(headerView: &headerView, title: NSLocalizedString("Full name", comment: ""), buttonTag: kNameTextFieldTag,
- enabled: shouldEnableNameAndEmailScopeButton, scopeForImage: account.userDisplayNameScope)
- case ProfileSection.kProfileSectionEmail.rawValue:
- setupViewForSection(headerView: &headerView, title: NSLocalizedString("Email", comment: ""), buttonTag: kEmailTextFieldTag,
- enabled: shouldEnableNameAndEmailScopeButton, scopeForImage: account.emailScope)
- case ProfileSection.kProfileSectionPhoneNumber.rawValue:
- setupViewForSection(headerView: &headerView, title: NSLocalizedString("Phone number", comment: ""), buttonTag: kPhoneTextFieldTag,
- enabled: nil, scopeForImage: account.phoneScope)
- case ProfileSection.kProfileSectionAddress.rawValue:
- setupViewForSection(headerView: &headerView, title: NSLocalizedString("Address", comment: ""), buttonTag: kAddressTextFieldTag,
- enabled: nil, scopeForImage: account.addressScope)
- case ProfileSection.kProfileSectionWebsite.rawValue:
- setupViewForSection(headerView: &headerView, title: NSLocalizedString("Website", comment: ""), buttonTag: kWebsiteTextFieldTag,
- enabled: nil, scopeForImage: account.websiteScope)
- case ProfileSection.kProfileSectionTwitter.rawValue:
- setupViewForSection(headerView: &headerView, title: NSLocalizedString("Twitter", comment: ""), buttonTag: kTwitterTextFieldTag,
- enabled: nil, scopeForImage: account.twitterScope)
- default:
- break
- }
- return headerView
- }
- // MARK: Setup cells
- func textInputCellWith(text: String?, tag: Int?, interactionEnabled: Bool?, keyBoardType: UIKeyboardType?, autocapitalizationType: UITextAutocapitalizationType?, placeHolder: String?) -> TextInputTableViewCell {
- let textInputCell = tableView.dequeueReusableCell(withIdentifier: kTextInputCellIdentifier) as? TextInputTableViewCell ??
- TextInputTableViewCell(style: .default, reuseIdentifier: kTextInputCellIdentifier)
- textInputCell.textField.delegate = self
- if let text = text {
- textInputCell.textField.text = text
- }
- if let tag = tag {
- textInputCell.textField.tag = tag
- }
- if let interactionEnabled = interactionEnabled {
- textInputCell.textField.isUserInteractionEnabled = interactionEnabled
- }
- if let keyBoardType = keyBoardType {
- textInputCell.textField.keyboardType = keyBoardType
- }
- if let autocapitalizationType = autocapitalizationType {
- textInputCell.textField.autocapitalizationType = autocapitalizationType
- }
- if let placeHolder = placeHolder {
- textInputCell.textField.placeholder = placeHolder
- }
- return textInputCell
- }
- func summaryCellForRow(row: Int) -> UITableViewCell {
- let summaryCell = tableView.dequeueReusableCell(withIdentifier: "SummaryCellIdentifier") ?? UITableViewCell(style: .default, reuseIdentifier: "SummaryCellIdentifier")
- let summaryRow = self.rowsInSummarySection()[row]
- switch summaryRow {
- case SummaryRow.kSummaryRowEmail.rawValue:
- summaryCell.textLabel?.text = account.email
- summaryCell.imageView?.image = UIImage(systemName: "envelope")?.applyingSymbolConfiguration(iconConfiguration)
- case SummaryRow.kSummaryRowPhoneNumber.rawValue:
- let phoneNumber = try? phoneUtil.parse(account.phone, defaultRegion: nil)
- let text = (phoneNumber != nil) ? try? phoneUtil.format(phoneNumber, numberFormat: NBEPhoneNumberFormat.INTERNATIONAL) : nil
- summaryCell.textLabel?.text = text
- summaryCell.imageView?.image = UIImage(systemName: "iphone")?.applyingSymbolConfiguration(iconConfiguration)
- case SummaryRow.kSummaryRowAddress.rawValue:
- summaryCell.textLabel?.text = account.address
- summaryCell.imageView?.image = UIImage(systemName: "mappin")?.applyingSymbolConfiguration(iconConfiguration)
- case SummaryRow.kSummaryRowWebsite.rawValue:
- summaryCell.textLabel?.text = account.website
- summaryCell.imageView?.image = UIImage(systemName: "network")?.applyingSymbolConfiguration(iconConfiguration)
- case SummaryRow.kSummaryRowTwitter.rawValue:
- summaryCell.textLabel?.text = account.twitter
- summaryCell.imageView?.image = UIImage(named: "twitter")?.withRenderingMode(.alwaysTemplate)
- default:
- break
- }
- summaryCell.imageView?.tintColor = .secondaryLabel
- return summaryCell
- }
- func actionCellWith(identifier: String, text: String, textColor: UIColor, image: UIImage?, tintColor: UIColor) -> UITableViewCell {
- let actionCell = tableView.dequeueReusableCell(withIdentifier: identifier) ?? UITableViewCell(style: .default, reuseIdentifier: identifier)
- actionCell.textLabel?.text = text
- actionCell.textLabel?.textColor = textColor
- actionCell.imageView?.image = image?.withRenderingMode(.alwaysTemplate)
- actionCell.imageView?.tintColor = tintColor
- return actionCell
- }
- }
|