Browse Source

Implement nicknames

pull/357/head
Niels Andriesse 9 months ago
parent
commit
cf07fc1b1a
  1. 4
      Session.xcodeproj/project.pbxproj
  2. 107
      Session/Conversations/Settings/OWSConversationSettingsViewController.m
  3. 6
      Session/Conversations/Views & Modals/ConversationTitleView.swift
  4. 10
      SessionMessagingKit/Database/Notification+Contacts.swift
  5. 3
      SessionMessagingKit/Database/Storage+Contacts.swift
  6. 2
      SessionSnodeKit/OnionRequestAPI.swift
  7. 6
      SessionUIKit/Components/TextField.swift

4
Session.xcodeproj/project.pbxproj

@ -285,6 +285,7 @@
B8D84ECF25E3108A005A043E /* ExpandingAttachmentsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D84ECE25E3108A005A043E /* ExpandingAttachmentsButton.swift */; };
B8F5F52925EC4F8A003BF8D4 /* BlockListUIUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = B8F5F52825EC4F8A003BF8D4 /* BlockListUIUtils.m */; };
B8F5F54E25EC50A5003BF8D4 /* BlockListUIUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = B8F5F52725EC4F6A003BF8D4 /* BlockListUIUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
B8F5F56525EC8453003BF8D4 /* Notification+Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8F5F56425EC8453003BF8D4 /* Notification+Contacts.swift */; };
B8FF8DAE25C0D00F004D1F22 /* SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A6F025539DE700C340D1 /* SessionMessagingKit.framework */; };
B8FF8DAF25C0D00F004D1F22 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; };
B8FF8E6225C10DA5004D1F22 /* GeoLite2-Country-Blocks-IPv4 in Resources */ = {isa = PBXBuildFile; fileRef = B8FF8E6125C10DA5004D1F22 /* GeoLite2-Country-Blocks-IPv4 */; };
@ -1279,6 +1280,7 @@
B8D8F1EF256621180092EF10 /* MessageSender+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "MessageSender+Convenience.swift"; path = "../../SignalUtilitiesKit/Messaging/Sending & Receiving/MessageSender+Convenience.swift"; sourceTree = "<group>"; };
B8F5F52725EC4F6A003BF8D4 /* BlockListUIUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BlockListUIUtils.h; sourceTree = "<group>"; };
B8F5F52825EC4F8A003BF8D4 /* BlockListUIUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BlockListUIUtils.m; sourceTree = "<group>"; };
B8F5F56425EC8453003BF8D4 /* Notification+Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Contacts.swift"; sourceTree = "<group>"; };
B8FF8E6125C10DA5004D1F22 /* GeoLite2-Country-Blocks-IPv4 */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; name = "GeoLite2-Country-Blocks-IPv4"; path = "Countries/GeoLite2-Country-Blocks-IPv4"; sourceTree = "<group>"; };
B8FF8E7325C10FC3004D1F22 /* GeoLite2-Country-Locations-English */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; name = "GeoLite2-Country-Locations-English"; path = "Countries/GeoLite2-Country-Locations-English"; sourceTree = "<group>"; };
B8FF8EA525C11FEF004D1F22 /* IPv4.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPv4.swift; sourceTree = "<group>"; };
@ -2627,6 +2629,7 @@
C32C5BCB256DC818003C73A2 /* Database */ = {
isa = PBXGroup;
children = (
B8F5F56425EC8453003BF8D4 /* Notification+Contacts.swift */,
C33FDAEA255A580500E217F9 /* OWSBackupFragment.h */,
C33FDB07255A580700E217F9 /* OWSBackupFragment.m */,
C33FDA67255A57F900E217F9 /* OWSPrimaryStorage.h */,
@ -4797,6 +4800,7 @@
B8856E94256F1C37001CE70E /* OWSSounds.m in Sources */,
C32C5BCC256DC830003C73A2 /* Storage+ClosedGroups.swift in Sources */,
C3A3A0EC256E1949004D228D /* OWSRecipientIdentity.m in Sources */,
B8F5F56525EC8453003BF8D4 /* Notification+Contacts.swift in Sources */,
C32C5AB2256DBE8F003C73A2 /* TSMessage.m in Sources */,
C3A3A0FE256E1A3C004D228D /* TSDatabaseSecondaryIndexes.m in Sources */,
C38D5E8D2575011E00B6A65C /* MessageSender+ClosedGroups.swift in Sources */,

107
Session/Conversations/Settings/OWSConversationSettingsViewController.m

@ -13,7 +13,6 @@
#import <SessionMessagingKit/Environment.h>
#import <SignalUtilitiesKit/OWSProfileManager.h>
#import <SessionMessagingKit/OWSSounds.h>
#import <SessionMessagingKit/OWSUserProfile.h>
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
#import <SignalUtilitiesKit/UIUtil.h>
#import <SessionMessagingKit/OWSDisappearingConfigurationUpdateInfoMessage.h>
@ -41,6 +40,10 @@ CGFloat kIconViewLength = 24;
@property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper;
@property (nonatomic, readonly) UIImageView *avatarView;
@property (nonatomic, readonly) UILabel *disappearingMessagesDurationLabel;
@property (nonatomic) UILabel *displayNameLabel;
@property (nonatomic) SNTextField *displayNameTextField;
@property (nonatomic) UIView *displayNameContainer;
@property (nonatomic) BOOL isEditingDisplayName;
@end
@ -209,6 +212,32 @@ CGFloat kIconViewLength = 24;
{
[super viewDidLoad];
self.displayNameLabel = [UILabel new];
self.displayNameLabel.textColor = LKColors.text;
self.displayNameLabel.font = [UIFont boldSystemFontOfSize:LKValues.largeFontSize];
self.displayNameLabel.lineBreakMode = NSLineBreakByTruncatingTail;
self.displayNameLabel.textAlignment = NSTextAlignmentCenter;
self.displayNameTextField = [[SNTextField alloc] initWithPlaceholder:@"Enter a name" usesDefaultHeight:NO];
self.displayNameTextField.textAlignment = NSTextAlignmentCenter;
self.displayNameTextField.accessibilityLabel = @"Edit name text field";
self.displayNameTextField.alpha = 0;
self.displayNameContainer = [UIView new];
self.displayNameContainer.accessibilityLabel = @"Edit name text field";
self.displayNameContainer.isAccessibilityElement = YES;
[self.displayNameContainer autoSetDimension:ALDimensionHeight toSize:40];
[self.displayNameContainer addSubview:self.displayNameLabel];
[self.displayNameLabel autoPinToEdgesOfView:self.displayNameContainer];
[self.displayNameContainer addSubview:self.displayNameTextField];
[self.displayNameTextField autoPinToEdgesOfView:self.displayNameContainer];
if ([self.thread isKindOfClass:TSContactThread.class]) {
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showEditNameUI)];
[self.displayNameContainer addGestureRecognizer:tapGestureRecognizer];
}
self.tableView.estimatedRowHeight = 45;
self.tableView.rowHeight = UITableViewAutomaticDimension;
@ -683,13 +712,13 @@ CGFloat kIconViewLength = 24;
[profilePictureView autoSetDimension:ALDimensionHeight toSize:size];
[profilePictureView addGestureRecognizer:profilePictureTapGestureRecognizer];
UILabel *titleView = [UILabel new];
titleView.textColor = LKColors.text;
titleView.font = [UIFont boldSystemFontOfSize:LKValues.largeFontSize];
titleView.lineBreakMode = NSLineBreakByTruncatingTail;
titleView.text = (self.threadName != nil && self.threadName.length > 0) ? self.threadName : @"Anonymous";
self.displayNameLabel.text = (self.threadName != nil && self.threadName.length > 0) ? self.threadName : @"Anonymous";
if ([self.thread isKindOfClass:TSContactThread.class]) {
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showEditNameUI)];
[self.displayNameContainer addGestureRecognizer:tapGestureRecognizer];
}
UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:@[ profilePictureView, titleView ]];
UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:@[ profilePictureView, self.displayNameContainer ]];
stackView.axis = UILayoutConstraintAxisVertical;
stackView.spacing = LKValues.mediumSpacing;
stackView.distribution = UIStackViewDistributionEqualCentering;
@ -1072,6 +1101,70 @@ CGFloat kIconViewLength = 24;
[self.conversationSettingsViewDelegate conversationSettingsDidRequestConversationSearch:self];
}
- (void)hideEditNameUI
{
self.isEditingDisplayName = NO;
}
- (void)showEditNameUI
{
self.isEditingDisplayName = YES;
}
- (void)setIsEditingDisplayName:(BOOL)isEditingDisplayName
{
_isEditingDisplayName = isEditingDisplayName;
[self updateNavBarButtons];
[UIView animateWithDuration:0.25 animations:^{
self.displayNameLabel.alpha = self.isEditingDisplayName ? 0 : 1;
self.displayNameTextField.alpha = self.isEditingDisplayName ? 1 : 0;
}];
if (self.isEditingDisplayName) {
[self.displayNameTextField becomeFirstResponder];
} else {
[self.displayNameTextField resignFirstResponder];
}
}
- (void)saveName
{
if (![self.thread isKindOfClass:TSContactThread.class]) { return; }
SNContact *contact = [LKStorage.shared getContactWithSessionID:self.thread.contactIdentifier];
if (contact == nil) { return; }
NSString *text = [self.displayNameTextField.text stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
contact.nickname = text.length > 0 ? text : nil;
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[LKStorage.shared setContact:contact usingTransaction:transaction];
}];
self.displayNameLabel.text = text.length > 0 ? text : contact.name;
[self hideEditNameUI];
}
- (void)updateNavBarButtons
{
if (self.isEditingDisplayName) {
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(hideEditNameUI)];
cancelButton.tintColor = LKColors.text;
cancelButton.accessibilityLabel = @"Cancel button";
cancelButton.isAccessibilityElement = YES;
self.navigationItem.leftBarButtonItem = cancelButton;
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(saveName)];
doneButton.tintColor = LKColors.text;
doneButton.accessibilityLabel = @"Done button";
doneButton.isAccessibilityElement = YES;
self.navigationItem.rightBarButtonItem = doneButton;
} else {
self.navigationItem.leftBarButtonItem = nil;
UIBarButtonItem *editButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:@selector(showEditNameUI)];
editButton.tintColor = LKColors.text;
editButton.accessibilityLabel = @"Done button";
editButton.isAccessibilityElement = YES;
self.navigationItem.rightBarButtonItem = editButton;
}
}
#pragma mark - Notifications
- (void)identityStateDidChange:(NSNotification *)notification

