diff --git a/Session/Conversations/ConversationVC.swift b/Session/Conversations/ConversationVC.swift index f49bec425..c74aba0d2 100644 --- a/Session/Conversations/ConversationVC.swift +++ b/Session/Conversations/ConversationVC.swift @@ -126,7 +126,7 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat view.addSubview(messagesTableView) messagesTableView.pin(to: view) view.addSubview(scrollButton) - scrollButton.pin(.right, to: .right, of: view, withInset: -22) + scrollButton.pin(.right, to: .right, of: view, withInset: -16) // Blocked banner addOrRemoveBlockedBanner() // Notifications @@ -207,7 +207,7 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat guard let newHeight = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height else { return } if !didConstrainScrollButton { // Bit of a hack to do this here, but it works out. - scrollButton.pin(.bottom, to: .bottom, of: view, withInset: -(newHeight + 22)) + scrollButton.pin(.bottom, to: .bottom, of: view, withInset: -(newHeight + 16)) didConstrainScrollButton = true } UIView.animate(withDuration: 0.25) { diff --git a/Session/Conversations/Input View/InputView.swift b/Session/Conversations/Input View/InputView.swift index 4100d7221..9340b00e8 100644 --- a/Session/Conversations/Input View/InputView.swift +++ b/Session/Conversations/Input View/InputView.swift @@ -104,7 +104,7 @@ final class InputView : UIView, InputViewButtonDelegate, InputTextViewDelegate, mainStackView.axis = .vertical mainStackView.isLayoutMarginsRelativeArrangement = true let adjustment = (InputViewButton.expandedSize - InputViewButton.size) / 2 - mainStackView.layoutMargins = UIEdgeInsets(top: Values.smallSpacing, leading: Values.largeSpacing, bottom: Values.smallSpacing, trailing: Values.largeSpacing - adjustment) + mainStackView.layoutMargins = UIEdgeInsets(top: 2, leading: Values.mediumSpacing, bottom: 2, trailing: Values.mediumSpacing - adjustment) addSubview(mainStackView) mainStackView.pin(.top, to: .bottom, of: separator) mainStackView.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing ], to: self) diff --git a/Session/Conversations/Input View/InputViewButton.swift b/Session/Conversations/Input View/InputViewButton.swift index 31a08dff7..10a26fbc0 100644 --- a/Session/Conversations/Input View/InputViewButton.swift +++ b/Session/Conversations/Input View/InputViewButton.swift @@ -46,6 +46,9 @@ final class InputViewButton : UIView { let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .regular)) addSubview(blurView) blurView.pin(to: self) + layer.borderWidth = Values.separatorThickness + let borderColor = (isLightMode ? UIColor.black : UIColor.white).withAlphaComponent(Values.veryLowOpacity) + layer.borderColor = borderColor.cgColor } backgroundView.backgroundColor = isSendButton ? Colors.accent : Colors.text.withAlphaComponent(0.05) addSubview(backgroundView) diff --git a/Session/Conversations/Views & Modals/ScrollToBottomButton.swift b/Session/Conversations/Views & Modals/ScrollToBottomButton.swift index b5aaa9e84..cdaea97c1 100644 --- a/Session/Conversations/Views & Modals/ScrollToBottomButton.swift +++ b/Session/Conversations/Views & Modals/ScrollToBottomButton.swift @@ -38,7 +38,7 @@ final class ScrollToBottomButton : UIView { layer.cornerRadius = size / 2 layer.masksToBounds = true // Border - layer.borderWidth = 1 + layer.borderWidth = Values.separatorThickness let borderColor = (isLightMode ? UIColor.black : UIColor.white).withAlphaComponent(Values.veryLowOpacity) layer.borderColor = borderColor.cgColor // Icon diff --git a/Session/Device Linking/LinkDeviceVC.swift b/Session/Device Linking/LinkDeviceVC.swift index 67367fc6e..535704211 100644 --- a/Session/Device Linking/LinkDeviceVC.swift +++ b/Session/Device Linking/LinkDeviceVC.swift @@ -35,7 +35,7 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon }() private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = { - let message = "Bla bla foo bar" + let message = "Navigate to Settings → Recovery Phrase on your other device to show your QR code." let result = ScanQRCodeWrapperVC(message: message) result.delegate = self return result @@ -117,7 +117,8 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon } func controller(_ controller: OWSQRCodeScanningViewController, didDetectQRCodeWith string: String) { - print(string) + guard let seed = string.data(using: .utf8) else { return } + continueWithSeed(seed) } func continueWithSeed(_ seed: Data) { @@ -126,11 +127,7 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon TSAccountManager.sharedInstance().phoneNumberAwaitingVerification = x25519KeyPair.hexEncodedPublicKey OWSPrimaryStorage.shared().setRestorationTime(Date().timeIntervalSince1970) UserDefaults.standard[.hasViewedSeed] = true - UserDefaults.standard[.isUsingFullAPNs] = true // TODO: Get this from the sync message or show the PN mode screen TSAccountManager.sharedInstance().didRegister() - let syncTokensJob = SyncPushTokensJob(accountManager: AppEnvironment.shared.accountManager, preferences: Environment.shared.preferences) - syncTokensJob.uploadOnlyIfStale = false - let _: Promise = syncTokensJob.run() NotificationCenter.default.addObserver(self, selector: #selector(handleConfigurationMessageReceived), name: .configurationMessageReceived, object: nil) ModalActivityIndicatorViewController.present(fromViewController: navigationController!) { [weak self] modal in self?.activityIndicatorModal = modal @@ -138,7 +135,12 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon } @objc private func handleConfigurationMessageReceived() { - + DispatchQueue.main.async { + self.navigationController!.dismiss(animated: true) { + let pnModeVC = PNModeVC() + self.navigationController!.setViewControllers([ pnModeVC ], animated: true) + } + } } } diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index 960435ec7..a7228dc5e 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -4,6 +4,7 @@ extension AppDelegate { @objc(syncConfigurationIfNeeded) func syncConfigurationIfNeeded() { + guard Storage.shared.getUserDisplayName() != nil else { return } let userDefaults = UserDefaults.standard let lastSync = userDefaults[.lastConfigurationSync] ?? .distantPast guard Date().timeIntervalSince(lastSync) > 2 * 24 * 60 * 60 else { return } // Sync every 2 days @@ -17,6 +18,7 @@ extension AppDelegate { } func forceSyncConfigurationNowIfNeeded() -> Promise { + guard Storage.shared.getUserDisplayName() != nil else { return Promise.value(()) } let configurationMessage = ConfigurationMessage.getCurrent() let destination = Message.Destination.contact(publicKey: getUserHexEncodedPublicKey()) let (promise, seal) = Promise.pending() diff --git a/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage.swift b/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage.swift index 65e9e1575..e9f5c6209 100644 --- a/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage.swift +++ b/SessionMessagingKit/Messages/Control Messages/ConfigurationMessage.swift @@ -4,16 +4,28 @@ import SessionUtilitiesKit public final class ConfigurationMessage : ControlMessage { public var closedGroups: Set = [] public var openGroups: Set = [] + public var displayName: String? + public var profilePictureURL: String? + public var profileKey: Data? public override var ttl: UInt64 { 4 * 24 * 60 * 60 * 1000 } public override var isSelfSendValid: Bool { true } + // MARK: Validation + public override var isValid: Bool { + guard displayName != nil else { return false } + return true + } + // MARK: Initialization public override init() { super.init() } - public init(closedGroups: Set, openGroups: Set) { + public init(displayName: String, profilePictureURL: String?, profileKey: Data?, closedGroups: Set, openGroups: Set) { super.init() + self.displayName = displayName + self.profilePictureURL = profilePictureURL + self.profileKey = profileKey self.closedGroups = closedGroups self.openGroups = openGroups } @@ -23,24 +35,36 @@ public final class ConfigurationMessage : ControlMessage { super.init(coder: coder) if let closedGroups = coder.decodeObject(forKey: "closedGroups") as! Set? { self.closedGroups = closedGroups } if let openGroups = coder.decodeObject(forKey: "openGroups") as! Set? { self.openGroups = openGroups } + if let displayName = coder.decodeObject(forKey: "displayName") as! String? { self.displayName = displayName } + if let profilePictureURL = coder.decodeObject(forKey: "profilePictureURL") as! String? { self.profilePictureURL = profilePictureURL } + if let profileKey = coder.decodeObject(forKey: "profileKey") as! Data? { self.profileKey = profileKey } } public override func encode(with coder: NSCoder) { super.encode(with: coder) coder.encode(closedGroups, forKey: "closedGroups") coder.encode(openGroups, forKey: "openGroups") + coder.encode(displayName, forKey: "displayName") + coder.encode(profilePictureURL, forKey: "profilePictureURL") + coder.encode(profileKey, forKey: "profileKey") } // MARK: Proto Conversion public override class func fromProto(_ proto: SNProtoContent) -> ConfigurationMessage? { guard let configurationProto = proto.configurationMessage else { return nil } + let displayName = configurationProto.displayName + let profilePictureURL = configurationProto.profilePicture + let profileKey = configurationProto.profileKey let closedGroups = Set(configurationProto.closedGroups.compactMap { ClosedGroup.fromProto($0) }) let openGroups = Set(configurationProto.openGroups) - return ConfigurationMessage(closedGroups: closedGroups, openGroups: openGroups) + return ConfigurationMessage(displayName: displayName, profilePictureURL: profilePictureURL, profileKey: profileKey, closedGroups: closedGroups, openGroups: openGroups) } public override func toProto(using transaction: YapDatabaseReadWriteTransaction) -> SNProtoContent? { - let configurationProto = SNProtoConfigurationMessage.builder() + guard let displayName = displayName else { return nil } + let configurationProto = SNProtoConfigurationMessage.builder(displayName: displayName) + if let profilePictureURL = profilePictureURL { configurationProto.setProfilePicture(profilePictureURL) } + if let profileKey = profileKey { configurationProto.setProfileKey(profileKey) } configurationProto.setClosedGroups(closedGroups.compactMap { $0.toProto() }) configurationProto.setOpenGroups([String](openGroups)) let contentProto = SNProtoContent.builder() diff --git a/SessionMessagingKit/Protos/Generated/SNProto.swift b/SessionMessagingKit/Protos/Generated/SNProto.swift index 5d79bfbbb..9ba40dedc 100644 --- a/SessionMessagingKit/Protos/Generated/SNProto.swift +++ b/SessionMessagingKit/Protos/Generated/SNProto.swift @@ -3126,15 +3126,21 @@ extension SNProtoConfigurationMessageClosedGroup.SNProtoConfigurationMessageClos // MARK: - SNProtoConfigurationMessageBuilder - @objc public class func builder() -> SNProtoConfigurationMessageBuilder { - return SNProtoConfigurationMessageBuilder() + @objc public class func builder(displayName: String) -> SNProtoConfigurationMessageBuilder { + return SNProtoConfigurationMessageBuilder(displayName: displayName) } // asBuilder() constructs a builder that reflects the proto's contents. @objc public func asBuilder() -> SNProtoConfigurationMessageBuilder { - let builder = SNProtoConfigurationMessageBuilder() + let builder = SNProtoConfigurationMessageBuilder(displayName: displayName) builder.setClosedGroups(closedGroups) builder.setOpenGroups(openGroups) + if let _value = profilePicture { + builder.setProfilePicture(_value) + } + if let _value = profileKey { + builder.setProfileKey(_value) + } return builder } @@ -3144,6 +3150,12 @@ extension SNProtoConfigurationMessageClosedGroup.SNProtoConfigurationMessageClos @objc fileprivate override init() {} + @objc fileprivate init(displayName: String) { + super.init() + + setDisplayName(displayName) + } + @objc public func addClosedGroups(_ valueParam: SNProtoConfigurationMessageClosedGroup) { var items = proto.closedGroups items.append(valueParam.proto) @@ -3164,6 +3176,18 @@ extension SNProtoConfigurationMessageClosedGroup.SNProtoConfigurationMessageClos proto.openGroups = wrappedItems } + @objc public func setDisplayName(_ valueParam: String) { + proto.displayName = valueParam + } + + @objc public func setProfilePicture(_ valueParam: String) { + proto.profilePicture = valueParam + } + + @objc public func setProfileKey(_ valueParam: Data) { + proto.profileKey = valueParam + } + @objc public func build() throws -> SNProtoConfigurationMessage { return try SNProtoConfigurationMessage.parseProto(proto) } @@ -3177,14 +3201,38 @@ extension SNProtoConfigurationMessageClosedGroup.SNProtoConfigurationMessageClos @objc public let closedGroups: [SNProtoConfigurationMessageClosedGroup] + @objc public let displayName: String + @objc public var openGroups: [String] { return proto.openGroups } + @objc public var profilePicture: String? { + guard proto.hasProfilePicture else { + return nil + } + return proto.profilePicture + } + @objc public var hasProfilePicture: Bool { + return proto.hasProfilePicture + } + + @objc public var profileKey: Data? { + guard proto.hasProfileKey else { + return nil + } + return proto.profileKey + } + @objc public var hasProfileKey: Bool { + return proto.hasProfileKey + } + private init(proto: SessionProtos_ConfigurationMessage, - closedGroups: [SNProtoConfigurationMessageClosedGroup]) { + closedGroups: [SNProtoConfigurationMessageClosedGroup], + displayName: String) { self.proto = proto self.closedGroups = closedGroups + self.displayName = displayName } @objc @@ -3201,12 +3249,18 @@ extension SNProtoConfigurationMessageClosedGroup.SNProtoConfigurationMessageClos var closedGroups: [SNProtoConfigurationMessageClosedGroup] = [] closedGroups = try proto.closedGroups.map { try SNProtoConfigurationMessageClosedGroup.parseProto($0) } + guard proto.hasDisplayName else { + throw SNProtoError.invalidProtobuf(description: "\(logTag) missing required field: displayName") + } + let displayName = proto.displayName + // MARK: - Begin Validation Logic for SNProtoConfigurationMessage - // MARK: - End Validation Logic for SNProtoConfigurationMessage - let result = SNProtoConfigurationMessage(proto: proto, - closedGroups: closedGroups) + closedGroups: closedGroups, + displayName: displayName) return result } diff --git a/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift b/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift index c266767ee..9ec42a6dd 100644 --- a/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift +++ b/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift @@ -1139,7 +1139,7 @@ struct SessionProtos_DataMessage { /// name, members case update // = 2 - /// wrappers + /// publicKey, wrappers case encryptionKeyPair // = 3 /// name @@ -1151,8 +1151,6 @@ struct SessionProtos_DataMessage { /// members case membersRemoved // = 6 case memberLeft // = 7 - - /// wrappers case encryptionKeyPairRequest // = 8 init() { @@ -1248,6 +1246,34 @@ struct SessionProtos_ConfigurationMessage { var openGroups: [String] = [] + /// @required + var displayName: String { + get {return _displayName ?? String()} + set {_displayName = newValue} + } + /// Returns true if `displayName` has been explicitly set. + var hasDisplayName: Bool {return self._displayName != nil} + /// Clears the value of `displayName`. Subsequent reads from it will return its default value. + mutating func clearDisplayName() {self._displayName = nil} + + var profilePicture: String { + get {return _profilePicture ?? String()} + set {_profilePicture = newValue} + } + /// Returns true if `profilePicture` has been explicitly set. + var hasProfilePicture: Bool {return self._profilePicture != nil} + /// Clears the value of `profilePicture`. Subsequent reads from it will return its default value. + mutating func clearProfilePicture() {self._profilePicture = nil} + + var profileKey: Data { + get {return _profileKey ?? SwiftProtobuf.Internal.emptyData} + set {_profileKey = newValue} + } + /// Returns true if `profileKey` has been explicitly set. + var hasProfileKey: Bool {return self._profileKey != nil} + /// Clears the value of `profileKey`. Subsequent reads from it will return its default value. + mutating func clearProfileKey() {self._profileKey = nil} + var unknownFields = SwiftProtobuf.UnknownStorage() struct ClosedGroup { @@ -1300,6 +1326,10 @@ struct SessionProtos_ConfigurationMessage { } init() {} + + fileprivate var _displayName: String? = nil + fileprivate var _profilePicture: String? = nil + fileprivate var _profileKey: Data? = nil } struct SessionProtos_ReceiptMessage { @@ -3186,9 +3216,13 @@ extension SessionProtos_ConfigurationMessage: SwiftProtobuf.Message, SwiftProtob static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "closedGroups"), 2: .same(proto: "openGroups"), + 3: .same(proto: "displayName"), + 4: .same(proto: "profilePicture"), + 5: .same(proto: "profileKey"), ] public var isInitialized: Bool { + if self._displayName == nil {return false} if !SwiftProtobuf.Internal.areAllInitialized(self.closedGroups) {return false} return true } @@ -3198,6 +3232,9 @@ extension SessionProtos_ConfigurationMessage: SwiftProtobuf.Message, SwiftProtob switch fieldNumber { case 1: try decoder.decodeRepeatedMessageField(value: &self.closedGroups) case 2: try decoder.decodeRepeatedStringField(value: &self.openGroups) + case 3: try decoder.decodeSingularStringField(value: &self._displayName) + case 4: try decoder.decodeSingularStringField(value: &self._profilePicture) + case 5: try decoder.decodeSingularBytesField(value: &self._profileKey) default: break } } @@ -3210,12 +3247,24 @@ extension SessionProtos_ConfigurationMessage: SwiftProtobuf.Message, SwiftProtob if !self.openGroups.isEmpty { try visitor.visitRepeatedStringField(value: self.openGroups, fieldNumber: 2) } + if let v = self._displayName { + try visitor.visitSingularStringField(value: v, fieldNumber: 3) + } + if let v = self._profilePicture { + try visitor.visitSingularStringField(value: v, fieldNumber: 4) + } + if let v = self._profileKey { + try visitor.visitSingularBytesField(value: v, fieldNumber: 5) + } try unknownFields.traverse(visitor: &visitor) } static func ==(lhs: SessionProtos_ConfigurationMessage, rhs: SessionProtos_ConfigurationMessage) -> Bool { if lhs.closedGroups != rhs.closedGroups {return false} if lhs.openGroups != rhs.openGroups {return false} + if lhs._displayName != rhs._displayName {return false} + if lhs._profilePicture != rhs._profilePicture {return false} + if lhs._profileKey != rhs._profileKey {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/SessionMessagingKit/Protos/SessionProtos.proto b/SessionMessagingKit/Protos/SessionProtos.proto index fc9e8f2c1..8d44e579e 100644 --- a/SessionMessagingKit/Protos/SessionProtos.proto +++ b/SessionMessagingKit/Protos/SessionProtos.proto @@ -222,8 +222,12 @@ message ConfigurationMessage { repeated bytes admins = 5; } - repeated ClosedGroup closedGroups = 1; - repeated string openGroups = 2; + repeated ClosedGroup closedGroups = 1; + repeated string openGroups = 2; + // @required + required string displayName = 3; + optional string profilePicture = 4; + optional bytes profileKey = 5; } message ReceiptMessage { diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift index 8bb1241c9..842184d53 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift @@ -145,6 +145,22 @@ extension MessageReceiver { private static func handleConfigurationMessage(_ message: ConfigurationMessage, using transaction: Any) { guard message.sender == getUserHexEncodedPublicKey(), !UserDefaults.standard[.hasSyncedConfiguration] else { return } let storage = SNMessagingKitConfiguration.shared.storage + let profileManager = SSKEnvironment.shared.profileManager + // Profile + let sessionID = getUserHexEncodedPublicKey() + let contact = Storage.shared.getContact(with: sessionID) ?? Contact(sessionID: sessionID) + if let displayName = message.displayName { + profileManager.updateProfileForContact(withID: sessionID, displayName: displayName, with: transaction as! YapDatabaseReadWriteTransaction) + contact.displayName = displayName + } + if let profileKey = message.profileKey, let profilePictureURL = message.profilePictureURL, profileKey.count == kAES256_KeyByteLength { + profileManager.setProfileKeyData(profileKey, forRecipientId: sessionID, avatarURL: profilePictureURL) + contact.profilePictureURL = profilePictureURL + contact.profilePictureEncryptionKey = OWSAES256Key(data: profileKey) + } + // Notification + UserDefaults.standard[.hasSyncedConfiguration] = true + // Closed groups let allClosedGroupPublicKeys = storage.getUserClosedGroupPublicKeys() NotificationCenter.default.post(name: .configurationMessageReceived, object: nil) for closedGroup in message.closedGroups { @@ -152,12 +168,12 @@ extension MessageReceiver { handleNewClosedGroup(groupPublicKey: closedGroup.publicKey, name: closedGroup.name, encryptionKeyPair: closedGroup.encryptionKeyPair, members: [String](closedGroup.members), admins: [String](closedGroup.admins), messageSentTimestamp: message.sentTimestamp!, using: transaction) } + // Open groups let allOpenGroups = Set(storage.getAllUserOpenGroups().keys) for openGroupURL in message.openGroups { guard !allOpenGroups.contains(openGroupURL) else { continue } OpenGroupManager.shared.add(with: openGroupURL, using: transaction).retainUntilComplete() } - UserDefaults.standard[.hasSyncedConfiguration] = true } @discardableResult diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift index e021fe481..db167c791 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver.swift @@ -152,7 +152,9 @@ public enum MessageReceiver { if message is VisibleMessage && !isValid && proto.dataMessage?.attachments.isEmpty == false { isValid = true } - guard isValid else { throw Error.invalidMessage } + guard isValid else { + throw Error.invalidMessage + } // Return return (message, proto) } else { diff --git a/SignalUtilitiesKit/Messaging/ConfigurationMessage+Convenience.swift b/SignalUtilitiesKit/Messaging/ConfigurationMessage+Convenience.swift index 82702e72a..8894ad52c 100644 --- a/SignalUtilitiesKit/Messaging/ConfigurationMessage+Convenience.swift +++ b/SignalUtilitiesKit/Messaging/ConfigurationMessage+Convenience.swift @@ -2,6 +2,10 @@ extension ConfigurationMessage { public static func getCurrent() -> ConfigurationMessage { + let storage = Storage.shared + let displayName = storage.getUserDisplayName()! + let profilePictureURL = storage.getUserProfilePictureURL() + let profileKey = storage.getUserProfileKey() var closedGroups: Set = [] var openGroups: Set = [] Storage.read { transaction in @@ -12,18 +16,18 @@ extension ConfigurationMessage { guard thread.isCurrentUserMemberInGroup() else { return } let groupID = thread.groupModel.groupId let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID) - guard Storage.shared.isClosedGroup(groupPublicKey), - let encryptionKeyPair = Storage.shared.getLatestClosedGroupEncryptionKeyPair(for: groupPublicKey) else { return } + guard storage.isClosedGroup(groupPublicKey), + let encryptionKeyPair = storage.getLatestClosedGroupEncryptionKeyPair(for: groupPublicKey) else { return } let closedGroup = ClosedGroup(publicKey: groupPublicKey, name: thread.groupModel.groupName!, encryptionKeyPair: encryptionKeyPair, members: Set(thread.groupModel.groupMemberIds), admins: Set(thread.groupModel.groupAdminIds)) closedGroups.insert(closedGroup) case .openGroup: - guard let openGroup = Storage.shared.getOpenGroup(for: thread.uniqueId!) else { return } + guard let openGroup = storage.getOpenGroup(for: thread.uniqueId!) else { return } openGroups.insert(openGroup.server) default: break } } } - return ConfigurationMessage(closedGroups: closedGroups, openGroups: openGroups) + return ConfigurationMessage(displayName: displayName, profilePictureURL: profilePictureURL, profileKey: profileKey, closedGroups: closedGroups, openGroups: openGroups) } }