From 5e79b4eeee29d91dab8dd752544f1ee7f796a691 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Fri, 25 Sep 2020 16:20:42 +1000 Subject: [PATCH] Implement basic closed group editing UI --- Signal.xcodeproj/project.pbxproj | 8 + .../View Controllers/EditClosedGroupVC.swift | 272 ++++++++++++++++++ .../src/Loki/View Controllers/SeedModal.swift | 2 +- .../OWSConversationSettingsViewController.m | 51 ++-- .../translations/en.lproj/Localizable.strings | 3 - .../Redesign/Utilities/UIView+Utilities.swift | 22 ++ 6 files changed, 333 insertions(+), 25 deletions(-) create mode 100644 Signal/src/Loki/View Controllers/EditClosedGroupVC.swift create mode 100644 SignalMessaging/Loki/Redesign/Utilities/UIView+Utilities.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 75c4dedf1..fe2ab3172 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -568,6 +568,7 @@ BFF3FB9730634F37D25903F4 /* Pods_Signal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D17BB5C25D615AB49813100C /* Pods_Signal.framework */; }; C31A6C5A247F214E001123EF /* UIView+Glow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31A6C59247F214E001123EF /* UIView+Glow.swift */; }; C31A6C5C247F2CF3001123EF /* CGRect+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31A6C5B247F2CF3001123EF /* CGRect+Utilities.swift */; }; + C31D1DD325216101005D4DA8 /* UIView+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DD225216101005D4DA8 /* UIView+Utilities.swift */; }; C329FEEC24F7277900B1C64C /* LightModeSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C329FEEB24F7277900B1C64C /* LightModeSheet.swift */; }; C329FEEF24F7743F00B1C64C /* UIViewController+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C329FEED24F7742E00B1C64C /* UIViewController+Utilities.swift */; }; C34C8F7423A7830B00D82669 /* SpaceMono-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C34C8F7323A7830A00D82669 /* SpaceMono-Bold.ttf */; }; @@ -596,6 +597,7 @@ C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; }; C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */; }; C3DFFAC823E970080058DAF8 /* OpenGroupSuggestionSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC723E970080058DAF8 /* OpenGroupSuggestionSheet.swift */; }; + C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */; }; CC875800737563D6891B741D /* Pods_SignalTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 748A5CAEDD7C919FC64C6807 /* Pods_SignalTests.framework */; }; D202868116DBE0E7009068E9 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2AEACDB16C426DA00C364C0 /* CFNetwork.framework */; }; D202868216DBE0F4009068E9 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2179CFD16BB0B480006F3AB /* SystemConfiguration.framework */; }; @@ -1362,6 +1364,7 @@ B9EB5ABC1884C002007CBB57 /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; }; C31A6C59247F214E001123EF /* UIView+Glow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Glow.swift"; sourceTree = ""; }; C31A6C5B247F2CF3001123EF /* CGRect+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGRect+Utilities.swift"; sourceTree = ""; }; + C31D1DD225216101005D4DA8 /* UIView+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Utilities.swift"; sourceTree = ""; }; C329FEEB24F7277900B1C64C /* LightModeSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightModeSheet.swift; sourceTree = ""; }; C329FEED24F7742E00B1C64C /* UIViewController+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Utilities.swift"; sourceTree = ""; }; C34C8F7323A7830A00D82669 /* SpaceMono-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SpaceMono-Bold.ttf"; sourceTree = ""; }; @@ -1394,6 +1397,7 @@ C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCopyableLabel.swift; sourceTree = ""; }; C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sheet.swift; sourceTree = ""; }; C3DFFAC723E970080058DAF8 /* OpenGroupSuggestionSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupSuggestionSheet.swift; sourceTree = ""; }; + C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditClosedGroupVC.swift; sourceTree = ""; }; D17BB5C25D615AB49813100C /* Pods_Signal.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Signal.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2179CFB16BB0B3A0006F3AB /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; }; D2179CFD16BB0B480006F3AB /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; @@ -2654,6 +2658,7 @@ B8544E3023D16CA500299F14 /* DeviceUtilities.swift */, C329FEED24F7742E00B1C64C /* UIViewController+Utilities.swift */, B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */, + C31D1DD225216101005D4DA8 /* UIView+Utilities.swift */, ); path = Utilities; sourceTree = ""; @@ -2709,6 +2714,7 @@ B80C6B582384C4E700FDBC8B /* DeviceNameModal.swift */, B80C6B5A2384C7F900FDBC8B /* DeviceNameModalDelegate.swift */, B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */, + C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */, B88847BB23E10BC6009836D2 /* GroupMembersVC.swift */, B8BB82A4238F627000BA5194 /* HomeVC.swift */, B8CCF63E23975CFB0091D419 /* JoinPublicChatVC.swift */, @@ -3705,6 +3711,7 @@ 4541B71D209D3B7A0008608F /* ContactShareViewModel.swift in Sources */, 4C618199219DF03A009BD6B5 /* OWSButton.swift in Sources */, 34AC09F3211B39B100997B47 /* NewNonContactConversationViewController.m in Sources */, + C31D1DD325216101005D4DA8 /* UIView+Utilities.swift in Sources */, 4C3E245C21F29FCE000AE092 /* Toast.swift in Sources */, 34BBC84B220B2CB200857249 /* ImageEditorTextViewController.swift in Sources */, 34AC09FA211B39B100997B47 /* SharingThreadPickerViewController.m in Sources */, @@ -3770,6 +3777,7 @@ 45C9DEB81DF4E35A0065CA84 /* WebRTCCallMessageHandler.swift in Sources */, 34D1F0501F7D45A60066283D /* GifPickerCell.swift in Sources */, 3496957421A301A100DCFE74 /* OWSBackupAPI.swift in Sources */, + C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */, 34D99C931F2937CC00D284D6 /* OWSAnalytics.swift in Sources */, B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */, B80C6B5B2384C7F900FDBC8B /* DeviceNameModalDelegate.swift in Sources */, diff --git a/Signal/src/Loki/View Controllers/EditClosedGroupVC.swift b/Signal/src/Loki/View Controllers/EditClosedGroupVC.swift new file mode 100644 index 000000000..09dc0b1eb --- /dev/null +++ b/Signal/src/Loki/View Controllers/EditClosedGroupVC.swift @@ -0,0 +1,272 @@ + +@objc(LKEditClosedGroupVC) +final class EditClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegate { + private let thread: TSGroupThread + private var isEditingGroupName = false { didSet { handleIsEditingGroupNameChanged() } } + + private lazy var members: [String] = { + func getDisplayName(for hexEncodedPublicKey: String) -> String { + return UserDisplayNameUtilities.getPrivateChatDisplayName(for: hexEncodedPublicKey) ?? hexEncodedPublicKey + } + return GroupUtilities.getClosedGroupMembers(thread).sorted { getDisplayName(for: $0) < getDisplayName(for: $1) } + }() + + // MARK: Components + private lazy var groupNameLabel: UILabel = { + let result = UILabel() + result.textColor = Colors.text + result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize) + result.lineBreakMode = .byTruncatingTail + result.textAlignment = .center + return result + }() + + private lazy var groupNameTextField: TextField = { + let result = TextField(placeholder: "Enter a group name", usesDefaultHeight: false) + result.textAlignment = .center + return result + }() + + @objc private lazy var tableView: UITableView = { + let result = UITableView() + result.dataSource = self + result.delegate = self + result.register(Cell.self, forCellReuseIdentifier: "Cell") + result.separatorStyle = .none + result.backgroundColor = .clear + result.isScrollEnabled = false + return result + }() + + // MARK: Lifecycle + @objc(initWithThreadID:) + init(with threadID: String) { + var thread: TSGroupThread! + Storage.read { transaction in + thread = TSGroupThread.fetch(uniqueId: threadID, transaction: transaction)! + } + self.thread = thread + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(with:) instead.") + } + + override func viewDidLoad() { + super.viewDidLoad() + setUpGradientBackground() + setUpNavBarStyle() + setNavBarTitle("Edit Group") + let backButton = UIBarButtonItem(title: "Back", style: .plain, target: nil, action: nil) + backButton.tintColor = Colors.text + navigationItem.backBarButtonItem = backButton + setUpViewHierarchy() + updateNavigationBarButtons() + } + + private func setUpViewHierarchy() { + // Group name container + groupNameLabel.text = thread.groupModel.groupName + let groupNameContainer = UIView() + groupNameContainer.addSubview(groupNameLabel) + groupNameLabel.pin(to: groupNameContainer) + groupNameContainer.addSubview(groupNameTextField) + groupNameTextField.pin(to: groupNameContainer) + groupNameContainer.set(.height, to: 40) + groupNameTextField.alpha = 0 + // Top container + let topContainer = UIView() + topContainer.addSubview(groupNameContainer) + groupNameContainer.center(in: topContainer) + topContainer.set(.height, to: 40) + let topContainerTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(showEditGroupNameUI)) + topContainer.addGestureRecognizer(topContainerTapGestureRecognizer) + // Members label + let membersLabel = UILabel() + membersLabel.textColor = Colors.text + membersLabel.font = .systemFont(ofSize: Values.mediumFontSize) + membersLabel.text = "Members" + // Add members button + let addMembersButton = Button(style: .prominentOutline, size: .large) + addMembersButton.setTitle("Add Members", for: UIControl.State.normal) + addMembersButton.addTarget(self, action: #selector(addMembers), for: UIControl.Event.touchUpInside) + addMembersButton.contentEdgeInsets = UIEdgeInsets(top: 0, leading: Values.mediumSpacing, bottom: 0, trailing: Values.mediumSpacing) + // Middle stack view + let middleStackView = UIStackView(arrangedSubviews: [ membersLabel, addMembersButton ]) + middleStackView.axis = .horizontal + middleStackView.alignment = .center + middleStackView.layoutMargins = UIEdgeInsets(top: Values.smallSpacing, leading: Values.mediumSpacing, bottom: Values.smallSpacing, trailing: Values.mediumSpacing) + middleStackView.isLayoutMarginsRelativeArrangement = true + // Main stack view + let mainStackView = UIStackView(arrangedSubviews: [ + UIView.vSpacer(Values.veryLargeSpacing), + topContainer, + UIView.vSpacer(Values.veryLargeSpacing), + UIView.separator(), + middleStackView, + UIView.separator(), + tableView + ]) + mainStackView.axis = .vertical + mainStackView.alignment = .fill + mainStackView.set(.width, to: UIScreen.main.bounds.width) + // Scroll view + let scrollView = UIScrollView() + scrollView.showsVerticalScrollIndicator = false + scrollView.addSubview(mainStackView) + mainStackView.pin(to: scrollView) + view.addSubview(scrollView) + scrollView.pin(to: view) + mainStackView.pin(.bottom, to: .bottom, of: view) + } + + // MARK: Table View Data Source / Delegate + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return members.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! Cell + let publicKey = members[indexPath.row] + cell.publicKey = publicKey + return cell + } + + func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + let publicKey = members[indexPath.row] + return publicKey != getUserHexEncodedPublicKey() + } + + func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { + let removeAction = UITableViewRowAction(style: .destructive, title: "Remove") { [weak self] _, _ in + // TODO: Implement + } + removeAction.backgroundColor = Colors.destructive + return [ removeAction ] + } + + // MARK: Updating + private func updateNavigationBarButtons() { + if isEditingGroupName { + let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(handleCancelGroupNameEditingButtonTapped)) + cancelButton.tintColor = Colors.text + navigationItem.leftBarButtonItem = cancelButton + } else { + navigationItem.leftBarButtonItem = nil + } + let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleSaveGroupNameButtonTapped)) + doneButton.tintColor = Colors.text + navigationItem.rightBarButtonItem = doneButton + } + + private func handleIsEditingGroupNameChanged() { + updateNavigationBarButtons() + UIView.animate(withDuration: 0.25) { + self.groupNameLabel.alpha = self.isEditingGroupName ? 0 : 1 + self.groupNameTextField.alpha = self.isEditingGroupName ? 1 : 0 + } + if isEditingGroupName { + groupNameTextField.becomeFirstResponder() + } else { + groupNameTextField.resignFirstResponder() + } + } + + // MARK: Interaction + @objc private func showEditGroupNameUI() { + isEditingGroupName = true + } + + @objc private func handleCancelGroupNameEditingButtonTapped() { + isEditingGroupName = false + } + + @objc private func handleSaveGroupNameButtonTapped() { + isEditingGroupName = false + } + + @objc private func addMembers() { + + } + + +} + + + +// MARK: - Cell + +private extension EditClosedGroupVC { + + final class Cell : UITableViewCell { + var publicKey = "" { didSet { update() } } + + // MARK: Components + private lazy var profilePictureView = ProfilePictureView() + + private lazy var displayNameLabel: UILabel = { + let result = UILabel() + result.textColor = Colors.text + result.font = .boldSystemFont(ofSize: Values.mediumFontSize) + result.lineBreakMode = .byTruncatingTail + return result + }() + + private lazy var separator: UIView = { + let result = UIView() + result.backgroundColor = Colors.separator + result.set(.height, to: Values.separatorThickness) + return result + }() + + // MARK: Initialization + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setUpViewHierarchy() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setUpViewHierarchy() + } + + private func setUpViewHierarchy() { + // Set the cell background color + backgroundColor = Colors.cellBackground + // Set up the highlight color + let selectedBackgroundView = UIView() + selectedBackgroundView.backgroundColor = .clear // Disabled for now + self.selectedBackgroundView = selectedBackgroundView + // Set up the profile picture image view + let profilePictureViewSize = Values.smallProfilePictureSize + profilePictureView.set(.width, to: profilePictureViewSize) + profilePictureView.set(.height, to: profilePictureViewSize) + profilePictureView.size = profilePictureViewSize + // Set up the main stack view + let stackView = UIStackView(arrangedSubviews: [ profilePictureView, displayNameLabel ]) + stackView.axis = .horizontal + stackView.alignment = .center + stackView.spacing = Values.mediumSpacing + contentView.addSubview(stackView) + stackView.pin(.leading, to: .leading, of: contentView, withInset: Values.mediumSpacing) + stackView.pin(.top, to: .top, of: contentView, withInset: Values.mediumSpacing) + contentView.pin(.trailing, to: .trailing, of: stackView, withInset: Values.mediumSpacing) + contentView.pin(.bottom, to: .bottom, of: stackView, withInset: Values.mediumSpacing) + stackView.set(.width, to: UIScreen.main.bounds.width - 2 * Values.mediumSpacing) + // Set up the separator + contentView.addSubview(separator) + separator.pin(.leading, to: .leading, of: contentView) + contentView.pin(.trailing, to: .trailing, of: separator) + separator.pin(.bottom, to: .bottom, of: contentView) + separator.set(.width, to: UIScreen.main.bounds.width) + } + + // MARK: Updating + private func update() { + profilePictureView.hexEncodedPublicKey = publicKey + profilePictureView.update() + displayNameLabel.text = UserDisplayNameUtilities.getPrivateChatDisplayName(for: publicKey) ?? publicKey + } + } +} diff --git a/Signal/src/Loki/View Controllers/SeedModal.swift b/Signal/src/Loki/View Controllers/SeedModal.swift index 85de504b5..593a61d12 100644 --- a/Signal/src/Loki/View Controllers/SeedModal.swift +++ b/Signal/src/Loki/View Controllers/SeedModal.swift @@ -63,7 +63,7 @@ final class SeedModal : Modal { let disclaimerLabel = UILabel() disclaimerLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) disclaimerLabel.font = .systemFont(ofSize: 10) - disclaimerLabel.text = NSLocalizedString("modal_seed_disclaimer", comment: "") + disclaimerLabel.text = "It is not possible to use the same Session ID on multiple devices simultaneously" disclaimerLabel.numberOfLines = 0 disclaimerLabel.lineBreakMode = .byWordWrapping disclaimerLabel.textAlignment = .center diff --git a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m index 261e97db4..200f82321 100644 --- a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m +++ b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m @@ -651,34 +651,37 @@ const CGFloat kIconViewLength = 24; if (self.isGroupThread && self.isPrivateGroupChat) { -// [OWSTableItem + if (((TSGroupThread *)self.thread).usesSharedSenderKeys) { + [mainSection addItem:[OWSTableItem + itemWithCustomCellBlock:^{ + UITableViewCell *cell = + [weakSelf disclosureCellWithName:NSLocalizedString(@"EDIT_GROUP_ACTION", + @"table cell label in conversation settings") + iconName:@"table_ic_group_edit" + accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME( + OWSConversationSettingsViewController, @"edit_group")]; + cell.userInteractionEnabled = !weakSelf.hasLeftGroup; + return cell; + } + actionBlock:^{ + [weakSelf editGroup]; + }] + ]; + } +// [mainSection addItem:[OWSTableItem // itemWithCustomCellBlock:^{ // UITableViewCell *cell = -// [weakSelf disclosureCellWithName:NSLocalizedString(@"EDIT_GROUP_ACTION", +// [weakSelf disclosureCellWithName:NSLocalizedString(@"LIST_GROUP_MEMBERS_ACTION", // @"table cell label in conversation settings") -// iconName:@"table_ic_group_edit" +// iconName:@"table_ic_group_members" // accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME( -// OWSConversationSettingsViewController, @"edit_group")]; -// cell.userInteractionEnabled = !weakSelf.hasLeftGroup; +// OWSConversationSettingsViewController, @"group_members")]; // return cell; // } // actionBlock:^{ -// [weakSelf showUpdateGroupView:UpdateGroupMode_Default]; -// }], - [mainSection addItem:[OWSTableItem - itemWithCustomCellBlock:^{ - UITableViewCell *cell = - [weakSelf disclosureCellWithName:NSLocalizedString(@"LIST_GROUP_MEMBERS_ACTION", - @"table cell label in conversation settings") - iconName:@"table_ic_group_members" - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME( - OWSConversationSettingsViewController, @"group_members")]; - return cell; - } - actionBlock:^{ - [weakSelf showGroupMembersView]; - }] - ]; +// [weakSelf showGroupMembersView]; +// }] +// ]; NSString *userPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey; if ([((TSGroupThread *)self.thread).groupModel.groupMemberIds containsObject:userPublicKey]) { [mainSection addItem:[OWSTableItem @@ -1144,6 +1147,12 @@ const CGFloat kIconViewLength = 24; [self presentContactViewController]; } +- (void)editGroup +{ + LKEditClosedGroupVC *editClosedGroupVC = [[LKEditClosedGroupVC alloc] initWithThreadID:self.thread.uniqueId]; + [self.navigationController pushViewController:editClosedGroupVC animated:YES]; +} + - (void)didTapLeaveGroup { UIAlertController *alert = diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index d3a28f234..c3869fecf 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -2728,6 +2728,3 @@ "vc_contact_selection_contacts_title" = "Contacts"; "vc_contact_selection_closed_groups_title" = "Closed Groups"; "vc_contact_selection_open_groups_title" = "Open Groups"; - -// MARK: Next round of translation -"modal_seed_disclaimer" = "It is not possible to use the same Session ID on multiple devices simultaneously"; diff --git a/SignalMessaging/Loki/Redesign/Utilities/UIView+Utilities.swift b/SignalMessaging/Loki/Redesign/Utilities/UIView+Utilities.swift new file mode 100644 index 000000000..9048ba2e2 --- /dev/null +++ b/SignalMessaging/Loki/Redesign/Utilities/UIView+Utilities.swift @@ -0,0 +1,22 @@ + +public extension UIView { + + static func hSpacer(_ width: CGFloat) -> UIView { + let result = UIView() + result.set(.width, to: width) + return result + } + + static func vSpacer(_ height: CGFloat) -> UIView { + let result = UIView() + result.set(.height, to: height) + return result + } + + static func separator() -> UIView { + let result = UIView() + result.set(.height, to: Values.separatorThickness) + result.backgroundColor = Colors.separator + return result + } +}