6
Session/Conversations/Views & Modals/ConversationTitleView.swift

@ -46,8 +46,10 @@ final class ConversationTitleView : UIView {
stackView.layoutMargins = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 0)
addSubview(stackView)
stackView.pin(to: self)
NotificationCenter.default.addObserver(self, selector: #selector(update), name: Notification.Name.groupThreadUpdated, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(update), name: Notification.Name.muteSettingUpdated, object: nil)
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(update), name: Notification.Name.groupThreadUpdated, object: nil)
notificationCenter.addObserver(self, selector: #selector(update), name: Notification.Name.muteSettingUpdated, object: nil)
notificationCenter.addObserver(self, selector: #selector(update), name: Notification.Name.contactUpdated, object: nil)
update()
}

10
SessionMessagingKit/Database/Notification+Contacts.swift

@ -0,0 +1,10 @@
public extension Notification.Name {
static let contactUpdated = Notification.Name("contactUpdated")
}
@objc public extension NSNotification {
@objc static let contactUpdated = Notification.Name.contactUpdated.rawValue as NSString
}

3
SessionMessagingKit/Database/Storage+Contacts.swift

@ -15,6 +15,9 @@ extension Storage {
@objc(setContact:usingTransaction:)
public func setContact(_ contact: Contact, using transaction: Any) {
(transaction as! YapDatabaseReadWriteTransaction).setObject(contact, forKey: contact.sessionID, inCollection: Storage.contactCollection)
DispatchQueue.main.async {
NotificationCenter.default.post(name: .contactUpdated, object: contact.sessionID)
}
}
public func getAllContacts() -> Set<Contact> {

2
SessionSnodeKit/OnionRequestAPI.swift

@ -285,7 +285,7 @@ public enum OnionRequestAPI {
}.map2 { _ in (guardSnode, encryptionResult, targetSnodeSymmetricKey) }
}
// MARK: Internal API
// MARK: Public API
/// Sends an onion request to `snode`. Builds new paths as needed.
public static func sendOnionRequest(to snode: Snode, invoking method: Snode.Method, with parameters: JSON, associatedWith publicKey: String) -> Promise<JSON> {
let payload: JSON = [ "method" : method.rawValue, "params" : parameters ]

6
SessionUIKit/Components/TextField.swift

@ -1,5 +1,6 @@
import UIKit
@objc(SNTextField)
public final class TextField : UITextField {
private let usesDefaultHeight: Bool
private let height: CGFloat
@ -9,6 +10,11 @@ public final class TextField : UITextField {
static let height: CGFloat = isIPhone5OrSmaller ? CGFloat(48) : CGFloat(80)
public static let cornerRadius: CGFloat = 8
@objc(initWithPlaceholder:usesDefaultHeight:)
public convenience init(placeholder: String, usesDefaultHeight: Bool) {
self.init(placeholder: placeholder, usesDefaultHeight: usesDefaultHeight, customHeight: nil, customHorizontalInset: nil, customVerticalInset: nil)
}
public init(placeholder: String, usesDefaultHeight: Bool = true, customHeight: CGFloat? = nil, customHorizontalInset: CGFloat? = nil, customVerticalInset: CGFloat? = nil) {
self.usesDefaultHeight = usesDefaultHeight
self.height = customHeight ?? TextField.height

Loading…
Cancel
Save