Move user profile table view controller to Swift.

Signed-off-by: Aleksandra Lazarevic <aleksandra@nextcloud.com>
This commit is contained in:
Aleksandra Lazarevic 2022-01-27 15:36:55 +01:00 коммит произвёл Ivan Sein
Родитель 9901cdcae4
Коммит b02cda7e44
14 изменённых файлов: 1128 добавлений и 1308 удалений

Просмотреть файл

@ -256,17 +256,21 @@
2CEDA88926F10BB20044552B /* UserStatusMessageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEDA88726F10BB20044552B /* UserStatusMessageViewController.swift */; };
2CEDA88A26F10BB20044552B /* UserStatusMessageViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2CEDA88826F10BB20044552B /* UserStatusMessageViewController.xib */; };
2CEDA88C26F492610044552B /* NSMutableAttributedString+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEDA88B26F492610044552B /* NSMutableAttributedString+Extensions.swift */; };
2CF9CBDB26010835002246EF /* UserProfileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CF9CBDA26010835002246EF /* UserProfileViewController.m */; };
2CF9CBFC26025F64002246EF /* TextInputTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CF9CBFA26025F64002246EF /* TextInputTableViewCell.m */; };
2CF9CBFF26025F65002246EF /* TextInputTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2CF9CBFB26025F64002246EF /* TextInputTableViewCell.xib */; };
3FCA62550CD1442D28E8A7C6 /* libPods-NotificationServiceExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B81BB7A4920C391CC2CACFD /* libPods-NotificationServiceExtension.a */; };
8789AE73BFCAA413B43319C0 /* libPods-ShareExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 684807120F4439797973DF73 /* libPods-ShareExtension.a */; };
9993261EDAC77481FF4EF58A /* libPods-NextcloudTalk.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F7C31E9D74F550EAF89931B /* libPods-NextcloudTalk.a */; };
DA1AEFC3270F1FA90088E519 /* DateLabelCustom.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1AEFC2270F1FA90088E519 /* DateLabelCustom.swift */; };
DA66582B27B6992F00B46B11 /* UserProfileTableViewController+AvatarSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA66582A27B6992F00B46B11 /* UserProfileTableViewController+AvatarSetup.swift */; };
DA66582D27B6A73800B46B11 /* UserProfileTableViewController+DelegateMethods.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA66582C27B6A73800B46B11 /* UserProfileTableViewController+DelegateMethods.swift */; };
DA66582F27B6B19C00B46B11 /* UserProfileTableViewController+ActionsSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA66582E27B6B19C00B46B11 /* UserProfileTableViewController+ActionsSetup.swift */; };
DA66583127B6B24E00B46B11 /* UserProfileTableViewController+UISetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA66583027B6B24E00B46B11 /* UserProfileTableViewController+UISetup.swift */; };
DA75580F278EEA1000A48A1B /* SettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA75580E278EEA1000A48A1B /* SettingsTableViewController.swift */; };
DA755811278EF3EF00A48A1B /* UserSettingsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA755810278EF3EF00A48A1B /* UserSettingsTableViewCell.swift */; };
DA7558132790D65700A48A1B /* AccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA7558122790D65700A48A1B /* AccountTableViewCell.swift */; };
DA7558C6279AE67F00A48A1B /* UserStatusTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA7558C5279AE67F00A48A1B /* UserStatusTableViewController.swift */; };
DA8801A227A2DA00009EF248 /* UserProfileTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA8801A127A2DA00009EF248 /* UserProfileTableViewController.swift */; };
DA8801A427AC52AC009EF248 /* TextInputTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA8801A327AC52AC009EF248 /* TextInputTableViewCell.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -721,10 +725,6 @@
2CEDA88726F10BB20044552B /* UserStatusMessageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserStatusMessageViewController.swift; sourceTree = "<group>"; };
2CEDA88826F10BB20044552B /* UserStatusMessageViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = UserStatusMessageViewController.xib; sourceTree = "<group>"; };
2CEDA88B26F492610044552B /* NSMutableAttributedString+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSMutableAttributedString+Extensions.swift"; sourceTree = "<group>"; };
2CF9CBD926010835002246EF /* UserProfileViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserProfileViewController.h; sourceTree = "<group>"; };
2CF9CBDA26010835002246EF /* UserProfileViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserProfileViewController.m; sourceTree = "<group>"; };
2CF9CBF926025F64002246EF /* TextInputTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TextInputTableViewCell.h; sourceTree = "<group>"; };
2CF9CBFA26025F64002246EF /* TextInputTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TextInputTableViewCell.m; sourceTree = "<group>"; };
2CF9CBFB26025F64002246EF /* TextInputTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TextInputTableViewCell.xib; sourceTree = "<group>"; };
4202C63030F0FFBB1C16D75E /* Pods-NextcloudTalk.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NextcloudTalk.debug.xcconfig"; path = "Pods/Target Support Files/Pods-NextcloudTalk/Pods-NextcloudTalk.debug.xcconfig"; sourceTree = "<group>"; };
4D4C7BF2F97F47B0D9094618 /* Pods-NextcloudTalk.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NextcloudTalk.release.xcconfig"; path = "Pods/Target Support Files/Pods-NextcloudTalk/Pods-NextcloudTalk.release.xcconfig"; sourceTree = "<group>"; };
@ -736,10 +736,16 @@
D6DF51D976DC0F681FF83F7B /* Pods-NotificationServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.debug.xcconfig"; sourceTree = "<group>"; };
D86091EC1125C3057B9A299B /* Pods-NotificationServiceExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.release.xcconfig"; sourceTree = "<group>"; };
DA1AEFC2270F1FA90088E519 /* DateLabelCustom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateLabelCustom.swift; sourceTree = "<group>"; };
DA66582A27B6992F00B46B11 /* UserProfileTableViewController+AvatarSetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserProfileTableViewController+AvatarSetup.swift"; sourceTree = "<group>"; };
DA66582C27B6A73800B46B11 /* UserProfileTableViewController+DelegateMethods.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserProfileTableViewController+DelegateMethods.swift"; sourceTree = "<group>"; };
DA66582E27B6B19C00B46B11 /* UserProfileTableViewController+ActionsSetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserProfileTableViewController+ActionsSetup.swift"; sourceTree = "<group>"; };
DA66583027B6B24E00B46B11 /* UserProfileTableViewController+UISetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserProfileTableViewController+UISetup.swift"; sourceTree = "<group>"; };
DA75580E278EEA1000A48A1B /* SettingsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTableViewController.swift; sourceTree = "<group>"; };
DA755810278EF3EF00A48A1B /* UserSettingsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsTableViewCell.swift; sourceTree = "<group>"; };
DA7558122790D65700A48A1B /* AccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountTableViewCell.swift; sourceTree = "<group>"; };
DA7558C5279AE67F00A48A1B /* UserStatusTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserStatusTableViewController.swift; sourceTree = "<group>"; };
DA8801A127A2DA00009EF248 /* UserProfileTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileTableViewController.swift; sourceTree = "<group>"; };
DA8801A327AC52AC009EF248 /* TextInputTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextInputTableViewCell.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -1067,10 +1073,12 @@
2C78E9E125120DE500E3D4CA /* NCUserStatus.h */,
2C78E9E225120DE500E3D4CA /* NCUserStatus.m */,
DA7558C5279AE67F00A48A1B /* UserStatusTableViewController.swift */,
2CF9CBD926010835002246EF /* UserProfileViewController.h */,
2CF9CBDA26010835002246EF /* UserProfileViewController.m */,
2CF9CBF926025F64002246EF /* TextInputTableViewCell.h */,
2CF9CBFA26025F64002246EF /* TextInputTableViewCell.m */,
DA8801A127A2DA00009EF248 /* UserProfileTableViewController.swift */,
DA66582A27B6992F00B46B11 /* UserProfileTableViewController+AvatarSetup.swift */,
DA66582C27B6A73800B46B11 /* UserProfileTableViewController+DelegateMethods.swift */,
DA66582E27B6B19C00B46B11 /* UserProfileTableViewController+ActionsSetup.swift */,
DA66583027B6B24E00B46B11 /* UserProfileTableViewController+UISetup.swift */,
DA8801A327AC52AC009EF248 /* TextInputTableViewCell.swift */,
2CF9CBFB26025F64002246EF /* TextInputTableViewCell.xib */,
2C4446EA265D25BA00DF1DBC /* NCKeyChainController.h */,
2C4446EB265D25BA00DF1DBC /* NCKeyChainController.m */,
@ -1833,6 +1841,7 @@
2CBF82AE1FC888FC00636459 /* NCPushNotification.m in Sources */,
2CC7159420C54D080045C789 /* ChatTableViewCell.m in Sources */,
2CA1CCAA1F02D1A4002FE6A2 /* NCAPIController.m in Sources */,
DA66582B27B6992F00B46B11 /* UserProfileTableViewController+AvatarSetup.swift in Sources */,
2C36A038261342220026F04A /* AvatarHeaderView.m in Sources */,
2C3780C3210F49DC003F9AE8 /* HeaderWithButton.m in Sources */,
2C1ABDC6257A7CF000AEDFB6 /* NCContactsManager.m in Sources */,
@ -1846,6 +1855,7 @@
2C9B0B9C217F756B00A4752C /* NCNotification.m in Sources */,
2C4CDCCC269618240023F403 /* RoomDescriptionTableViewCell.m in Sources */,
2CC007BD20D8F24B0096D91F /* RoomCreation2TableViewController.m in Sources */,
DA66583127B6B24E00B46B11 /* UserProfileTableViewController+UISetup.swift in Sources */,
2CBF82BD1FD5AE0A00636459 /* NCImageSessionManager.m in Sources */,
2C1ABD9925769F7500AEDFB6 /* ShareItem.m in Sources */,
2C2E64251F3462AF00D39CE8 /* NCSignalingMessage.m in Sources */,
@ -1863,7 +1873,6 @@
2C4E758F214B942D003910D5 /* OpenInFirefoxControllerObjC.m in Sources */,
2CA1CCA41F025F64002FE6A2 /* RoomsTableViewController.m in Sources */,
2CB3041A2264775E0053078A /* SLKTextInput+Implementation.m in Sources */,
2CF9CBFC26025F64002246EF /* TextInputTableViewCell.m in Sources */,
2C90E5D31EE80C870093D85A /* AuthenticationViewController.m in Sources */,
2C604BD9211988A700D34DCD /* SystemMessageTableViewCell.m in Sources */,
2CA1CCD01F1E1779002FE6A2 /* SearchTableViewController.m in Sources */,
@ -1890,6 +1899,7 @@
2CA155562099E07700CE8EF0 /* GroupedChatMessageTableViewCell.m in Sources */,
2CA15548208EA1EA00CE8EF0 /* ChatMessageTableViewCell.m in Sources */,
2C4446D8265814D100DF1DBC /* ServerCapabilities.m in Sources */,
DA66582D27B6A73800B46B11 /* UserProfileTableViewController+DelegateMethods.swift in Sources */,
1F5CDF642584E78900B0026E /* NCChatFileStatus.m in Sources */,
2CBF82C11FD5AE3F00636459 /* NCPushProxySessionManager.m in Sources */,
2C78EF951F7E70EB008AFA74 /* NCPeerConnection.m in Sources */,
@ -1900,7 +1910,7 @@
2C78EFA01F828C41008AFA74 /* CallViewController.m in Sources */,
DA1AEFC3270F1FA90088E519 /* DateLabelCustom.swift in Sources */,
2C6E74462386D33200AE396C /* ReplyMessageView.m in Sources */,
2CF9CBDB26010835002246EF /* UserProfileViewController.m in Sources */,
DA8801A227A2DA00009EF248 /* UserProfileTableViewController.swift in Sources */,
DA75580F278EEA1000A48A1B /* SettingsTableViewController.swift in Sources */,
2CB6ACD22640814100D3D641 /* LocationMessageTableViewCell.m in Sources */,
2CB3041D2264775E0053078A /* SLKTextView.m in Sources */,
@ -1909,6 +1919,7 @@
2C7A245B24FE7B5300921A21 /* ShareConfirmationViewController.m in Sources */,
2C4446D32658147900DF1DBC /* TalkAccount.m in Sources */,
2CA1CCD61F1E664C002FE6A2 /* ContactsTableViewCell.m in Sources */,
DA66582F27B6B19C00B46B11 /* UserProfileTableViewController+ActionsSetup.swift in Sources */,
2C6E7449238C1A0800AE396C /* QuotedMessageView.m in Sources */,
2C1ABDCE257E939600AEDFB6 /* NCContact.m in Sources */,
2C7A12422017872600864818 /* AddParticipantsTableViewController.m in Sources */,
@ -1918,6 +1929,7 @@
2C42ADB420B58E6300296DEA /* NCChatController.m in Sources */,
2C4CDCD026A84AEA0023F403 /* ShareViewController.m in Sources */,
2C4446EC265D25BA00DF1DBC /* NCKeyChainController.m in Sources */,
DA8801A427AC52AC009EF248 /* TextInputTableViewCell.swift in Sources */,
2C2A788E2359CC8800EEB797 /* NCAppBranding.m in Sources */,
2CA15541208E350300CE8EF0 /* NCChatMessage.m in Sources */,
2CB304192264775E0053078A /* SLKInputAccessoryView.m in Sources */,

