Convert profile view to Swift.
This commit is contained in:
parent
9ff21657b7
commit
c693f96ae8
@ -394,7 +394,6 @@
|
||||
34C6B0AE1FA0E4AA00D35993 /* test-jpg.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 34C6B0AD1FA0E4AA00D35993 /* test-jpg.jpg */; };
|
||||
34C758DA2289DDDD00802527 /* StickerSharingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34C758D92289DDDC00802527 /* StickerSharingViewController.swift */; };
|
||||
34C758DD228DD81C00802527 /* StickerTooltip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34C758DC228DD81C00802527 /* StickerTooltip.swift */; };
|
||||
34CE88E71F2FB9A10098030F /* ProfileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CE88E61F2FB9A10098030F /* ProfileViewController.m */; };
|
||||
34CF0787203E6B78005C4D61 /* busy_tone_ansi.caf in Resources */ = {isa = PBXBuildFile; fileRef = 34CF0783203E6B77005C4D61 /* busy_tone_ansi.caf */; };
|
||||
34CF0788203E6B78005C4D61 /* ringback_tone_ansi.caf in Resources */ = {isa = PBXBuildFile; fileRef = 34CF0784203E6B77005C4D61 /* ringback_tone_ansi.caf */; };
|
||||
34CF078A203E6B78005C4D61 /* end_call_tone_cept.caf in Resources */ = {isa = PBXBuildFile; fileRef = 34CF0786203E6B78005C4D61 /* end_call_tone_cept.caf */; };
|
||||
@ -1406,8 +1405,6 @@
|
||||
34C758D92289DDDC00802527 /* StickerSharingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerSharingViewController.swift; sourceTree = "<group>"; };
|
||||
34C758DC228DD81C00802527 /* StickerTooltip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerTooltip.swift; sourceTree = "<group>"; };
|
||||
34CA1C261F7156F300E51C51 /* MessageDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageDetailViewController.swift; sourceTree = "<group>"; };
|
||||
34CE88E51F2FB9A10098030F /* ProfileViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProfileViewController.h; sourceTree = "<group>"; };
|
||||
34CE88E61F2FB9A10098030F /* ProfileViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProfileViewController.m; sourceTree = "<group>"; };
|
||||
34CF0783203E6B77005C4D61 /* busy_tone_ansi.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = busy_tone_ansi.caf; path = Signal/AudioFiles/busy_tone_ansi.caf; sourceTree = SOURCE_ROOT; };
|
||||
34CF0784203E6B77005C4D61 /* ringback_tone_ansi.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = ringback_tone_ansi.caf; path = Signal/AudioFiles/ringback_tone_ansi.caf; sourceTree = SOURCE_ROOT; };
|
||||
34CF0786203E6B78005C4D61 /* end_call_tone_cept.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = end_call_tone_cept.caf; path = Signal/AudioFiles/end_call_tone_cept.caf; sourceTree = SOURCE_ROOT; };
|
||||
@ -2784,8 +2781,6 @@
|
||||
881677C422DD2B21007BAF49 /* OWSPinReminderViewController.swift */,
|
||||
881D85B722D92C2B00E118DF /* OWSPinSetupViewController.swift */,
|
||||
34969558219B605E00DCFE74 /* Photos */,
|
||||
34CE88E51F2FB9A10098030F /* ProfileViewController.h */,
|
||||
34CE88E61F2FB9A10098030F /* ProfileViewController.m */,
|
||||
34798A8425AF370D00EC89B4 /* ProfileViewController.swift */,
|
||||
8835DDFB230CEE7600DC6B66 /* RecipientPicker */,
|
||||
340FC875204DAC8C007AEB0F /* Registration */,
|
||||
@ -5156,7 +5151,6 @@
|
||||
8835DDFD230CEE8900DC6B66 /* RecipientPickerDelegate.swift in Sources */,
|
||||
3465947E2434CCC100E5C510 /* NewGroupConfirmViewController.swift in Sources */,
|
||||
3470C8742554926200F5847C /* QuotedMessageView.swift in Sources */,
|
||||
34CE88E71F2FB9A10098030F /* ProfileViewController.m in Sources */,
|
||||
34B14D8F24F41C4300CC3A9A /* GroupLinkQRCodeViewController.swift in Sources */,
|
||||
3490D57F25ADE49800F5F96C /* ActionSheetContentBuilder.swift in Sources */,
|
||||
347C3845252CE6C900F3D941 /* CVComponentQuotedReply.swift in Sources */,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@ -47,7 +47,6 @@
|
||||
#import "Pastelog.h"
|
||||
#import "PinEntryView.h"
|
||||
#import "PrivacySettingsTableViewController.h"
|
||||
#import "ProfileViewController.h"
|
||||
#import "RecipientPickerViewController.h"
|
||||
#import "RemoteVideoView.h"
|
||||
#import "SignalApp.h"
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
#import "OWSBackupSettingsViewController.h"
|
||||
#import "OWSNavigationController.h"
|
||||
#import "PrivacySettingsTableViewController.h"
|
||||
#import "ProfileViewController.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import <SignalMessaging/Environment.h>
|
||||
#import <SignalMessaging/OWSContactsManager.h>
|
||||
@ -347,10 +346,10 @@
|
||||
- (void)showProfile
|
||||
{
|
||||
UIViewController *vc =
|
||||
[[ProfileViewController2 alloc] initWithMode:ProfileViewMode_AppSettings
|
||||
completionHandler:^(ProfileViewController2 *completedVC) {
|
||||
[completedVC.navigationController popViewControllerAnimated:YES];
|
||||
}];
|
||||
[[ProfileViewController alloc] initWithMode:ProfileViewModeAppSettings
|
||||
completionHandler:^(ProfileViewController *completedVC) {
|
||||
[completedVC.navigationController popViewControllerAnimated:YES];
|
||||
}];
|
||||
[self.navigationController pushViewController:vc animated:YES];
|
||||
}
|
||||
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <SignalMessaging/OWSViewController.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ConversationListViewController;
|
||||
@class SDSKeyValueStore;
|
||||
|
||||
typedef NS_ENUM(NSInteger, ProfileViewMode) {
|
||||
ProfileViewMode_AppSettings = 0,
|
||||
ProfileViewMode_Registration,
|
||||
ProfileViewMode_ExperienceUpgrade,
|
||||
};
|
||||
|
||||
@interface ProfileViewController : OWSViewController
|
||||
|
||||
+ (SDSKeyValueStore *)keyValueStore;
|
||||
|
||||
- (instancetype)initWithMode:(ProfileViewMode)profileViewMode
|
||||
completionHandler:(void (^)(ProfileViewController *))completionHandler;
|
||||
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
+ (BOOL)shouldDisplayProfileViewOnLaunch;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,846 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ProfileViewController.h"
|
||||
#import "AppDelegate.h"
|
||||
#import "AvatarViewHelper.h"
|
||||
#import "ConversationListViewController.h"
|
||||
#import "OWSNavigationController.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import "UIFont+OWS.h"
|
||||
#import "UIView+OWS.h"
|
||||
#import <PromiseKit/AnyPromise.h>
|
||||
#import <SignalCoreKit/NSDate+OWS.h>
|
||||
#import <SignalCoreKit/NSString+OWS.h>
|
||||
#import <SignalMessaging/OWSNavigationController.h>
|
||||
#import <SignalMessaging/OWSProfileManager.h>
|
||||
#import <SignalMessaging/SignalMessaging-Swift.h>
|
||||
#import <SignalMessaging/UIUtil.h>
|
||||
#import <SignalMessaging/UIViewController+OWS.h>
|
||||
|
||||
@import SafariServices;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDate";
|
||||
|
||||
@interface ProfileViewController () <UITextFieldDelegate, AvatarViewHelperDelegate, OWSNavigationView>
|
||||
|
||||
@property (nonatomic, readonly) AvatarViewHelper *avatarViewHelper;
|
||||
|
||||
@property (nonatomic) UITextField *givenNameTextField;
|
||||
|
||||
@property (nonatomic) UITextField *familyNameTextField;
|
||||
|
||||
@property (nonatomic) UILabel *profileNamePreviewLabel;
|
||||
|
||||
@property (nonatomic) UILabel *usernameLabel;
|
||||
|
||||
@property (nonatomic) AvatarImageView *avatarView;
|
||||
|
||||
@property (nonatomic) UIImageView *cameraImageView;
|
||||
|
||||
@property (nonatomic) OWSFlatButton *saveButton;
|
||||
|
||||
@property (nonatomic, nullable) NSData *avatarData;
|
||||
|
||||
@property (nonatomic) BOOL hasUnsavedChanges;
|
||||
|
||||
@property (nonatomic) ProfileViewMode profileViewMode;
|
||||
|
||||
@property (nonatomic, readonly) void (^completionHandler)(ProfileViewController *);
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation ProfileViewController
|
||||
|
||||
+ (SDSKeyValueStore *)keyValueStore
|
||||
{
|
||||
return [[SDSKeyValueStore alloc] initWithCollection:@"kProfileView_Collection"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithMode:(ProfileViewMode)profileViewMode
|
||||
completionHandler:(void (^)(ProfileViewController *))completionHandler
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_profileViewMode = profileViewMode;
|
||||
_completionHandler = completionHandler;
|
||||
|
||||
DatabaseStorageAsyncWrite(SDSDatabaseStorage.shared, ^(SDSAnyWriteTransaction *transaction) {
|
||||
[ProfileViewController.keyValueStore setDate:[NSDate new]
|
||||
key:kProfileView_LastPresentedDate
|
||||
transaction:transaction];
|
||||
});
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)loadView
|
||||
{
|
||||
[super loadView];
|
||||
|
||||
self.title = NSLocalizedString(@"PROFILE_VIEW_TITLE", @"Title for the profile view.");
|
||||
|
||||
_avatarViewHelper = [AvatarViewHelper new];
|
||||
_avatarViewHelper.delegate = self;
|
||||
|
||||
_avatarData = [OWSProfileManager.shared localProfileAvatarData];
|
||||
|
||||
[self createViews];
|
||||
[self updateNavigationItem];
|
||||
|
||||
if (self.profileViewMode == ProfileViewMode_Registration) {
|
||||
// mark as dirty if re-registration has content
|
||||
if (self.familyNameTextField.text.length > 0 || self.givenNameTextField.text.length > 0
|
||||
|| self.avatarData != nil) {
|
||||
self.hasUnsavedChanges = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createViews
|
||||
{
|
||||
self.view.backgroundColor = Theme.backgroundColor;
|
||||
|
||||
UIStackView *stackView = [UIStackView new];
|
||||
stackView.axis = UILayoutConstraintAxisVertical;
|
||||
stackView.spacing = 0;
|
||||
|
||||
[self.view addSubview:stackView];
|
||||
|
||||
[stackView autoPinToTopLayoutGuideOfViewController:self withInset:15.f];
|
||||
[stackView autoPinWidthToSuperview];
|
||||
|
||||
void (^addSeparator)(BOOL) = ^(BOOL withLeadingInset) {
|
||||
UIView *separatorWrapper = [UIView containerView];
|
||||
[stackView addArrangedSubview:separatorWrapper];
|
||||
UIView *separator = [UIView containerView];
|
||||
separator.backgroundColor = Theme.cellSeparatorColor;
|
||||
[separatorWrapper addSubview:separator];
|
||||
[separator autoPinHeightToSuperview];
|
||||
[separator autoPinLeadingToSuperviewMarginWithInset:withLeadingInset ? 18 : 0];
|
||||
[separator autoPinTrailingToSuperviewMargin];
|
||||
[separator autoSetDimension:ALDimensionHeight toSize:CGHairlineWidth()];
|
||||
};
|
||||
|
||||
CGFloat rowSpacing = 10;
|
||||
UIEdgeInsets rowMargins = UIEdgeInsetsMake(10, 18, 10, 18);
|
||||
|
||||
// Avatar
|
||||
|
||||
UIView *avatarRow = [UIView containerView];
|
||||
[stackView addArrangedSubview:avatarRow];
|
||||
|
||||
self.avatarView = [AvatarImageView new];
|
||||
self.avatarView.userInteractionEnabled = YES;
|
||||
self.avatarView.accessibilityIdentifier = ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"avatarView");
|
||||
|
||||
[self.avatarView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(avatarViewTapped:)]];
|
||||
|
||||
[avatarRow addSubview:self.avatarView];
|
||||
[self.avatarView autoHCenterInSuperview];
|
||||
[self.avatarView autoPinEdgeToSuperviewEdge:ALEdgeTop];
|
||||
[self.avatarView autoSetDimension:ALDimensionWidth toSize:self.avatarSize];
|
||||
[self.avatarView autoSetDimension:ALDimensionHeight toSize:self.avatarSize];
|
||||
|
||||
self.cameraImageView = [UIImageView new];
|
||||
[self.cameraImageView setTemplateImageName:@"camera-outline-24" tintColor:Theme.secondaryTextAndIconColor];
|
||||
[self.cameraImageView autoSetDimensionsToSize:CGSizeMake(32, 32)];
|
||||
self.cameraImageView.contentMode = UIViewContentModeCenter;
|
||||
self.cameraImageView.backgroundColor = Theme.backgroundColor;
|
||||
self.cameraImageView.layer.cornerRadius = 16;
|
||||
self.cameraImageView.layer.shadowColor =
|
||||
[(Theme.isDarkThemeEnabled ? Theme.darkThemeWashColor : Theme.primaryTextColor) CGColor];
|
||||
self.cameraImageView.layer.shadowOffset = CGSizeMake(1, 1);
|
||||
self.cameraImageView.layer.shadowOpacity = 0.5;
|
||||
self.cameraImageView.layer.shadowRadius = 4;
|
||||
|
||||
[avatarRow addSubview:self.cameraImageView];
|
||||
[self.cameraImageView autoPinTrailingToEdgeOfView:self.avatarView];
|
||||
[self.cameraImageView autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:self.avatarView];
|
||||
|
||||
self.profileNamePreviewLabel = [UILabel new];
|
||||
self.profileNamePreviewLabel.textAlignment = NSTextAlignmentCenter;
|
||||
self.profileNamePreviewLabel.textColor = Theme.secondaryTextAndIconColor;
|
||||
self.profileNamePreviewLabel.font = UIFont.ows_dynamicTypeSubheadlineClampedFont;
|
||||
[avatarRow addSubview:self.profileNamePreviewLabel];
|
||||
[self.profileNamePreviewLabel autoPinWidthToSuperviewWithMargin:16];
|
||||
[self.profileNamePreviewLabel autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.avatarView withOffset:16];
|
||||
[self.profileNamePreviewLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom
|
||||
withInset:28
|
||||
relation:NSLayoutRelationGreaterThanOrEqual];
|
||||
[self.profileNamePreviewLabel autoSetDimension:ALDimensionHeight toSize:16];
|
||||
|
||||
[self updateAvatarView];
|
||||
|
||||
addSeparator(NO);
|
||||
|
||||
// Given Name
|
||||
|
||||
void (^addGivenNameRow)(void) = ^{
|
||||
UIStackView *givenNameRow = [UIStackView new];
|
||||
givenNameRow.spacing = rowSpacing;
|
||||
givenNameRow.layoutMarginsRelativeArrangement = YES;
|
||||
givenNameRow.layoutMargins = rowMargins;
|
||||
[givenNameRow
|
||||
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(givenNameRowTapped:)]];
|
||||
givenNameRow.accessibilityIdentifier = ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"givenNameRow");
|
||||
[stackView addArrangedSubview:givenNameRow];
|
||||
|
||||
UILabel *givenNameLabel = [UILabel new];
|
||||
givenNameLabel.text = NSLocalizedString(
|
||||
@"PROFILE_VIEW_GIVEN_NAME_FIELD", @"Label for the given name field of the profile view.");
|
||||
givenNameLabel.textColor = Theme.primaryTextColor;
|
||||
givenNameLabel.font = [[UIFont ows_dynamicTypeBodyClampedFont] ows_semibold];
|
||||
[givenNameRow addArrangedSubview:givenNameLabel];
|
||||
|
||||
UITextField *givenNameTextField;
|
||||
if (UIDevice.currentDevice.isIPhone5OrShorter) {
|
||||
givenNameTextField = [DismissableTextField new];
|
||||
} else {
|
||||
givenNameTextField = [OWSTextField new];
|
||||
}
|
||||
self.givenNameTextField = givenNameTextField;
|
||||
givenNameTextField.returnKeyType = UIReturnKeyNext;
|
||||
givenNameTextField.autocorrectionType = UITextAutocorrectionTypeNo;
|
||||
givenNameTextField.spellCheckingType = UITextSpellCheckingTypeNo;
|
||||
givenNameTextField.font = [UIFont ows_dynamicTypeBodyClampedFont];
|
||||
givenNameTextField.textColor = Theme.primaryTextColor;
|
||||
givenNameTextField.placeholder = NSLocalizedString(
|
||||
@"PROFILE_VIEW_GIVEN_NAME_DEFAULT_TEXT", @"Default text for the given name field of the profile view.");
|
||||
givenNameTextField.delegate = self;
|
||||
givenNameTextField.text = OWSProfileManager.shared.localGivenName;
|
||||
givenNameTextField.textAlignment = NSTextAlignmentRight;
|
||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, givenNameTextField);
|
||||
[givenNameTextField addTarget:self
|
||||
action:@selector(textFieldDidChange:)
|
||||
forControlEvents:UIControlEventEditingChanged];
|
||||
[givenNameRow addArrangedSubview:givenNameTextField];
|
||||
};
|
||||
|
||||
// Family Name
|
||||
|
||||
void (^addFamilyNameRow)(void) = ^{
|
||||
UIStackView *familyNameRow = [UIStackView new];
|
||||
familyNameRow.spacing = rowSpacing;
|
||||
familyNameRow.layoutMarginsRelativeArrangement = YES;
|
||||
familyNameRow.layoutMargins = rowMargins;
|
||||
[familyNameRow
|
||||
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(familyNameRowTapped:)]];
|
||||
familyNameRow.accessibilityIdentifier = ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"familyNameRow");
|
||||
[stackView addArrangedSubview:familyNameRow];
|
||||
|
||||
UILabel *familyNameLabel = [UILabel new];
|
||||
familyNameLabel.text = NSLocalizedString(
|
||||
@"PROFILE_VIEW_FAMILY_NAME_FIELD", @"Label for the family name field of the profile view.");
|
||||
familyNameLabel.textColor = Theme.primaryTextColor;
|
||||
familyNameLabel.font = [[UIFont ows_dynamicTypeBodyClampedFont] ows_semibold];
|
||||
[familyNameRow addArrangedSubview:familyNameLabel];
|
||||
|
||||
UITextField *familyNameTextField;
|
||||
if (UIDevice.currentDevice.isIPhone5OrShorter) {
|
||||
familyNameTextField = [DismissableTextField new];
|
||||
} else {
|
||||
familyNameTextField = [OWSTextField new];
|
||||
}
|
||||
self.familyNameTextField = familyNameTextField;
|
||||
familyNameTextField.returnKeyType = UIReturnKeyDone;
|
||||
familyNameTextField.autocorrectionType = UITextAutocorrectionTypeNo;
|
||||
familyNameTextField.spellCheckingType = UITextSpellCheckingTypeNo;
|
||||
familyNameTextField.font = [UIFont ows_dynamicTypeBodyClampedFont];
|
||||
familyNameTextField.textColor = Theme.primaryTextColor;
|
||||
familyNameTextField.placeholder = NSLocalizedString(
|
||||
@"PROFILE_VIEW_FAMILY_NAME_DEFAULT_TEXT", @"Default text for the family name field of the profile view.");
|
||||
familyNameTextField.delegate = self;
|
||||
familyNameTextField.text = OWSProfileManager.shared.localFamilyName;
|
||||
familyNameTextField.textAlignment = NSTextAlignmentRight;
|
||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, familyNameTextField);
|
||||
[familyNameTextField addTarget:self
|
||||
action:@selector(textFieldDidChange:)
|
||||
forControlEvents:UIControlEventEditingChanged];
|
||||
[familyNameRow addArrangedSubview:familyNameTextField];
|
||||
};
|
||||
|
||||
// For CJKV locales, display family name field first.
|
||||
if (NSLocale.currentLocale.isCJKV) {
|
||||
addFamilyNameRow();
|
||||
addSeparator(YES);
|
||||
addGivenNameRow();
|
||||
|
||||
// Otherwise, display given name field first.
|
||||
} else {
|
||||
addGivenNameRow();
|
||||
addSeparator(YES);
|
||||
addFamilyNameRow();
|
||||
}
|
||||
|
||||
[self updateProfileNamePreview];
|
||||
|
||||
// Username
|
||||
|
||||
if (self.shouldShowUsernameRow) {
|
||||
addSeparator(YES);
|
||||
|
||||
UIStackView *usernameRow = [UIStackView new];
|
||||
usernameRow.spacing = rowSpacing;
|
||||
usernameRow.layoutMarginsRelativeArrangement = YES;
|
||||
usernameRow.layoutMargins = rowMargins;
|
||||
[usernameRow
|
||||
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(usernameRowTapped:)]];
|
||||
usernameRow.accessibilityIdentifier = ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"usernameRow");
|
||||
[stackView addArrangedSubview:usernameRow];
|
||||
|
||||
UILabel *usernameTitleLabel = [UILabel new];
|
||||
usernameTitleLabel.text
|
||||
= NSLocalizedString(@"PROFILE_VIEW_USERNAME_FIELD", @"Label for the username field of the profile view.");
|
||||
usernameTitleLabel.textColor = Theme.primaryTextColor;
|
||||
usernameTitleLabel.font = [[UIFont ows_dynamicTypeBodyClampedFont] ows_semibold];
|
||||
[usernameRow addArrangedSubview:usernameTitleLabel];
|
||||
|
||||
UILabel *usernameLabel = [UILabel new];
|
||||
|
||||
usernameLabel.font = [UIFont ows_dynamicTypeBodyClampedFont];
|
||||
usernameLabel.textAlignment = NSTextAlignmentRight;
|
||||
[usernameRow addArrangedSubview:usernameLabel];
|
||||
|
||||
_usernameLabel = usernameLabel;
|
||||
|
||||
UIView *disclosureImageContainer = [UIView containerView];
|
||||
[usernameRow addArrangedSubview:disclosureImageContainer];
|
||||
|
||||
NSString *disclosureImageName
|
||||
= CurrentAppContext().isRTL ? @"system_disclosure_indicator_rtl" : @"system_disclosure_indicator";
|
||||
UIImageView *disclosureImageView = [UIImageView new];
|
||||
[disclosureImageView setTemplateImageName:disclosureImageName tintColor:Theme.cellSeparatorColor];
|
||||
|
||||
[disclosureImageContainer addSubview:disclosureImageView];
|
||||
[disclosureImageView autoPinWidthToSuperview];
|
||||
[disclosureImageView autoVCenterInSuperview];
|
||||
[disclosureImageView autoSetDimension:ALDimensionHeight toSize:13];
|
||||
[disclosureImageView autoSetDimension:ALDimensionWidth toSize:11];
|
||||
|
||||
[self updateUsername];
|
||||
|
||||
addSeparator(NO);
|
||||
} else {
|
||||
addSeparator(NO);
|
||||
}
|
||||
|
||||
// Information
|
||||
|
||||
UIView *infoRow = [UIView new];
|
||||
infoRow.layoutMargins = rowMargins;
|
||||
[infoRow
|
||||
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(infoRowTapped:)]];
|
||||
infoRow.accessibilityIdentifier = ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"infoRow");
|
||||
[stackView addArrangedSubview:infoRow];
|
||||
|
||||
UILabel *infoLabel = [UILabel new];
|
||||
infoLabel.textColor = Theme.secondaryTextAndIconColor;
|
||||
infoLabel.font = [UIFont ows_dynamicTypeCaption1ClampedFont];
|
||||
NSMutableAttributedString *text = [NSMutableAttributedString new];
|
||||
[text appendAttributedString:[[NSAttributedString alloc]
|
||||
initWithString:NSLocalizedString(@"PROFILE_VIEW_PROFILE_DESCRIPTION",
|
||||
@"Description of the user profile.")
|
||||
attributes:@{}]];
|
||||
[text appendAttributedString:[[NSAttributedString alloc] initWithString:@" " attributes:@{}]];
|
||||
[text appendAttributedString:[[NSAttributedString alloc]
|
||||
initWithString:CommonStrings.learnMore
|
||||
attributes:@{
|
||||
NSUnderlineStyleAttributeName : @(NSUnderlineStyleNone),
|
||||
NSForegroundColorAttributeName : Theme.accentBlueColor,
|
||||
}]];
|
||||
infoLabel.attributedText = text;
|
||||
infoLabel.numberOfLines = 0;
|
||||
infoLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
[infoRow addSubview:infoLabel];
|
||||
[infoLabel autoPinEdgesToSuperviewMargins];
|
||||
|
||||
// Big Button
|
||||
|
||||
switch (self.profileViewMode) {
|
||||
case ProfileViewMode_Registration:
|
||||
case ProfileViewMode_ExperienceUpgrade: {
|
||||
UIView *buttonRow = [UIView new];
|
||||
buttonRow.layoutMargins = rowMargins;
|
||||
[stackView addArrangedSubview:buttonRow];
|
||||
|
||||
const CGFloat kButtonHeight = 47.f;
|
||||
OWSFlatButton *saveButton =
|
||||
[OWSFlatButton buttonWithTitle:NSLocalizedString(@"PROFILE_VIEW_SAVE_BUTTON",
|
||||
@"Button to save the profile view in the profile view.")
|
||||
font:[OWSFlatButton fontForHeight:kButtonHeight]
|
||||
titleColor:[UIColor whiteColor]
|
||||
backgroundColor:UIColor.ows_accentBlueColor
|
||||
target:self
|
||||
selector:@selector(saveButtonPressed)];
|
||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, saveButton);
|
||||
self.saveButton = saveButton;
|
||||
[buttonRow addSubview:saveButton];
|
||||
[saveButton autoPinEdgesToSuperviewMargins];
|
||||
[saveButton autoSetDimension:ALDimensionHeight toSize:47.f];
|
||||
|
||||
break;
|
||||
}
|
||||
case ProfileViewMode_AppSettings:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
switch (self.profileViewMode) {
|
||||
case ProfileViewMode_AppSettings:
|
||||
break;
|
||||
case ProfileViewMode_Registration:
|
||||
case ProfileViewMode_ExperienceUpgrade:
|
||||
[self.givenNameTextField becomeFirstResponder];
|
||||
break;
|
||||
}
|
||||
|
||||
[self updateUsername];
|
||||
}
|
||||
|
||||
#pragma mark - Event Handling
|
||||
|
||||
- (void)leaveViewCheckingForUnsavedChanges
|
||||
{
|
||||
[self.familyNameTextField resignFirstResponder];
|
||||
[self.givenNameTextField resignFirstResponder];
|
||||
|
||||
if (!self.hasUnsavedChanges) {
|
||||
// If user made no changes, return to conversation settings view.
|
||||
[self profileCompleted];
|
||||
return;
|
||||
}
|
||||
|
||||
__weak ProfileViewController *weakSelf = self;
|
||||
[OWSActionSheets showPendingChangesActionSheetWithDiscardAction:^{
|
||||
[weakSelf profileCompleted];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)avatarTapped
|
||||
{
|
||||
[self.avatarViewHelper showChangeAvatarUI];
|
||||
}
|
||||
|
||||
- (void)setHasUnsavedChanges:(BOOL)hasUnsavedChanges
|
||||
{
|
||||
_hasUnsavedChanges = hasUnsavedChanges;
|
||||
|
||||
[self updateNavigationItem];
|
||||
}
|
||||
|
||||
- (void)updateNavigationItem
|
||||
{
|
||||
// The navigation bar is hidden in the registration workflow.
|
||||
if (self.navigationController.navigationBarHidden) {
|
||||
[self.navigationController setNavigationBarHidden:NO animated:YES];
|
||||
}
|
||||
|
||||
BOOL forceSaveButtonEnabled = NO;
|
||||
|
||||
switch (self.profileViewMode) {
|
||||
case ProfileViewMode_AppSettings:
|
||||
if (self.hasUnsavedChanges) {
|
||||
// If we have a unsaved changes, right item should be a "save" button.
|
||||
UIBarButtonItem *saveButton =
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave
|
||||
target:self
|
||||
action:@selector(updatePressed)];
|
||||
SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, saveButton);
|
||||
self.navigationItem.rightBarButtonItem = saveButton;
|
||||
} else {
|
||||
self.navigationItem.rightBarButtonItem = nil;
|
||||
}
|
||||
break;
|
||||
case ProfileViewMode_Registration:
|
||||
self.navigationItem.hidesBackButton = YES;
|
||||
self.navigationItem.rightBarButtonItem = nil;
|
||||
|
||||
// During registration, if you have a name pre-populatd we want
|
||||
// to enable the save button even if you haven't edited anything.
|
||||
if (self.givenNameTextField.text.length > 0) {
|
||||
forceSaveButtonEnabled = YES;
|
||||
}
|
||||
break;
|
||||
case ProfileViewMode_ExperienceUpgrade:
|
||||
self.navigationItem.rightBarButtonItem = nil;
|
||||
|
||||
// During the experience upgrade, if you have a name we want
|
||||
// to enable the save button even if you haven't edited anything.
|
||||
if (self.givenNameTextField.text.length > 0) {
|
||||
forceSaveButtonEnabled = YES;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (self.hasUnsavedChanges || forceSaveButtonEnabled) {
|
||||
self.saveButton.enabled = YES;
|
||||
[self.saveButton setBackgroundColorsWithUpColor:UIColor.ows_accentBlueColor];
|
||||
} else {
|
||||
self.saveButton.enabled = NO;
|
||||
[self.saveButton
|
||||
setBackgroundColorsWithUpColor:[UIColor.ows_accentBlueColor blendedWithColor:Theme.backgroundColor
|
||||
alpha:0.5f]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updatePressed
|
||||
{
|
||||
[self updateProfile];
|
||||
}
|
||||
|
||||
- (void)updateProfile
|
||||
{
|
||||
__weak ProfileViewController *weakSelf = self;
|
||||
|
||||
NSString *normalizedGivenName = [self normalizedGivenName];
|
||||
NSString *normalizedFamilyName = [self normalizedFamilyName];
|
||||
|
||||
if (normalizedGivenName.length <= 0) {
|
||||
[OWSActionSheets showErrorAlertWithMessage:
|
||||
NSLocalizedString(@"PROFILE_VIEW_ERROR_GIVEN_NAME_REQUIRED",
|
||||
@"Error message shown when user tries to update profile without a given name")];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([OWSProfileManager.shared isProfileNameTooLong:normalizedGivenName]) {
|
||||
[OWSActionSheets
|
||||
showErrorAlertWithMessage:NSLocalizedString(@"PROFILE_VIEW_ERROR_GIVEN_NAME_TOO_LONG",
|
||||
@"Error message shown when user tries to update profile with a given name "
|
||||
@"that is too long.")];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([OWSProfileManager.shared isProfileNameTooLong:normalizedFamilyName]) {
|
||||
[OWSActionSheets
|
||||
showErrorAlertWithMessage:NSLocalizedString(@"PROFILE_VIEW_ERROR_FAMILY_NAME_TOO_LONG",
|
||||
@"Error message shown when user tries to update profile with a family name "
|
||||
@"that is too long.")];
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.reachabilityManager.isReachable) {
|
||||
[OWSActionSheets
|
||||
showErrorAlertWithMessage:
|
||||
NSLocalizedString(@"PROFILE_VIEW_NO_CONNECTION",
|
||||
@"Error shown when the user tries to update their profile when the app is not connected to the "
|
||||
@"internet.")];
|
||||
return;
|
||||
}
|
||||
|
||||
// Show an activity indicator to block the UI during the profile upload.
|
||||
[ModalActivityIndicatorViewController
|
||||
presentFromViewController:self
|
||||
canCancel:NO
|
||||
backgroundBlock:^(ModalActivityIndicatorViewController *modalActivityIndicator) {
|
||||
// [OWSProfileManager
|
||||
// updateLocalProfilePromiseObjWithProfileGivenName:normalizedGivenName
|
||||
// profileFamilyName:normalizedFamilyName
|
||||
// profileAvatarData:weakSelf.avatarData]
|
||||
//
|
||||
// .then(^{
|
||||
// OWSAssertIsOnMainThread();
|
||||
//
|
||||
// [modalActivityIndicator dismissWithCompletion:^{
|
||||
// [weakSelf updateProfileCompleted];
|
||||
// }];
|
||||
// })
|
||||
// .catch(^(NSError *error) {
|
||||
// OWSAssertIsOnMainThread();
|
||||
// OWSFailDebug(@"Error: %@", error);
|
||||
//
|
||||
// [modalActivityIndicator dismissWithCompletion:^{
|
||||
// // Don't show an error alert; the profile update
|
||||
// // is enqueued and will be completed later.
|
||||
// [weakSelf updateProfileCompleted];
|
||||
// }];
|
||||
// });
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)normalizedGivenName
|
||||
{
|
||||
return [self.givenNameTextField.text ows_stripped];
|
||||
}
|
||||
|
||||
- (NSString *)normalizedFamilyName
|
||||
{
|
||||
return [self.familyNameTextField.text ows_stripped];
|
||||
}
|
||||
|
||||
- (void)updateProfileCompleted
|
||||
{
|
||||
OWSLogVerbose(@"");
|
||||
|
||||
[self profileCompleted];
|
||||
}
|
||||
|
||||
- (void)profileCompleted
|
||||
{
|
||||
OWSLogVerbose(@"");
|
||||
self.completionHandler(self);
|
||||
}
|
||||
|
||||
- (void)showConversationSplitView
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
OWSLogVerbose(@"");
|
||||
|
||||
[SignalApp.sharedApp showConversationSplitView];
|
||||
}
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
|
||||
- (UITextField *)firstTextField
|
||||
{
|
||||
return NSLocale.currentLocale.isCJKV ? self.familyNameTextField : self.givenNameTextField;
|
||||
}
|
||||
|
||||
- (UITextField *)secondTextField
|
||||
{
|
||||
return NSLocale.currentLocale.isCJKV ? self.givenNameTextField : self.familyNameTextField;
|
||||
}
|
||||
|
||||
- (BOOL)textField:(UITextField *)textField
|
||||
shouldChangeCharactersInRange:(NSRange)editingRange
|
||||
replacementString:(NSString *)insertionText
|
||||
{
|
||||
// TODO: Possibly filter invalid input.
|
||||
return [TextFieldHelper textField:textField
|
||||
shouldChangeCharactersInRange:editingRange
|
||||
replacementString:insertionText.withoutBidiControlCharacters
|
||||
byteLimit:OWSUserProfile.kNameDataLength];
|
||||
}
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField
|
||||
{
|
||||
if (textField == self.firstTextField) {
|
||||
[self.secondTextField becomeFirstResponder];
|
||||
} else {
|
||||
[textField resignFirstResponder];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)textFieldDidChange:(id)sender
|
||||
{
|
||||
self.hasUnsavedChanges = YES;
|
||||
|
||||
[self updateProfileNamePreview];
|
||||
|
||||
// TODO: Update length warning.
|
||||
}
|
||||
|
||||
#pragma mark - Avatar
|
||||
|
||||
- (void)setAvatarImage:(nullable UIImage *)avatarImage
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
NSData *_Nullable avatarData = nil;
|
||||
if (avatarImage != nil) {
|
||||
avatarData = [OWSProfileManager avatarDataForAvatarImage:avatarImage];
|
||||
}
|
||||
self.hasUnsavedChanges = ![NSObject isNullableObject:avatarData equalTo:_avatarData];
|
||||
_avatarData = avatarData;
|
||||
|
||||
[self updateAvatarView];
|
||||
}
|
||||
|
||||
- (NSUInteger)avatarSize
|
||||
{
|
||||
return 96;
|
||||
}
|
||||
|
||||
- (void)updateAvatarView
|
||||
{
|
||||
if (self.avatarData != nil) {
|
||||
self.avatarView.image = [UIImage imageWithData:self.avatarData];
|
||||
} else {
|
||||
self.avatarView.image =
|
||||
[[[OWSContactAvatarBuilder alloc] initForLocalUserWithDiameter:self.avatarSize] buildDefaultImage];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateProfileNamePreview
|
||||
{
|
||||
NSPersonNameComponents *components = [NSPersonNameComponents new];
|
||||
components.givenName = [self normalizedGivenName];
|
||||
components.familyName = [self normalizedFamilyName];
|
||||
|
||||
self.profileNamePreviewLabel.text =
|
||||
[NSPersonNameComponentsFormatter localizedStringFromPersonNameComponents:components style:0 options:0];
|
||||
}
|
||||
|
||||
- (void)updateUsername
|
||||
{
|
||||
NSString *_Nullable username = [OWSProfileManager.shared localUsername];
|
||||
if (username) {
|
||||
self.usernameLabel.text = [CommonFormats formatUsername:username];
|
||||
self.usernameLabel.textColor = Theme.primaryTextColor;
|
||||
} else {
|
||||
self.usernameLabel.text = NSLocalizedString(@"PROFILE_VIEW_CREATE_USERNAME",
|
||||
@"A string indicating that the user can create a username on the profile view.");
|
||||
self.usernameLabel.textColor = Theme.accentBlueColor;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)givenNameRowTapped:(UIGestureRecognizer *)sender
|
||||
{
|
||||
if (sender.state == UIGestureRecognizerStateRecognized) {
|
||||
[self.givenNameTextField becomeFirstResponder];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)familyNameRowTapped:(UIGestureRecognizer *)sender
|
||||
{
|
||||
if (sender.state == UIGestureRecognizerStateRecognized) {
|
||||
[self.familyNameTextField becomeFirstResponder];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)usernameRowTapped:(UIGestureRecognizer *)sender
|
||||
{
|
||||
UsernameViewController *usernameVC = [UsernameViewController new];
|
||||
if (self.profileViewMode == ProfileViewMode_Registration) {
|
||||
usernameVC.modalPresentation = YES;
|
||||
[self presentFormSheetViewController:[[OWSNavigationController alloc] initWithRootViewController:usernameVC]
|
||||
animated:YES
|
||||
completion:nil];
|
||||
} else {
|
||||
[self.navigationController pushViewController:usernameVC animated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)shouldShowUsernameRow
|
||||
{
|
||||
switch (self.profileViewMode) {
|
||||
case ProfileViewMode_ExperienceUpgrade:
|
||||
case ProfileViewMode_Registration:
|
||||
return false;
|
||||
case ProfileViewMode_AppSettings:
|
||||
return RemoteConfig.usernames;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)avatarViewTapped:(UIGestureRecognizer *)sender
|
||||
{
|
||||
if (sender.state == UIGestureRecognizerStateRecognized) {
|
||||
[self avatarTapped];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)infoRowTapped:(UIGestureRecognizer *)sender
|
||||
{
|
||||
if (sender.state == UIGestureRecognizerStateRecognized) {
|
||||
SFSafariViewController *safariVC = [[SFSafariViewController alloc]
|
||||
initWithURL:[NSURL URLWithString:@"https://support.signal.org/hc/articles/360007459591"]];
|
||||
[self presentViewController:safariVC animated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)saveButtonPressed
|
||||
{
|
||||
[self updatePressed];
|
||||
}
|
||||
|
||||
#pragma mark - AvatarViewHelperDelegate
|
||||
|
||||
+ (BOOL)shouldDisplayProfileViewOnLaunch
|
||||
{
|
||||
// Only nag until the user sets a profile _name_. Profile names are
|
||||
// recommended; profile avatars are optional.
|
||||
if ([OWSProfileManager shared].localGivenName.length > 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSTimeInterval kProfileNagFrequency = kDayInterval * 30;
|
||||
__block NSDate *_Nullable lastPresentedDate;
|
||||
[self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) {
|
||||
lastPresentedDate =
|
||||
[ProfileViewController.keyValueStore getDate:kProfileView_LastPresentedDate transaction:transaction];
|
||||
}];
|
||||
|
||||
return (!lastPresentedDate || fabs([lastPresentedDate timeIntervalSinceNow]) > kProfileNagFrequency);
|
||||
}
|
||||
|
||||
#pragma mark - AvatarViewHelperDelegate
|
||||
|
||||
- (nullable NSString *)avatarActionSheetTitle
|
||||
{
|
||||
return NSLocalizedString(
|
||||
@"PROFILE_VIEW_AVATAR_ACTIONSHEET_TITLE", @"Action Sheet title prompting the user for a profile avatar");
|
||||
}
|
||||
|
||||
- (void)avatarDidChange:(UIImage *)image
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
OWSAssertDebug(image);
|
||||
|
||||
[self setAvatarImage:[image resizedImageToFillPixelSize:CGSizeMake(kOWSProfileManager_MaxAvatarDiameter,
|
||||
kOWSProfileManager_MaxAvatarDiameter)]];
|
||||
}
|
||||
|
||||
- (UIViewController *)fromViewController
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)hasClearAvatarAction
|
||||
{
|
||||
return self.avatarData != nil;
|
||||
}
|
||||
|
||||
- (NSString *)clearAvatarActionLabel
|
||||
{
|
||||
return NSLocalizedString(@"PROFILE_VIEW_CLEAR_AVATAR", @"Label for action that clear's the user's profile avatar");
|
||||
}
|
||||
|
||||
- (void)clearAvatar
|
||||
{
|
||||
[self setAvatarImage:nil];
|
||||
}
|
||||
|
||||
#pragma mark - OWSNavigationView
|
||||
|
||||
- (BOOL)shouldCancelNavigationBack
|
||||
{
|
||||
BOOL result = self.hasUnsavedChanges;
|
||||
if (result) {
|
||||
[self leaveViewCheckingForUnsavedChanges];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma mark - Orientation
|
||||
|
||||
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
|
||||
{
|
||||
if (UIDevice.currentDevice.isIPad) {
|
||||
return UIInterfaceOrientationMaskAll;
|
||||
}
|
||||
|
||||
return (self.profileViewMode == ProfileViewMode_Registration ? UIInterfaceOrientationMaskPortrait
|
||||
: UIInterfaceOrientationMaskAllButUpsideDown);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -6,17 +6,17 @@ import Foundation
|
||||
import SafariServices
|
||||
import PromiseKit
|
||||
|
||||
//@objc
|
||||
//public enum ProfileViewMode: UInt {
|
||||
// case appSettings
|
||||
// case registration
|
||||
// case experienceUpgrade
|
||||
//}
|
||||
@objc
|
||||
public enum ProfileViewMode: UInt {
|
||||
case appSettings
|
||||
case registration
|
||||
case experienceUpgrade
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
public class ProfileViewController2: OWSTableViewController {
|
||||
public class ProfileViewController: OWSTableViewController {
|
||||
|
||||
private let avatarViewHelper = AvatarViewHelper()
|
||||
|
||||
@ -50,11 +50,11 @@ public class ProfileViewController2: OWSTableViewController {
|
||||
|
||||
private let mode: ProfileViewMode
|
||||
|
||||
private let completionHandler: (ProfileViewController2) -> Void
|
||||
private let completionHandler: (ProfileViewController) -> Void
|
||||
|
||||
@objc
|
||||
public required init(mode: ProfileViewMode,
|
||||
completionHandler: @escaping (ProfileViewController2) -> Void) {
|
||||
completionHandler: @escaping (ProfileViewController) -> Void) {
|
||||
|
||||
self.mode = mode
|
||||
self.completionHandler = completionHandler
|
||||
@ -77,7 +77,7 @@ public class ProfileViewController2: OWSTableViewController {
|
||||
|
||||
// MARK: -
|
||||
|
||||
private static let keyValueStore = SDSKeyValueStore(collection: "kProfileView_Collection")
|
||||
public static let keyValueStore = SDSKeyValueStore(collection: "kProfileView_Collection")
|
||||
|
||||
private static let lastPresentedDateKey = "kProfileView_LastPresentedDate"
|
||||
|
||||
@ -285,13 +285,13 @@ public class ProfileViewController2: OWSTableViewController {
|
||||
label.font = .ows_dynamicTypeCaption1Clamped
|
||||
let attributedText = NSMutableAttributedString()
|
||||
attributedText.append(NSLocalizedString("PROFILE_VIEW_PROFILE_DESCRIPTION",
|
||||
comment: "Description of the user profile."))
|
||||
comment: "Description of the user profile."))
|
||||
attributedText.append(" ")
|
||||
attributedText.append(NSAttributedString(string: CommonStrings.learnMore,
|
||||
attributes: [
|
||||
attributes: [
|
||||
NSAttributedString.Key.foregroundColor: Theme.accentBlueColor,
|
||||
NSAttributedString.Key.underlineStyle: 0
|
||||
]))
|
||||
]))
|
||||
label.attributedText = attributedText
|
||||
label.numberOfLines = 0
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
@ -319,12 +319,12 @@ public class ProfileViewController2: OWSTableViewController {
|
||||
|
||||
let buttonHeight: CGFloat = 47
|
||||
let button = OWSFlatButton.button(title: NSLocalizedString("PROFILE_VIEW_SAVE_BUTTON",
|
||||
comment: "Button to save the profile view in the profile view."),
|
||||
font: OWSFlatButton.fontForHeight(buttonHeight),
|
||||
titleColor: UIColor.ows_white,
|
||||
backgroundColor: UIColor.ows_accentBlue,
|
||||
target: target,
|
||||
selector: selector)
|
||||
comment: "Button to save the profile view in the profile view."),
|
||||
font: OWSFlatButton.fontForHeight(buttonHeight),
|
||||
titleColor: UIColor.ows_white,
|
||||
backgroundColor: UIColor.ows_accentBlue,
|
||||
target: target,
|
||||
selector: selector)
|
||||
button.accessibilityIdentifier = "save_button"
|
||||
button.autoSetDimension(.height, toSize: buttonHeight)
|
||||
cell.contentView.addSubview(button)
|
||||
@ -333,7 +333,7 @@ public class ProfileViewController2: OWSTableViewController {
|
||||
cell.backgroundColor = Theme.tableViewBackgroundColor
|
||||
cell.contentView.backgroundColor = Theme.tableViewBackgroundColor
|
||||
|
||||
return cell
|
||||
return cell
|
||||
},
|
||||
actionBlock: { [weak self] in
|
||||
self?.updateProfile()
|
||||
@ -484,7 +484,7 @@ public class ProfileViewController2: OWSTableViewController {
|
||||
} else {
|
||||
saveButton.setEnabled(false)
|
||||
saveButton.setBackgroundColors(upColor: UIColor.ows_accentBlue.blended(with: Theme.backgroundColor,
|
||||
alpha: 0.5))
|
||||
alpha: 0.5))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -503,19 +503,19 @@ public class ProfileViewController2: OWSTableViewController {
|
||||
|
||||
if profileManager.isProfileNameTooLong(normalizedGivenName) {
|
||||
OWSActionSheets.showErrorAlert(message: NSLocalizedString("PROFILE_VIEW_ERROR_GIVEN_NAME_TOO_LONG",
|
||||
comment: "Error message shown when user tries to update profile with a given name that is too long."))
|
||||
comment: "Error message shown when user tries to update profile with a given name that is too long."))
|
||||
return
|
||||
}
|
||||
|
||||
if profileManager.isProfileNameTooLong(normalizedFamilyName) {
|
||||
OWSActionSheets.showErrorAlert(message: NSLocalizedString("PROFILE_VIEW_ERROR_FAMILY_NAME_TOO_LONG",
|
||||
comment: "Error message shown when user tries to update profile with a family name that is too long."))
|
||||
comment: "Error message shown when user tries to update profile with a family name that is too long."))
|
||||
return
|
||||
}
|
||||
|
||||
if !self.reachabilityManager.isReachable {
|
||||
OWSActionSheets.showErrorAlert(message: NSLocalizedString("PROFILE_VIEW_NO_CONNECTION",
|
||||
comment: "Error shown when the user tries to update their profile when the app is not connected to the internet."))
|
||||
comment: "Error shown when the user tries to update their profile when the app is not connected to the internet."))
|
||||
return
|
||||
}
|
||||
|
||||
@ -641,7 +641,7 @@ public class ProfileViewController2: OWSTableViewController {
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension ProfileViewController2: AvatarViewHelperDelegate {
|
||||
extension ProfileViewController: AvatarViewHelperDelegate {
|
||||
|
||||
public func avatarActionSheetTitle() -> String? {
|
||||
NSLocalizedString("PROFILE_VIEW_AVATAR_ACTIONSHEET_TITLE", comment: "Action Sheet title prompting the user for a profile avatar")
|
||||
@ -672,7 +672,7 @@ extension ProfileViewController2: AvatarViewHelperDelegate {
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension ProfileViewController2: UITextFieldDelegate {
|
||||
extension ProfileViewController: UITextFieldDelegate {
|
||||
|
||||
private var firstTextField: UITextField {
|
||||
NSLocale.current.isCJKV ? familyNameTextField : givenNameTextField
|
||||
@ -712,7 +712,7 @@ extension ProfileViewController2: UITextFieldDelegate {
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension ProfileViewController2: OWSNavigationView {
|
||||
extension ProfileViewController: OWSNavigationView {
|
||||
|
||||
public func shouldCancelNavigationBack() -> Bool {
|
||||
let result = hasUnsavedChanges
|
||||
|
||||
@ -80,7 +80,7 @@ public class AppEnvironment: NSObject {
|
||||
|
||||
YDBToGRDBMigration.add(keyStore: backup.keyValueStore, label: "backup")
|
||||
YDBToGRDBMigration.add(keyStore: AppUpdateNag.shared.keyValueStore, label: "AppUpdateNag")
|
||||
YDBToGRDBMigration.add(keyStore: ProfileViewController.keyValueStore(), label: "ProfileViewController")
|
||||
YDBToGRDBMigration.add(keyStore: ProfileViewController.keyValueStore, label: "ProfileViewController")
|
||||
}
|
||||
|
||||
@objc
|
||||
|
||||
Loading…
Reference in New Issue
Block a user