Просмотреть файл

@ -23,7 +23,9 @@
#ifndef NextcloudTalk_Bridging_Header_h
#define NextcloudTalk_Bridging_Header_h
#import "AvatarHeaderView.h"
#import "DetailedOptionsSelectorTableViewController.h"
#import "HeaderWithButton.h"
#import "NBPhoneNumber.h"
#import "NBPhoneNumberDefines.h"
#import "NBPhoneNumberUtil.h"
@ -35,10 +37,15 @@
#import "NCNavigationController.h"
#import "NCSettingsController.h"
#import "NCUserDefaults.h"
#import "NCUserInterfaceController.h"
#import "NCUserStatus.h"
#import "NotificationCenterNotifications.h"
#import "RoundedNumberView.h"
#import "UIView+Toast.h"
#import "UserProfileViewController.h"
#import "TOCroppedImageAttributes.h"
#import "TOCropViewController.h"
#import "TOCropViewControllerTransitioning.h"
#import "TOActivityCroppedImageProvider.h"
#import "UIImage+CropRotate.h"
#endif /* NextcloudTalk_Bridging_Header_h */

Просмотреть файл

@ -217,7 +217,7 @@ class SettingsTableViewController: UITableViewController, UITextFieldDelegate {
func userProfilePressed() {
let activeAccount = NCDatabaseManager.sharedInstance().activeAccount()
let userProfileVC = UserProfileViewController(account: activeAccount)
let userProfileVC = UserProfileTableViewController(withAccount: activeAccount)
self.navigationController?.pushViewController(userProfileVC, animated: true)
}

Просмотреть файл

@ -1,36 +0,0 @@
/**
* @copyright Copyright (c) 2021 Ivan Sein <ivan@nextcloud.com>
*
* @author Ivan Sein <ivan@nextcloud.com>
*
* @license GNU GPL version 3 or any later version
*
* 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 <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
extern NSString *const kTextInputCellIdentifier;
extern NSString *const kTextInputTableViewCellNibName;
@interface TextInputTableViewCell : UITableViewCell
@property (weak, nonatomic) IBOutlet UITextField *textField;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,53 +0,0 @@
/**
* @copyright Copyright (c) 2021 Ivan Sein <ivan@nextcloud.com>
*
* @author Ivan Sein <ivan@nextcloud.com>
*
* @license GNU GPL version 3 or any later version
*
* 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 "TextInputTableViewCell.h"
NSString *const kTextInputCellIdentifier = @"TextInputCellIdentifier";
NSString *const kTextInputTableViewCellNibName = @"TextInputTableViewCell";
@implementation TextInputTableViewCell
- (void)awakeFromNib
{
[super awakeFromNib];
self.textField.clearButtonMode = UITextFieldViewModeWhileEditing;
self.textField.returnKeyType = UIReturnKeyDone;
}
- (void)prepareForReuse
{
[super prepareForReuse];
self.textField.text = @"";
self.textField.keyboardType = UIKeyboardTypeDefault;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
@end

Просмотреть файл

@ -0,0 +1,44 @@
/**
* @copyright Copyright (c) 2022 Aleksandra Lazarevic <aleksandra@nextcloud.com>
*
* @author Aleksandra Lazarevic <aleksandra@nextcloud.com>
*
* @license GNU GPL version 3 or any later version
*
* 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 UIKit
let kTextInputCellIdentifier = "TextInputCellIdentifier"
let kTextInputTableViewCellNibName = "TextInputTableViewCell"
class TextInputTableViewCell: UITableViewCell {
@IBOutlet weak var textField: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.textField.clearButtonMode = .whileEditing
self.textField.returnKeyType = .done
}
override func prepareForReuse() {
super.prepareForReuse()
self.textField.text = ""
self.textField.keyboardType = .default
}
}

Просмотреть файл

@ -1,16 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="TextInputTableViewCell">
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="TextInputTableViewCell" customModule="NextcloudTalk" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
@ -27,7 +27,7 @@
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="aW0-zy-SZf"/>
<connections>
<outlet property="textField" destination="lFm-yU-OyT" id="ztI-zh-GUh"/>
<outlet property="textField" destination="lFm-yU-OyT" id="2WF-SE-wN7"/>
</connections>
<point key="canvasLocation" x="137.68115942028987" y="72.991071428571431"/>
</tableViewCell>

Просмотреть файл

@ -0,0 +1,124 @@
/**
* @copyright Copyright (c) 2022 Aleksandra Lazarevic <aleksandra@nextcloud.com>
*
* @author Aleksandra Lazarevic <aleksandra@nextcloud.com>
*
* @license GNU GPL version 3 or any later version
*
* 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 UserProfileTableViewController {
// MARK: Actions
@objc func editButtonPressed() {
if activeTextField != nil {
self.waitingForModification = true
return
}
if !isEditable {
isEditable = true
self.showDoneButton()
} else {
isEditable = false
self.showEditButton()
}
self.refreshProfileTableView()
}
func addNewAccount() {
self.dismiss(animated: true) {
NCUserInterfaceController.sharedInstance().presentLoginViewController()
}
}
func showLogoutConfirmationDialog() {
let alertTitle = multiAccountEnabled.boolValue ? NSLocalizedString("Remove account", comment: "") : NSLocalizedString("Log out", comment: "")
let alertMessageFirstOption = NSLocalizedString("Do you really want to remove this account?", comment: "")
let alertMessageSecondOption = NSLocalizedString("Do you really want to log out from this account?", comment: "")
let alertMessage = multiAccountEnabled.boolValue ? alertMessageFirstOption : alertMessageSecondOption
let actionTitle = multiAccountEnabled.boolValue ? NSLocalizedString("Remove", comment: "") : NSLocalizedString("Log out", comment: "")
let confirmDialog = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)
let confirmAction = UIAlertAction(title: actionTitle, style: .destructive) { _ in
self.logout()
}
confirmDialog.addAction(confirmAction)
let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil)
confirmDialog.addAction(cancelAction)
self.present(confirmDialog, animated: true, completion: nil)
}
func logout() {
let activeAccount = NCDatabaseManager.sharedInstance().activeAccount()
NCSettingsController.sharedInstance().logoutAccount(withAccountId: activeAccount.accountId) { _ in
NCUserInterfaceController.sharedInstance().presentConversationsList()
NCConnectionController.sharedInstance().checkAppState()
}
}
func presentSetPhoneNumberDialog() {
let setPhoneNumberDialog = UIAlertController(title: NSLocalizedString("Phone number", comment: ""), message: nil, preferredStyle: .alert)
let hasPhone = !account.phone.isEmpty
setPhoneNumberDialog.addTextField { [self] textField in
let location = NSLocale.current.regionCode
let countryCode = phoneUtil?.getCountryCode(forRegion: location)
if let countryCode = countryCode {
textField.text = "+\(countryCode)"
}
if hasPhone {
textField.text = self.account.phone
}
let exampleNumber = try? self.phoneUtil?.getExampleNumber(location ?? "")
if let exampleNumber = exampleNumber {
textField.placeholder = try? self.phoneUtil?.format(exampleNumber, numberFormat: NBEPhoneNumberFormat.INTERNATIONAL)
textField.keyboardType = .phonePad
textField.delegate = self
textField.tag = self.kPhoneTextFieldTag
}
}
setPhoneAction = UIAlertAction(title: NSLocalizedString("Set", comment: ""), style: .default, handler: { _ in
let phoneNumber = setPhoneNumberDialog.textFields?[0].text
if let phoneNumber = phoneNumber {
self.setPhoneNumber(phoneNumber)
}
})
setPhoneAction.isEnabled = false
setPhoneNumberDialog.addAction(setPhoneAction)
if hasPhone {
let removeAction = UIAlertAction(title: NSLocalizedString("Remove", comment: ""), style: .destructive) { _ in
self.setPhoneNumber("")
}
setPhoneNumberDialog.addAction(removeAction)
}
let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil)
setPhoneNumberDialog.addAction(cancelAction)
self.present(setPhoneNumberDialog, animated: true, completion: nil)
}
func setPhoneNumber(_ phoneNumber: String) {
self.setModifyingProfileUI()
NCAPIController.sharedInstance().setUserProfileField(kUserProfilePhone, withValue: phoneNumber, for: account) { error, _ in
if error != nil {
self.showProfileModificationErrorForField(inTextField: self.kPhoneTextFieldTag, textField: nil)
} else {
self.refreshUserProfile()
}
self.removeModifyingProfileUI()
}
}
}

Просмотреть файл

@ -0,0 +1,149 @@
/**
* @copyright Copyright (c) 2022 Aleksandra Lazarevic <aleksandra@nextcloud.com>
*
* @author Aleksandra Lazarevic <aleksandra@nextcloud.com>
*
* @license GNU GPL version 3 or any later version
*
* 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 UserProfileTableViewController {
func avatarHeaderView() -> UIView? {
let headerView = AvatarHeaderView()
headerView.frame = CGRect(x: 0, y: 0, width: 200, height: 150)
headerView.avatarImageView?.layer.cornerRadius = 40.0
headerView.avatarImageView?.layer.masksToBounds = true
headerView.avatarImageView?.image = NCAPIController.sharedInstance().userProfileImage(for: account, with: CGSize(width: 160, height: 160))
headerView.nameLabel?.text = account.userDisplayName
headerView.nameLabel?.isHidden = self.isEditable
headerView.scopeButton?.tag = kAvatarScopeButtonTag
headerView.scopeButton?.setImage(self.imageForScope(scope: account.avatarScope), for: .normal)
headerView.scopeButton?.addTarget(self, action: #selector(showScopeSelectionDialog(_:)), for: .touchUpInside)
headerView.scopeButton?.isHidden = !(isEditable && (showScopes != nil))
headerView.editButton?.isHidden = !(isEditable && NCDatabaseManager.sharedInstance().serverHasTalkCapability(kCapabilityTempUserAvatarAPI, forAccountId: account.accountId))
headerView.editButton?.setTitle(NSLocalizedString("Edit", comment: ""), for: .normal)
headerView.editButton?.addTarget(self, action: #selector(showAvatarOptions), for: .touchUpInside)
if let editButton = headerView.editButton {
editAvatarButton = editButton
}
return headerView
}
@objc func showAvatarOptions() {
let optionsActionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let cameraAction = UIAlertAction(title: NSLocalizedString("Camera", comment: ""), style: .default) { _ in
self.checkAndPresentCamera()
}
cameraAction.setValue(UIImage(named: "camera")?.withRenderingMode(.alwaysTemplate), forKey: "image")
let photoLibraryAction = UIAlertAction(title: NSLocalizedString("Photo Library", comment: ""), style: .default) { _ in
self.presentPhotoLibrary()
}
photoLibraryAction.setValue(UIImage(named: "photos")?.withRenderingMode(.alwaysTemplate), forKey: "image")
let removeAction = UIAlertAction(title: NSLocalizedString("Remove", comment: ""), style: .destructive) { _ in
self.removeUserProfileImage()
}
removeAction.setValue(UIImage(named: "delete")?.withRenderingMode(.alwaysTemplate), forKey: "image")
if UIImagePickerController.isSourceTypeAvailable(.camera) {
optionsActionSheet.addAction(cameraAction)
}
optionsActionSheet.addAction(photoLibraryAction)
if account.hasCustomAvatar {
optionsActionSheet.addAction(removeAction)
}
optionsActionSheet.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil))
// Presentation on iPads
optionsActionSheet.popoverPresentationController?.sourceView = editAvatarButton
optionsActionSheet.popoverPresentationController?.sourceRect = editAvatarButton.frame
self.present(optionsActionSheet, animated: true, completion: nil)
}
func checkAndPresentCamera() {
// https://stackoverflow.com/a/20464727/2512312
let mediaType: String = AVMediaType.video.rawValue
let authStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType(mediaType))
if authStatus == AVAuthorizationStatus.authorized {
self.presentCamera()
return
} else if authStatus == AVAuthorizationStatus.notDetermined {
AVCaptureDevice.requestAccess(for: AVMediaType(mediaType)) { granted in
if granted {
self.presentCamera()
}
}
return
}
let alertTitle = NSLocalizedString("Could not access camera", comment: "")
let alertMessage = NSLocalizedString("Camera access is not allowed. Check your settings.", comment: "")
let alert = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)
let okButton = UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)
alert.addAction(okButton)
NCUserInterfaceController.sharedInstance().presentAlertViewController(alert)
}
func presentCamera() {
DispatchQueue.main.async {
self.imagePicker = UIImagePickerController()
self.imagePicker?.sourceType = UIImagePickerController.SourceType.camera
if self.imagePicker != nil {
self.imagePicker!.delegate = self
self.present(self.imagePicker!, animated: true)
}
}
}
func presentPhotoLibrary() {
DispatchQueue.main.async {
self.imagePicker = UIImagePickerController()
self.imagePicker?.sourceType = .photoLibrary
if self.imagePicker != nil {
self.imagePicker!.delegate = self
self.present(self.imagePicker!, animated: true)
}
}
}
func sendUserProfileImage(image: UIImage) {
NCAPIController.sharedInstance().setUserProfileImage(image, for: account) { error, _ in
if error == nil {
self.refreshUserProfile()
} else {
self.showProfileImageError(NSLocalizedString("An error occurred setting profile image", comment: ""))
print("Error removing profile image: \(error.debugDescription)")
}
}
}
func removeUserProfileImage() {
NCAPIController.sharedInstance().removeUserProfileImage(for: account) { error, _ in
if error == nil {
self.refreshUserProfile()
} else {
self.showProfileImageError(NSLocalizedString("An error occurred removing profile image", comment: ""))
print("Error removing profile image: ", error.debugDescription)
}
}
}
func showProfileImageError(_ reason: String) {
let errorDialog = UIAlertController(title: reason, message: nil, preferredStyle: .alert)
let okAction = UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)
errorDialog.addAction(okAction)
self.present(errorDialog, animated: true, completion: nil)
}
}

Просмотреть файл

@ -0,0 +1,144 @@
/**
* @copyright Copyright (c) 2022 Aleksandra Lazarevic <aleksandra@nextcloud.com>
*
* @author Aleksandra Lazarevic <aleksandra@nextcloud.com>
*
* @license GNU GPL version 3 or any later version
*
* 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 UserProfileTableViewController: UINavigationControllerDelegate, UITextFieldDelegate, UIGestureRecognizerDelegate, UIImagePickerControllerDelegate {
// MARK: DetailedOptionSelector Delegate
func detailedOptionsSelector(_ viewController: DetailedOptionsSelectorTableViewController!, didSelectOptionWithIdentifier option: DetailedOption!) {
self.dismiss(animated: true) {
if !option.selected {
self.setUserProfileField(viewController.senderId, scopeValue: option.identifier)
}
}
}
func detailedOptionsSelectorWasCancelled(_ viewController: DetailedOptionsSelectorTableViewController!) {
self.dismiss(animated: true, completion: nil)
}
// MARK: UIImagePickerController Delegate
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
let mediaType = info[UIImagePickerController.InfoKey.mediaType] as? String
if mediaType == "public.image" {
let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
self.dismiss(animated: true) {
if let image = image {
let cropViewController = TOCropViewController(croppingStyle: TOCropViewCroppingStyle.circular, image: image)
cropViewController.delegate = self
self.present(cropViewController, animated: true)
}
}
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.dismiss(animated: true, completion: nil)
}
// MARK: TOCROPViewControllerDelegate
func cropViewController(_ cropViewController: TOCropViewController, didCropTo image: UIImage, with cropRect: CGRect, angle: Int) {
self.sendUserProfileImage(image: image)
// Fixes weird iOS 13 bug: https://github.com/TimOliver/TOCropViewController/issues/365
cropViewController.transitioningDelegate = nil
cropViewController.dismiss(animated: true)
}
func cropViewController(_ cropViewController: TOCropViewController, didFinishCancelled cancelled: Bool) {
// Fixes weird iOS 13 bug: https://github.com/TimOliver/TOCropViewController/issues/365
cropViewController.transitioningDelegate = nil
cropViewController.dismiss(animated: true)
}
// MARK: UITextField delegate
func textFieldDidBeginEditing(_ textField: UITextField) {
activeTextField = textField
}
func textFieldDidEndEditing(_ textField: UITextField) {
var field: String?
var currentValue: String?
let newValue = textField.text!.trimmingCharacters(in: CharacterSet.whitespaces)
let tag = textField.tag
let waitForModification = self.waitingForModification
self.waitingForModification = false
activeTextField = nil
if tag == kNameTextFieldTag {
field = kUserProfileDisplayName
currentValue = account.userDisplayName
} else if tag == kEmailTextFieldTag {
field = kUserProfileEmail
currentValue = account.email
} else if tag == kPhoneTextFieldTag {
return
} else if tag == kAddressTextFieldTag {
field = kUserProfileAddress
currentValue = account.address
} else if tag == kWebsiteTextFieldTag {
field = kUserProfileWebsite
currentValue = account.website
} else if tag == kTwitterTextFieldTag {
field = kUserProfileTwitter
currentValue = account.twitter
}
textField.text = newValue
self.setModifyingProfileUI()
if newValue != currentValue {
NCAPIController.sharedInstance().setUserProfileField(field, withValue: newValue, for: account) { error, _ in
if error != nil {
self.showProfileModificationErrorForField(inTextField: tag, textField: textField)
} else {
if waitForModification {
self.editButtonPressed()
}
self.refreshUserProfile()
}
self.removeModifyingProfileUI()
}
} else {
if waitForModification {
self.editButtonPressed()
}
self.removeModifyingProfileUI()
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField.tag == kPhoneTextFieldTag {
let inputPhoneNumber = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
var phoneNumber: NBPhoneNumber?
phoneNumber = try? phoneUtil?.parse(inputPhoneNumber, defaultRegion: nil)
setPhoneAction.isEnabled = (phoneUtil?.isValidNumber(phoneNumber))! && (account.phone != inputPhoneNumber)
}
return true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}

Просмотреть файл

@ -0,0 +1,260 @@
/**
* @copyright Copyright (c) 2022 Aleksandra Lazarevic <aleksandra@nextcloud.com>
*
* @author Aleksandra Lazarevic <aleksandra@nextcloud.com>
*
* @license GNU GPL version 3 or any later version
*
* 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 UserProfileTableViewController {
// MARK: User Interface
func showEditButton() {
self.editButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.edit, target: self, action: #selector(editButtonPressed))
self.editButton.accessibilityHint = NSLocalizedString("Double tap to edit profile", comment: "")
self.navigationItem.rightBarButtonItem = editButton
}
func showDoneButton() {
self.editButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.done, target: self, action: #selector(editButtonPressed))
self.editButton.accessibilityHint = NSLocalizedString("Double tap to end editing profile", comment: "")
self.navigationItem.rightBarButtonItem = editButton
}
func setModifyingProfileUI() {
modifyingProfileView.startAnimating()
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: modifyingProfileView)
self.tableView.isUserInteractionEnabled = false
}
func removeModifyingProfileUI() {
modifyingProfileView.stopAnimating()
if isEditable {
self.showDoneButton()
} else {
self.showEditButton()
}
self.tableView.isUserInteractionEnabled = true
}
func refreshProfileTableView() {
tableView.tableHeaderView = self.avatarHeaderView()
tableView.tableHeaderView?.setNeedsDisplay()
tableView.reloadData()
}
func getUserProfileEditableFields() {
editButton.isEnabled = false
NCAPIController.sharedInstance().getUserProfileEditableFields(for: account) { userProfileEditableFields, error in
if error == nil {
if let userProfileEditableFields = userProfileEditableFields as NSArray? {
self.editableFields = userProfileEditableFields
self.editButton.isEnabled = true
}
}
}
}
func refreshUserProfile() {
NCSettingsController.sharedInstance().getUserProfile { _ in
self.account = NCDatabaseManager.sharedInstance().activeAccount()
self.refreshProfileTableView()
}
}
func showProfileModificationErrorForField(inTextField field: Int, textField: UITextField?) {
var errorDescription = ""
// The textfield pointer might be pointing to a different textfield at this point because
// if the user tapped the "Done" button in navigation bar (so the non-editable view is visible)
// That's the reason why we check the field instead of textfield.tag
switch field {
case kNameTextFieldTag:
errorDescription = NSLocalizedString("An error occurred setting user name", comment: "")
case kEmailTextFieldTag:
errorDescription = NSLocalizedString("An error occurred setting email address", comment: "")
case kPhoneTextFieldTag:
errorDescription = NSLocalizedString("An error occurred setting phone number", comment: "")
case kAddressTextFieldTag:
errorDescription = NSLocalizedString("An error occurred setting address", comment: "")
case kWebsiteTextFieldTag:
errorDescription = NSLocalizedString("An error occurred setting website", comment: "")
case kTwitterTextFieldTag:
errorDescription = NSLocalizedString("An error occurred setting Twitter account", comment: "")
default:
break
}
let errorDialog = UIAlertController(title: errorDescription, message: nil, preferredStyle: .alert)
let okAction = UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default) { _ in
if self.isEditable {
textField?.becomeFirstResponder()
}
}
errorDialog.addAction(okAction)
self.present(errorDialog, animated: true, completion: nil)
}
func imageForScope(scope: String) -> UIImage? {
if scope == kUserProfileScopePrivate {
return UIImage(named: "mobile-phone-20")?.withRenderingMode(.alwaysTemplate)
} else if scope == kUserProfileScopeLocal {
return UIImage(named: "password-20")?.withRenderingMode(.alwaysTemplate)
} else if scope == kUserProfileScopeFederated {
return UIImage(named: "group-20")?.withRenderingMode(.alwaysTemplate)
} else if scope == kUserProfileScopePublished {
return UIImage(named: "browser-20")?.withRenderingMode(.alwaysTemplate)
}
return nil
}
@objc func showScopeSelectionDialog( _ sender: UIButton?) {
var field: String?
var currentValue: String?
var title: String?
guard let sender = sender else {
return
}
switch sender.tag {
case kNameTextFieldTag:
setupFieldsForScopeSelectionDialog(field: &field, currentValue: &currentValue, title: &title,
fieldValue: kUserProfileDisplayNameScope, currentValueText: account.userDisplayNameScope, titleValue: "Full name")
case kEmailTextFieldTag:
setupFieldsForScopeSelectionDialog(field: &field, currentValue: &currentValue, title: &title,
fieldValue: kUserProfileEmailScope, currentValueText: account.emailScope, titleValue: "Email")
case kPhoneTextFieldTag:
setupFieldsForScopeSelectionDialog(field: &field, currentValue: &currentValue, title: &title,
fieldValue: kUserProfilePhoneScope, currentValueText: account.phoneScope, titleValue: "Phone number")
case kAddressTextFieldTag:
setupFieldsForScopeSelectionDialog(field: &field, currentValue: &currentValue, title: &title,
fieldValue: kUserProfileAddressScope, currentValueText: account.addressScope, titleValue: "Address")
case kWebsiteTextFieldTag:
setupFieldsForScopeSelectionDialog(field: &field, currentValue: &currentValue, title: &title,
fieldValue: kUserProfileWebsiteScope, currentValueText: account.websiteScope, titleValue: "Website")
case kTwitterTextFieldTag:
setupFieldsForScopeSelectionDialog(field: &field, currentValue: &currentValue, title: &title,
fieldValue: kUserProfileTwitterScope, currentValueText: account.twitterScope, titleValue: "Twitter")
case kAvatarScopeButtonTag:
setupFieldsForScopeSelectionDialog(field: &field, currentValue: &currentValue, title: &title,
fieldValue: kUserProfileAvatarScope, currentValueText: account.avatarScope, titleValue: "Profile picture")
default:
break
}
var options = [DetailedOption]()
let privateOption = setupDetailedOption(identifier: kUserProfileScopePrivate, imageName: "mobile-phone", title: NSLocalizedString("Private", comment: ""),
subtitle: NSLocalizedString("Only visible to people matched via phone number integration", comment: ""),
selected: currentValue == kUserProfileScopePrivate)
let localOption = setupDetailedOption(identifier: kUserProfileScopeLocal, imageName: "password-settings", title: NSLocalizedString("Local", comment: ""),
subtitle: NSLocalizedString("Only visible to people on this instance and guests", comment: ""), selected: currentValue == kUserProfileScopeLocal)
let federatedOption = setupDetailedOption(identifier: kUserProfileScopeFederated, imageName: "group", title: NSLocalizedString("Federated", comment: ""),
subtitle: NSLocalizedString("Only synchronize to trusted servers", comment: ""), selected: currentValue == kUserProfileScopeFederated)
let publishedOption = setupDetailedOption(identifier: kUserProfileScopePublished, imageName: "browser-settings", title: NSLocalizedString("Published", comment: ""),
subtitle: NSLocalizedString("Synchronize to trusted servers and the global and public address book", comment: ""),
selected: currentValue == kUserProfileScopePublished)
if field != kUserProfileDisplayNameScope && field != kUserProfileEmailScope {
options.append(privateOption)
}
options.append(localOption)
let serverCapabilities = NCDatabaseManager.sharedInstance().serverCapabilities(forAccountId: account.accountId)
if serverCapabilities.accountPropertyScopesFederationEnabled {
options.append(federatedOption)
options.append(publishedOption)
}
let optionSelectorVC = DetailedOptionsSelectorTableViewController(options: options, forSenderIdentifier: field, andTitle: title)
if let optionSelectorVC = optionSelectorVC {
optionSelectorVC.delegate = self
let optionSelectorNC = UINavigationController(rootViewController: optionSelectorVC)
self.present(optionSelectorNC, animated: true, completion: nil)
}
}
func setUserProfileField(_ field: String?, scopeValue scope: String?) {
setModifyingProfileUI()
NCAPIController.sharedInstance().setUserProfileField(field, withValue: scope, for: account) { [self] error, _ in
if error != nil {
showScopeModificationError()
} else {
refreshUserProfile()
}
removeModifyingProfileUI()
}
}
func showScopeModificationError() {
let errorDialog = UIAlertController(title: NSLocalizedString("An error occurred changing privacy setting", comment: ""), message: nil, preferredStyle: .alert)
let okAction = UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)
errorDialog.addAction(okAction)
self.present(errorDialog, animated: true, completion: nil)
}
// Setup Dialog Options
func setupDetailedOption(identifier: String, imageName: String, title: String, subtitle: String, selected: Bool) -> DetailedOption {
let detailedOption = DetailedOption()
detailedOption.identifier = identifier
detailedOption.image = UIImage(named: imageName)?.withRenderingMode(.alwaysTemplate)
detailedOption.title = title
detailedOption.subtitle = subtitle
detailedOption.selected = selected
return detailedOption
}
func setupFieldsForScopeSelectionDialog(field: inout String?, currentValue: inout String?, title: inout String?, fieldValue: String, currentValueText: String, titleValue: String) {
field = fieldValue
currentValue = currentValueText
title = NSLocalizedString(titleValue, comment: "")
}
func getProfileSections() -> [Int] {
var sections: [Int] = []
if isEditable {
sections.append(ProfileSection.kProfileSectionName.rawValue)
sections.append(ProfileSection.kProfileSectionEmail.rawValue)
sections.append(ProfileSection.kProfileSectionPhoneNumber.rawValue)
sections.append(ProfileSection.kProfileSectionAddress.rawValue)
sections.append(ProfileSection.kProfileSectionWebsite.rawValue)
sections.append(ProfileSection.kProfileSectionTwitter.rawValue)
} else if !(self.rowsInSummarySection().isEmpty) {
sections.append(ProfileSection.kProfileSectionSummary.rawValue)
}
if multiAccountEnabled.boolValue {
sections.append(ProfileSection.kProfileSectionAddAccount.rawValue)
}
sections.append(ProfileSection.kProfileSectionRemoveAccount.rawValue)
return sections
}
func rowsInSummarySection() -> [Int] {
var rows = [Int]()
if !account.email.isEmpty {
rows.append(SummaryRow.kSummaryRowEmail.rawValue)
}
if !account.phone.isEmpty {
rows.append(SummaryRow.kSummaryRowPhoneNumber.rawValue)
}
if !account.address.isEmpty {
rows.append(SummaryRow.kSummaryRowAddress.rawValue)
}
if !account.website.isEmpty {
rows.append(SummaryRow.kSummaryRowWebsite.rawValue)
}
if !account.twitter.isEmpty {
rows.append(SummaryRow.kSummaryRowTwitter.rawValue)
}
return rows
}
}

Просмотреть файл

@ -0,0 +1,369 @@
/**
* @copyright Copyright (c) 2022 Aleksandra Lazarevic <aleksandra@nextcloud.com>
*
* @author Aleksandra Lazarevic <aleksandra@nextcloud.com>
*
* @license GNU GPL version 3 or any later version
*
* 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 UIKit
enum ProfileSection: Int {
case kProfileSectionName = 0
case kProfileSectionEmail
case kProfileSectionPhoneNumber
case kProfileSectionAddress
case kProfileSectionWebsite
case kProfileSectionTwitter
case kProfileSectionSummary
case kProfileSectionAddAccount
case kProfileSectionRemoveAccount
}
enum SummaryRow: Int {
case kSummaryRowEmail = 0
case kSummaryRowPhoneNumber
case kSummaryRowAddress
case kSummaryRowWebsite
case kSummaryRowTwitter
}
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
var account = TalkAccount()
var isEditable = Bool()
var waitingForModification = Bool()
var editButton = UIBarButtonItem()
var activeTextField: UITextField?
var modifyingProfileView = UIActivityIndicatorView()
var editAvatarButton = UIButton()
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()
if #available(iOS 13.0, *) {
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()
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)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Workaround to fix label width
let headerView = self.tableView.tableHeaderView as? AvatarHeaderView
guard let headerView = headerView else {
return
}
var labelFrame = headerView.nameLabel?.frame
let padding: CGFloat = 16
labelFrame?.origin.x = padding
labelFrame?.size.width = self.tableView.bounds.size.width - padding * 2
if let labelFrame = labelFrame {
headerView.nameLabel?.frame = labelFrame
}
}
init(withAccount account: TalkAccount?) {
super.init(style: .grouped)
if let account = account {
self.account = account
}
self.phoneUtil = NBPhoneNumberUtil()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: Notifications
@objc func userProfileImageUpdated(notification: NSNotification) {
self.account = NCDatabaseManager.sharedInstance().activeAccount()
self.refreshProfileTableView()
}
// 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.kProfileSectionAddAccount.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 summaryCellIdentifier = "SummaryCellIdentifier"
let addAccountCellIdentifier = "AddAccountCellIdentifier"
let removeAccountCellIdentifier = "RemoveAccountCellIdentifier"
var isTextInputCell = false
var cell = UITableViewCell()
var textInputCell = tableView.dequeueReusableCell(withIdentifier: kTextInputCellIdentifier) as? TextInputTableViewCell
if textInputCell == nil {
textInputCell = TextInputTableViewCell(style: .default, reuseIdentifier: kTextInputCellIdentifier)
}
textInputCell?.textField?.delegate = self
textInputCell?.textField?.keyboardType = .default
textInputCell?.textField?.placeholder = nil
textInputCell?.textField?.autocorrectionType = .no
let section = self.getProfileSections()[indexPath.section]
switch section {
case ProfileSection.kProfileSectionName.rawValue:
setupTextInputCell(textInputCell: &textInputCell,
text: account.userDisplayName, tag: kNameTextFieldTag, interactionEnabled: editableFields.contains(kUserProfileDisplayName), keyBoardType: nil, placeHolder: nil)
isTextInputCell = true
case ProfileSection.kProfileSectionEmail.rawValue:
setupTextInputCell(textInputCell: &textInputCell, text: account.email,
tag: kEmailTextFieldTag, interactionEnabled: editableFields.contains(kUserProfileEmail),
keyBoardType: .emailAddress, placeHolder: NSLocalizedString("Your email address", comment: ""))
isTextInputCell = true
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
setupTextInputCell(textInputCell: &textInputCell, text: text,
tag: kPhoneTextFieldTag, interactionEnabled: false,
keyBoardType: .phonePad, placeHolder: NSLocalizedString("Your phone number", comment: ""))
isTextInputCell = true
case ProfileSection.kProfileSectionAddress.rawValue:
setupTextInputCell(textInputCell: &textInputCell, text: account.address,
tag: kAddressTextFieldTag, interactionEnabled: editableFields.contains(kUserProfileAddress),
keyBoardType: nil, placeHolder: NSLocalizedString("Your postal address", comment: ""))
isTextInputCell = true
case ProfileSection.kProfileSectionWebsite.rawValue:
setupTextInputCell(textInputCell: &textInputCell, text: account.website,
tag: kWebsiteTextFieldTag, interactionEnabled: editableFields.contains(kUserProfileWebsite),
keyBoardType: .URL, placeHolder: NSLocalizedString("Link https://…", comment: ""))
isTextInputCell = true
case ProfileSection.kProfileSectionTwitter.rawValue:
setupTextInputCell(textInputCell: &textInputCell, text: account.twitter,
tag: kTwitterTextFieldTag, interactionEnabled: editableFields.contains(kUserProfileTwitter),
keyBoardType: .emailAddress, placeHolder: NSLocalizedString("Twitter handle @…", comment: ""))
isTextInputCell = true
case ProfileSection.kProfileSectionSummary.rawValue:
cell = UITableViewCell(style: .default, reuseIdentifier: summaryCellIdentifier)
var scopeImage: UIImage?
let summaryRow = self.rowsInSummarySection()[indexPath.row]
switch summaryRow {
case SummaryRow.kSummaryRowEmail.rawValue:
setupSummaryRowCell(cell: &cell, scopeImage: &scopeImage, text: account.email, image: (UIImage(named: "mail")?.withRenderingMode(.alwaysTemplate)), scope: account.emailScope)
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
setupSummaryRowCell(cell: &cell, scopeImage: &scopeImage, text: text, image: UIImage(named: "phone")?.withRenderingMode(.alwaysTemplate), scope: account.phoneScope)
case SummaryRow.kSummaryRowAddress.rawValue:
setupSummaryRowCell(cell: &cell, scopeImage: &scopeImage, text: account.address, image: UIImage(named: "location")?.withRenderingMode(.alwaysTemplate), scope: account.addressScope)
case SummaryRow.kSummaryRowWebsite.rawValue:
setupSummaryRowCell(cell: &cell, scopeImage: &scopeImage, text: account.website, image: UIImage(named: "website")?.withRenderingMode(.alwaysTemplate), scope: account.websiteScope)
case SummaryRow.kSummaryRowTwitter.rawValue:
setupSummaryRowCell(cell: &cell, scopeImage: &scopeImage, text: account.twitter, image: UIImage(named: "twitter")?.withRenderingMode(.alwaysTemplate), scope: account.twitterScope)
default:
break
}
cell.imageView?.tintColor = UIColor(red: 0.43, green: 0.43, blue: 0.45, alpha: 1)
if showScopes ?? false {
let scopeImageView = UIImageView(image: scopeImage)
scopeImageView.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
scopeImageView.tintColor = NCAppBranding.placeholderColor()
}
case ProfileSection.kProfileSectionAddAccount.rawValue:
cell = UITableViewCell(style: .default, reuseIdentifier: addAccountCellIdentifier)
setupAddRemoveAccountCell(cell: &cell, text: NSLocalizedString("Add account", comment: ""), textColor: .systemBlue, image: UIImage(named: "add-action"), tintColor: .systemBlue)
case ProfileSection.kProfileSectionRemoveAccount.rawValue:
cell = UITableViewCell(style: .default, reuseIdentifier: removeAccountCellIdentifier)
let actionTitle = multiAccountEnabled.boolValue ? NSLocalizedString("Remove account", comment: "") : NSLocalizedString("Log out", comment: "")
let actionImage = multiAccountEnabled.boolValue ? UIImage(named: "delete") : UIImage(named: "logout")
setupAddRemoveAccountCell(cell: &cell, text: actionTitle, textColor: .systemRed, image: actionImage, tintColor: .systemRed)
default:
break
}
if isTextInputCell {
if let textInputCell = textInputCell {
cell = textInputCell
}
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let sections = getProfileSections()
let section = sections[indexPath.section]
if section == ProfileSection.kProfileSectionAddAccount.rawValue {
self.addNewAccount()
} else 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), for: .normal)
}
func setupViewforHeaderInSection(profileSection: Int) -> HeaderWithButton {
var headerView = HeaderWithButton()
headerView.button.addTarget(self, action: #selector(showScopeSelectionDialog(_:)), for: .touchUpInside)
let serverCapabilities = NCDatabaseManager.sharedInstance().serverCapabilities(forAccountId: account.accountId)
let shouldEnableNameAndEmailScopeButton = serverCapabilities.accountPropertyScopesFederationEnabled
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 setupTextInputCell(textInputCell: inout TextInputTableViewCell?, text: String?, tag: Int?, interactionEnabled: Bool?, keyBoardType: UIKeyboardType?, placeHolder: String?) {
guard let textInputCell = textInputCell else {
return
}
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 placeHolder = placeHolder {
textInputCell.textField.placeholder = placeHolder
}
}
func setupSummaryRowCell(cell: inout UITableViewCell, scopeImage: inout UIImage?, text: String?, image: UIImage?, scope: String) {
if let text = text {
cell.textLabel?.text = text
}
cell.imageView?.image = image
scopeImage = self.imageForScope(scope: scope)
}
func setupAddRemoveAccountCell(cell: inout UITableViewCell, text: String, textColor: UIColor, image: UIImage?, tintColor: UIColor) {
cell.textLabel?.text = text
cell.textLabel?.textColor = textColor
cell.imageView?.image = image?.withRenderingMode(.alwaysTemplate)
cell.imageView?.tintColor = tintColor
}
}

Просмотреть файл

@ -1,35 +0,0 @@
/**
* @copyright Copyright (c) 2021 Ivan Sein <ivan@nextcloud.com>
*
* @author Ivan Sein <ivan@nextcloud.com>
*
* @license GNU GPL version 3 or any later version
*
* 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 <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class TalkAccount;
@interface UserProfileViewController : UITableViewController
- (instancetype)initWithAccount:(TalkAccount *)account;
@end
NS_ASSUME_NONNULL_END

Разница между файлами не показана из-за своего большого размера Загрузить разницу