From 3c7369f465517c5a3f48ddce603253a4f7cbaed6 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Wed, 9 Oct 2019 10:06:26 +1100 Subject: [PATCH 01/17] Added storage functions. Minor refactor. --- Signal/src/AppDelegate.m | 2 +- .../src/Loki/Group Chat/GroupChatPoller.swift | 6 +-- .../Loki/API/Group Chat/LokiGroupChat.swift | 33 +++++++++++++---- .../Loki/Database/LokiDatabaseUtilities.swift | 20 ++++++++++ .../Database/OWSPrimaryStorage+Loki.swift | 37 ++++++++++++++++--- 5 files changed, 81 insertions(+), 17 deletions(-) diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 03d0f69d85d..bea3730926c 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -1527,7 +1527,7 @@ - (void)stopLongPollerIfNeeded - (LKGroupChat *)lokiPublicChat { - return [[LKGroupChat alloc] initWithServerID:LKGroupChatAPI.publicChatServerID server:LKGroupChatAPI.publicChatServer displayName:NSLocalizedString(@"Loki Public Chat", @"") isDeletable:true]; + return [[LKGroupChat alloc] initWithChannel:LKGroupChatAPI.publicChatServerID server:LKGroupChatAPI.publicChatServer displayName:NSLocalizedString(@"Loki Public Chat", @"") isDeletable:true]; } - (LKRSSFeed *)lokiNewsFeed diff --git a/Signal/src/Loki/Group Chat/GroupChatPoller.swift b/Signal/src/Loki/Group Chat/GroupChatPoller.swift index 7ed3c1623bf..21eee0ce92d 100644 --- a/Signal/src/Loki/Group Chat/GroupChatPoller.swift +++ b/Signal/src/Loki/Group Chat/GroupChatPoller.swift @@ -110,7 +110,7 @@ public final class GroupChatPoller : NSObject { } } // Poll - let _ = LokiGroupChatAPI.getMessages(for: group.serverID, on: group.server).done(on: .main) { messages in + let _ = LokiGroupChatAPI.getMessages(for: group.channel, on: group.server).done(on: .main) { messages in messages.forEach { message in if message.hexEncodedPublicKey != userHexEncodedPublicKey { processIncomingMessage(message) @@ -123,7 +123,7 @@ public final class GroupChatPoller : NSObject { private func pollForDeletedMessages() { let group = self.group - let _ = LokiGroupChatAPI.getDeletedMessageServerIDs(for: group.serverID, on: group.server).done { deletedMessageServerIDs in + let _ = LokiGroupChatAPI.getDeletedMessageServerIDs(for: group.channel, on: group.server).done { deletedMessageServerIDs in let storage = OWSPrimaryStorage.shared() storage.dbReadWriteConnection.readWrite { transaction in let deletedMessageIDs = deletedMessageServerIDs.compactMap { storage.getIDForMessage(withServerID: UInt($0), in: transaction) } @@ -135,6 +135,6 @@ public final class GroupChatPoller : NSObject { } private func pollForModerators() { - let _ = LokiGroupChatAPI.getModerators(for: group.serverID, on: group.server) + let _ = LokiGroupChatAPI.getModerators(for: group.channel, on: group.server) } } diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift index 0f5ec66be57..a25fa13386f 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift +++ b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift @@ -1,19 +1,38 @@ @objc(LKGroupChat) -public final class LokiGroupChat : NSObject { - @objc public let id: String - @objc public let serverID: UInt64 +public final class LokiGroupChat : NSObject, NSCoding { + @objc public var id: String { + return "\(server).\(channel)" + } + + @objc public let channel: UInt64 @objc public let server: String @objc public let displayName: String @objc public let isDeletable: Bool - @objc public init(serverID: UInt64, server: String, displayName: String, isDeletable: Bool) { - self.id = "\(server).\(serverID)" - self.serverID = serverID + @objc public init(channel: UInt64, server: String, displayName: String, isDeletable: Bool) { + self.channel = channel self.server = server self.displayName = displayName self.isDeletable = isDeletable } - override public var description: String { return displayName } + // MARK: Coding + + @objc public init?(coder: NSCoder) { + channel = UInt64(coder.decodeInt64(forKey: "channel")) + server = coder.decodeObject(forKey: "server") as! String + displayName = coder.decodeObject(forKey: "displayName") as! String + isDeletable = coder.decodeBool(forKey: "isDeletable") + super.init() + } + + @objc public func encode(with coder: NSCoder) { + coder.encode(Int64(channel), forKey: "channel") + coder.encode(server, forKey: "server") + coder.encode(displayName, forKey: "displayName") + coder.encode(isDeletable, forKey: "isDeletable") + } + + override public var description: String { return "\(displayName) - \(server)" } } diff --git a/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift b/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift index 2952040b475..5cbb79dfa1d 100644 --- a/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift +++ b/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift @@ -24,4 +24,24 @@ public final class LokiDatabaseUtilities : NSObject { public static func objc_getMasterHexEncodedPublicKey(for slaveHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> String? { return OWSPrimaryStorage.shared().getMasterHexEncodedPublicKey(for: slaveHexEncodedPublicKey, in: transaction) } + + @objc(getAllGroupChats:) + public static func objc_getAllGroupChats(in transaction: YapDatabaseReadTransaction) -> [String: LokiGroupChat] { + return OWSPrimaryStorage.shared().getAllGroupChats(in: transaction) + } + + @objc(getGroupChatForThreadID:transaction:) + public static func objc_getGroupChat(for threadID: String, in transaction: YapDatabaseReadTransaction) -> LokiGroupChat? { + return OWSPrimaryStorage.shared().getGroupChat(for: threadID, in: transaction) + } + + @objc(setGroupChat:threadID:transaction:) + public static func objc_setGroupChat(_ groupChat: LokiGroupChat, for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { + return OWSPrimaryStorage.shared().setGroupChat(groupChat, for: threadID, in: transaction) + } + + @objc(removeGroupChatForThreadID:transaction:) + public static func objc_removeGroupChat(for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { + return OWSPrimaryStorage.shared().removeGroupChat(for: threadID, in: transaction) + } } diff --git a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift index 542bbcc2526..42d56ecc12f 100644 --- a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift +++ b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift @@ -1,7 +1,10 @@ public extension OWSPrimaryStorage { - - private func getCollection(for primaryDevice: String) -> String { + private var groupChatCollection: String { + return "LokiGroupChatCollection" + } + + private func getDeviceLinkCollection(for primaryDevice: String) -> String { return "LokiDeviceLinkCollection-\(primaryDevice)" } @@ -13,23 +16,23 @@ public extension OWSPrimaryStorage { return } let masterHexEncodedPublicKey = masterHexEncodedPublicKeys.first! - let collection = getCollection(for: masterHexEncodedPublicKey) + let collection = getDeviceLinkCollection(for: masterHexEncodedPublicKey) transaction.removeAllObjects(inCollection: collection) deviceLinks.forEach { addDeviceLink($0, in: transaction) } // TODO: Check the performance impact of this } public func addDeviceLink(_ deviceLink: DeviceLink, in transaction: YapDatabaseReadWriteTransaction) { - let collection = getCollection(for: deviceLink.master.hexEncodedPublicKey) + let collection = getDeviceLinkCollection(for: deviceLink.master.hexEncodedPublicKey) transaction.setObject(deviceLink, forKey: deviceLink.slave.hexEncodedPublicKey, inCollection: collection) } public func removeDeviceLink(_ deviceLink: DeviceLink, in transaction: YapDatabaseReadWriteTransaction) { - let collection = getCollection(for: deviceLink.master.hexEncodedPublicKey) + let collection = getDeviceLinkCollection(for: deviceLink.master.hexEncodedPublicKey) transaction.removeObject(forKey: deviceLink.slave.hexEncodedPublicKey, inCollection: collection) } public func getDeviceLinks(for masterHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> Set { - let collection = getCollection(for: masterHexEncodedPublicKey) + let collection = getDeviceLinkCollection(for: masterHexEncodedPublicKey) var result: Set = [] transaction.enumerateRows(inCollection: collection) { _, object, _, _ in guard let deviceLink = object as? DeviceLink else { return } @@ -51,4 +54,26 @@ public extension OWSPrimaryStorage { public func getMasterHexEncodedPublicKey(for slaveHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> String? { return getDeviceLink(for: slaveHexEncodedPublicKey, in: transaction)?.master.hexEncodedPublicKey } + + public func getAllGroupChats(in transaction: YapDatabaseReadTransaction) -> [String: LokiGroupChat] { + var dict = [String: LokiGroupChat]() + transaction.enumerateKeysAndObjects(inCollection: groupChatCollection) { (threadID, object, _) in + if let groupChat = object as? LokiGroupChat { + dict[threadID] = groupChat + } + } + return dict + } + + public func getGroupChat(for threadID: String, in transaction: YapDatabaseReadTransaction) -> LokiGroupChat? { + return transaction.object(forKey: threadID, inCollection: groupChatCollection) as? LokiGroupChat + } + + public func setGroupChat(_ groupChat: LokiGroupChat, for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { + transaction.setObject(groupChat, forKey: threadID, inCollection: groupChatCollection) + } + + public func removeGroupChat(for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { + transaction.removeObject(forKey: threadID, inCollection: groupChatCollection) + } } From 683a5c1edc37e39dd382248c54af5564dfeb2421 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Wed, 9 Oct 2019 11:18:11 +1100 Subject: [PATCH 02/17] Remove dependency on hard coded public chat values. --- .../ConversationView/Cells/OWSMessageCell.m | 16 ++++++++++++---- .../Cells/OWSQuotedMessageView.m | 1 + .../ConversationView/ConversationViewItem.m | 17 +++++++++++++++-- SignalMessaging/profiles/OWSProfileManager.m | 1 + .../src/Messages/OWSMessageSender.m | 10 ++++++++-- 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index 6bfac127f06..a8f59839cc4 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -7,6 +7,7 @@ #import "OWSMessageBubbleView.h" #import "OWSMessageHeaderView.h" #import "Session-Swift.h" +#import NS_ASSUME_NONNULL_BEGIN @@ -298,10 +299,17 @@ - (BOOL)updateAvatarView [self.contentView addSubview:self.avatarView]; if (self.viewItem.isGroupThread && !self.viewItem.isRSSFeed) { - BOOL isModerator = [LKGroupChatAPI isUserModerator:incomingMessage.authorId forGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer]; - UIImage *moderatorIcon = [UIImage imageNamed:@"Crown"]; - self.moderatorIconImageView.image = moderatorIcon; - self.moderatorIconImageView.hidden = !isModerator; + __block LKGroupChat *groupChat; + [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + groupChat = [LKDatabaseUtilities getGroupChatForThreadID:self.viewItem.interaction.uniqueThreadId transaction: transaction]; + }]; + + if (groupChat != nil) { + BOOL isModerator = [LKGroupChatAPI isUserModerator:incomingMessage.authorId forGroup:groupChat.channel onServer:groupChat.server]; + UIImage *moderatorIcon = [UIImage imageNamed:@"Crown"]; + self.moderatorIconImageView.image = moderatorIcon; + self.moderatorIconImageView.hidden = !isModerator; + } } [self.contentView addSubview:self.moderatorIconImageView]; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index 92cdf5ec110..e3f3b22e287 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -553,6 +553,7 @@ - (UILabel *)configureQuotedAuthorLabel if (quotedAuthor == self.quotedMessage.authorId) { [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + // TODO: Fix this to use dynamic LKGroup variables! NSString *collection = [NSString stringWithFormat:@"%@.%@", LKGroupChatAPI.publicChatServer, @(LKGroupChatAPI.publicChatServerID)]; NSString *displayName = [transaction stringForKey:self.quotedMessage.authorId inCollection:collection]; if (displayName != nil) { quotedAuthor = displayName; } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 425ebd0153f..8978703293f 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -1187,9 +1187,15 @@ - (void)deleteAction TSMessage *message = (TSMessage *)self.interaction; if (!message.isGroupChatMessage) return; + __block LKGroupChat *groupChat; + [self.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + groupChat = [LKDatabaseUtilities getGroupChatForThreadID:groupThread.uniqueId transaction: transaction]; + }]; + if (groupChat == nil) return; + // Delete the message BOOL isSentByUser = (interationType == OWSInteractionType_OutgoingMessage); - [[LKGroupChatAPI deleteMessageWithID:message.groupChatServerID forGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer isSentByUser:isSentByUser].catch(^(NSError *error) { + [[LKGroupChatAPI deleteMessageWithID:message.groupChatServerID forGroup:groupChat.channel onServer:groupChat.server isSentByUser:isSentByUser].catch(^(NSError *error) { // Roll back [self.interaction save]; }) retainUntilComplete]; @@ -1256,9 +1262,16 @@ - (BOOL)userCanDeleteGroupMessage TSMessage *message = (TSMessage *)self.interaction; if (!message.isGroupChatMessage) return false; + // Make sure we have the details to contact the server + __block LKGroupChat *groupChat; + [self.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + groupChat = [LKDatabaseUtilities getGroupChatForThreadID:groupThread.uniqueId transaction: transaction]; + }]; + if (groupChat == nil) return false; + // Only allow deletion on incoming messages if the user has moderation permission if (interationType == OWSInteractionType_IncomingMessage) { - BOOL isModerator = [LKGroupChatAPI isUserModerator:self.userHexEncodedPublicKey forGroup:LKGroupChatAPI.publicChatServerID onServer: LKGroupChatAPI.publicChatServer]; + BOOL isModerator = [LKGroupChatAPI isUserModerator:self.userHexEncodedPublicKey forGroup:groupChat.channel onServer:groupChat.server]; if (!isModerator) return false; } diff --git a/SignalMessaging/profiles/OWSProfileManager.m b/SignalMessaging/profiles/OWSProfileManager.m index e983cc1c1a2..b326356f111 100644 --- a/SignalMessaging/profiles/OWSProfileManager.m +++ b/SignalMessaging/profiles/OWSProfileManager.m @@ -535,6 +535,7 @@ - (void)updateServiceWithProfileName:(nullable NSString *)localProfileName OWSAssertDebug(successBlock); OWSAssertDebug(failureBlock); + // TODO: Fix this to set display name on all servers [[LKGroupChatAPI setDisplayName:localProfileName on:LKGroupChatAPI.publicChatServer] .thenOn(dispatch_get_main_queue(), ^() { successBlock(); diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 239ef269d52..3b646fcebdb 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -1185,7 +1185,13 @@ - (void)sendMessage:(OWSMessageSend *)messageSend [self messageSendDidFail:messageSend deviceMessages:deviceMessages statusCode:statusCode error:error responseData:responseData]; }; - if ([recipient.recipientId isEqualToString:LKGroupChatAPI.publicChatServer]) { + // Get the group chat info if we have it + __block LKGroupChat *groupChat; + [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + groupChat = [LKDatabaseUtilities getGroupChatForThreadID:message.uniqueThreadId transaction: transaction]; + }]; + + if (groupChat != nil) { NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey; NSString *displayName = SSKEnvironment.shared.profileManager.localProfileName; if (displayName == nil) { displayName = @"Anonymous"; } @@ -1200,7 +1206,7 @@ - (void)sendMessage:(OWSMessageSend *)messageSend } LKGroupMessage *groupMessage = [[LKGroupMessage alloc] initWithHexEncodedPublicKey:userHexEncodedPublicKey displayName:displayName body:message.body type:LKGroupChatAPI.publicChatMessageType timestamp:message.timestamp quotedMessageTimestamp:quoteID quoteeHexEncodedPublicKey:quoteeHexEncodedPublicKey quotedMessageBody:quote.body quotedMessageServerID:quotedMessageServerID signatureData:nil signatureVersion:0]; - [[LKGroupChatAPI sendMessage:groupMessage toGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer] + [[LKGroupChatAPI sendMessage:groupMessage toGroup:groupChat.channel onServer:groupChat.server] .thenOn(OWSDispatch.sendingQueue, ^(LKGroupMessage *groupMessage) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [message saveGroupChatServerID:groupMessage.serverID in:transaction]; From d8d33287d1892287d25ffd7bc61f1a64a76ad13f Mon Sep 17 00:00:00 2001 From: Mikunj Date: Wed, 9 Oct 2019 12:40:39 +1100 Subject: [PATCH 03/17] Added PublicChatManager. Added migrations. --- Pods | 2 +- Signal.xcodeproj/project.pbxproj | 4 - Signal/src/AppDelegate.m | 56 ++------ .../Cells/OWSQuotedMessageView.m | 11 +- .../ConversationViewController.m | 1 + .../ConversationView/ConversationViewItem.m | 2 +- .../ViewControllers/DebugUI/DebugUIMessages.m | 5 +- .../ViewModels/OWSQuotedReplyModel.h | 6 + .../ViewModels/OWSQuotedReplyModel.m | 16 ++- .../environment/VersionMigrations.m | 20 +++ SignalMessaging/profiles/OWSProfileManager.m | 18 +-- .../Loki/API/Group Chat/LokiChannelInfo.swift | 3 + .../Loki/API/Group Chat/LokiGroupChat.swift | 14 +- .../API/Group Chat/LokiGroupChatAPI.swift | 26 +++- .../API/Group Chat/LokiGroupChatPoller.swift | 6 +- .../Group Chat/LokiPublicChatManager.swift | 121 ++++++++++++++++++ .../Loki/Database/LokiDatabaseUtilities.swift | 2 +- .../Database/OWSPrimaryStorage+Loki.swift | 2 +- .../src/Messages/OWSMessageSender.m | 5 +- 19 files changed, 237 insertions(+), 83 deletions(-) create mode 100644 SignalServiceKit/src/Loki/API/Group Chat/LokiChannelInfo.swift rename Signal/src/Loki/Group Chat/GroupChatPoller.swift => SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatPoller.swift (97%) create mode 100644 SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift diff --git a/Pods b/Pods index a6c8eadf5ec..68a1e499594 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit a6c8eadf5ecff074bc7d07f4cd02b7054e3d2b65 +Subproject commit 68a1e49959447a8ef4b4de77edec53375c598268 diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index afdbb038c0f..1658f38daee 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -565,7 +565,6 @@ B821F2FA2272CEEE002C88C0 /* SeedVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B821F2F92272CEEE002C88C0 /* SeedVC.swift */; }; B8258493230FA5E9001B41CB /* ScanQRCodeVC.m in Sources */ = {isa = PBXBuildFile; fileRef = B8258492230FA5E9001B41CB /* ScanQRCodeVC.m */; }; B82584A02315024B001B41CB /* RSSFeedPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = B825849F2315024B001B41CB /* RSSFeedPoller.swift */; }; - B845B4D4230CD09100D759F0 /* GroupChatPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = B845B4D3230CD09000D759F0 /* GroupChatPoller.swift */; }; B846365B22B7418B00AF1514 /* Identicon+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */; }; B86BD08123399883000F5AE3 /* QRCodeModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08023399883000F5AE3 /* QRCodeModal.swift */; }; B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; }; @@ -1374,7 +1373,6 @@ B8258491230FA5DA001B41CB /* ScanQRCodeVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScanQRCodeVC.h; sourceTree = ""; }; B8258492230FA5E9001B41CB /* ScanQRCodeVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScanQRCodeVC.m; sourceTree = ""; }; B825849F2315024B001B41CB /* RSSFeedPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSSFeedPoller.swift; sourceTree = ""; }; - B845B4D3230CD09000D759F0 /* GroupChatPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupChatPoller.swift; sourceTree = ""; }; B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Identicon+ObjC.swift"; sourceTree = ""; }; B86BD08023399883000F5AE3 /* QRCodeModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeModal.swift; sourceTree = ""; }; B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = ""; }; @@ -2699,7 +2697,6 @@ B86BD0892339A278000F5AE3 /* Group Chat */ = { isa = PBXGroup; children = ( - B845B4D3230CD09000D759F0 /* GroupChatPoller.swift */, B825849F2315024B001B41CB /* RSSFeedPoller.swift */, ); path = "Group Chat"; @@ -3744,7 +3741,6 @@ 34A4C62022175C5C0042EF2E /* OnboardingProfileViewController.swift in Sources */, 4505C2BF1E648EA300CEBF41 /* ExperienceUpgrade.swift in Sources */, EF764C351DB67CC5000D9A87 /* UIViewController+Permissions.m in Sources */, - B845B4D4230CD09100D759F0 /* GroupChatPoller.swift in Sources */, 45CD81EF1DC030E7004C9430 /* SyncPushTokensJob.swift in Sources */, 34D2CCE0206939B400CB1A14 /* DebugUIMessagesAssetLoader.m in Sources */, 4CEB78C92178EBAB00F315D2 /* OWSSessionResetJobRecord.m in Sources */, diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index bea3730926c..ba5626c2d63 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -69,7 +69,6 @@ @interface AppDelegate () // Loki @property (nonatomic) LKP2PServer *lokiP2PServer; @property (nonatomic) LKLongPoller *lokiLongPoller; -@property (nonatomic) LKGroupChatPoller *lokiPublicChatPoller; @property (nonatomic) LKRSSFeedPoller *lokiNewsFeedPoller; @property (nonatomic) LKRSSFeedPoller *lokiMessengerUpdatesFeedPoller; @@ -1525,11 +1524,6 @@ - (void)stopLongPollerIfNeeded [self.lokiLongPoller stopIfNeeded]; } -- (LKGroupChat *)lokiPublicChat -{ - return [[LKGroupChat alloc] initWithChannel:LKGroupChatAPI.publicChatServerID server:LKGroupChatAPI.publicChatServer displayName:NSLocalizedString(@"Loki Public Chat", @"") isDeletable:true]; -} - - (LKRSSFeed *)lokiNewsFeed { return [[LKRSSFeed alloc] initWithId:@"loki.network.feed" server:@"https://loki.network/feed/" displayName:NSLocalizedString(@"Loki News", @"") isDeletable:true]; @@ -1542,25 +1536,18 @@ - (LKRSSFeed *)lokiMessengerUpdatesFeed - (void)createGroupChatsIfNeeded { - LKGroupChat *publicChat = self.lokiPublicChat; - NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey; - NSString *userDefaultsKey = [@"isGroupChatSetUp." stringByAppendingString:publicChat.id]; - BOOL isChatSetUp = [NSUserDefaults.standardUserDefaults boolForKey:userDefaultsKey]; - if (!isChatSetUp || !publicChat.isDeletable) { - TSGroupModel *group = [[TSGroupModel alloc] initWithTitle:publicChat.displayName memberIds:@[ userHexEncodedPublicKey, publicChat.server ] image:nil groupId:[publicChat.id dataUsingEncoding:NSUTF8StringEncoding]]; - __block TSGroupThread *thread; - [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - thread = [TSGroupThread getOrCreateThreadWithGroupModel:group transaction:transaction]; - NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; - NSCalendar *calendar = NSCalendar.currentCalendar; - [calendar setTimeZone:timeZone]; - NSDateComponents *dateComponents = [NSDateComponents new]; - [dateComponents setYear:999]; - NSDate *date = [calendar dateByAddingComponents:dateComponents toDate:[NSDate new] options:0]; - [thread updateWithMutedUntilDate:date transaction:transaction]; - }]; - [OWSProfileManager.sharedManager addThreadToProfileWhitelist:thread]; - [NSUserDefaults.standardUserDefaults setBool:YES forKey:userDefaultsKey]; + // Setup our default public chats + for (LKGroupChat *chat in LKGroupChat.defaultChats) { + NSString *userDefaultsKey = [@"isGroupChatSetUp." stringByAppendingString:chat.id]; + BOOL isChatSetUp = [NSUserDefaults.standardUserDefaults boolForKey:userDefaultsKey]; + if (!isChatSetUp || !chat.isDeletable) { + [LKPublicChatManager.shared addChatWithServer:chat.server channel:chat.channel name:chat.displayName]; + [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + TSGroupThread *thread = [TSGroupThread threadWithGroupId:chat.idAsData transaction:transaction]; + if (thread != nil) { [OWSProfileManager.sharedManager addThreadToProfileWhitelist:thread]; } + }]; + [NSUserDefaults.standardUserDefaults setBool:YES forKey:userDefaultsKey]; + } } } @@ -1590,18 +1577,6 @@ - (void)createRSSFeedsIfNeeded } } -- (void)createGroupChatPollersIfNeeded -{ - // Only create the group chat pollers if their threads aren't deleted - __block TSGroupThread *thread; - [OWSPrimaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - thread = [TSGroupThread threadWithGroupId:[self.lokiPublicChat.id dataUsingEncoding:NSUTF8StringEncoding] transaction:transaction]; - }]; - if (thread != nil && self.lokiPublicChatPoller == nil) { - self.lokiPublicChatPoller = [[LKGroupChatPoller alloc] initForGroup:self.lokiPublicChat]; - } -} - - (void)createRSSFeedPollersIfNeeded { // Only create the RSS feed pollers if their threads aren't deleted @@ -1620,8 +1595,7 @@ - (void)createRSSFeedPollersIfNeeded - (void)startGroupChatPollersIfNeeded { - [self createGroupChatPollersIfNeeded]; - if (self.lokiPublicChatPoller != nil) { [self.lokiPublicChatPoller startIfNeeded]; } + [LKPublicChatManager.shared startPollersIfNeeded]; } - (void)startRSSFeedPollersIfNeeded @@ -1635,10 +1609,6 @@ - (void)handleThreadDeleted:(NSNotification *)notification { NSDictionary *userInfo = notification.userInfo; NSString *threadID = (NSString *)userInfo[@"threadId"]; if (threadID == nil) { return; } - if ([threadID isEqualToString:[TSGroupThread threadIdFromGroupId:[self.lokiPublicChat.id dataUsingEncoding:NSUTF8StringEncoding]]] && self.lokiPublicChatPoller != nil) { - [self.lokiPublicChatPoller stop]; - self.lokiPublicChatPoller = nil; - } if ([threadID isEqualToString:[TSGroupThread threadIdFromGroupId:[self.lokiNewsFeed.id dataUsingEncoding:NSUTF8StringEncoding]]] && self.lokiNewsFeedPoller != nil) { [self.lokiNewsFeedPoller stop]; self.lokiNewsFeedPoller = nil; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index e3f3b22e287..65409f09663 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -13,6 +13,7 @@ #import #import #import +#import NS_ASSUME_NONNULL_BEGIN @@ -553,10 +554,12 @@ - (UILabel *)configureQuotedAuthorLabel if (quotedAuthor == self.quotedMessage.authorId) { [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - // TODO: Fix this to use dynamic LKGroup variables! - NSString *collection = [NSString stringWithFormat:@"%@.%@", LKGroupChatAPI.publicChatServer, @(LKGroupChatAPI.publicChatServerID)]; - NSString *displayName = [transaction stringForKey:self.quotedMessage.authorId inCollection:collection]; - if (displayName != nil) { quotedAuthor = displayName; } + LKGroupChat *chat = [LKDatabaseUtilities getGroupChatForThreadID:self.quotedMessage.threadId transaction:transaction]; + if (chat != nil) { + NSString *collection = [NSString stringWithFormat:@"%@.%@", chat.server, @(chat.channel)]; + NSString *displayName = [transaction stringForKey:self.quotedMessage.authorId inCollection:collection]; + if (displayName != nil) { quotedAuthor = displayName; } + } }]; } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index ac3929a0377..96e6de6e1e0 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -2598,6 +2598,7 @@ - (void)populateReplyForViewItem:(id)conversationItem __block OWSQuotedReplyModel *quotedReply; [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { quotedReply = [OWSQuotedReplyModel quotedReplyForSendingWithConversationViewItem:conversationItem + threadId:conversationItem.interaction.uniqueThreadId transaction:transaction]; }]; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 8978703293f..5421291e95d 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -620,7 +620,7 @@ - (void)ensureViewState:(YapDatabaseReadTransaction *)transaction // since that logic may exit early. if (message.quotedMessage) { self.quotedReply = - [OWSQuotedReplyModel quotedReplyWithQuotedMessage:message.quotedMessage transaction:transaction]; + [OWSQuotedReplyModel quotedReplyWithQuotedMessage:message.quotedMessage threadId:message.uniqueThreadId transaction:transaction]; if (self.quotedReply.body.length > 0) { self.displayableQuotedText = diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index a1e358a072b..67b29b6fed2 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -2016,7 +2016,7 @@ + (DebugUIMessagesAction *)fakeRandomTextAction:(TSThread *)thread transaction:transaction conversationStyle:conversationStyle]; quotedMessage = [ - [OWSQuotedReplyModel quotedReplyForSendingWithConversationViewItem:viewItem transaction:transaction] + [OWSQuotedReplyModel quotedReplyForSendingWithConversationViewItem:viewItem threadId:viewItem.interaction.uniqueThreadId transaction:transaction] buildQuotedMessageForSending]; } else { TSOutgoingMessage *_Nullable messageToQuote = [self createFakeOutgoingMessage:thread @@ -2037,8 +2037,7 @@ + (DebugUIMessagesAction *)fakeRandomTextAction:(TSThread *)thread isRSSFeed:NO transaction:transaction conversationStyle:conversationStyle]; - quotedMessage = [ - [OWSQuotedReplyModel quotedReplyForSendingWithConversationViewItem:viewItem transaction:transaction] + quotedMessage = [[OWSQuotedReplyModel quotedReplyForSendingWithConversationViewItem:viewItem threadId:viewItem.interaction.uniqueThreadId transaction:transaction] buildQuotedMessageForSending]; } OWSAssertDebug(quotedMessage); diff --git a/SignalMessaging/ViewModels/OWSQuotedReplyModel.h b/SignalMessaging/ViewModels/OWSQuotedReplyModel.h index ae8e8695987..cb8b950b2a4 100644 --- a/SignalMessaging/ViewModels/OWSQuotedReplyModel.h +++ b/SignalMessaging/ViewModels/OWSQuotedReplyModel.h @@ -40,14 +40,20 @@ NS_ASSUME_NONNULL_BEGIN // Used for persisted quoted replies, both incoming and outgoing. + (instancetype)quotedReplyWithQuotedMessage:(TSQuotedMessage *)quotedMessage + threadId:(NSString *)threadId transaction:(YapDatabaseReadTransaction *)transaction; // Builds a not-yet-sent QuotedReplyModel + (nullable instancetype)quotedReplyForSendingWithConversationViewItem:(id)conversationItem + threadId:(NSString *)threadId transaction:(YapDatabaseReadTransaction *)transaction; - (TSQuotedMessage *)buildQuotedMessageForSending; +// Loki + +@property (nonatomic, readonly) NSString *threadId; + @end diff --git a/SignalMessaging/ViewModels/OWSQuotedReplyModel.m b/SignalMessaging/ViewModels/OWSQuotedReplyModel.m index a8f6eaa6688..2ce5c8f4b84 100644 --- a/SignalMessaging/ViewModels/OWSQuotedReplyModel.m +++ b/SignalMessaging/ViewModels/OWSQuotedReplyModel.m @@ -31,7 +31,8 @@ - (instancetype)initWithTimestamp:(uint64_t)timestamp sourceFilename:(nullable NSString *)sourceFilename attachmentStream:(nullable TSAttachmentStream *)attachmentStream thumbnailAttachmentPointer:(nullable TSAttachmentPointer *)thumbnailAttachmentPointer - thumbnailDownloadFailed:(BOOL)thumbnailDownloadFailed NS_DESIGNATED_INITIALIZER; + thumbnailDownloadFailed:(BOOL)thumbnailDownloadFailed + threadId:(NSString *)threadId NS_DESIGNATED_INITIALIZER; @end @@ -50,6 +51,7 @@ - (instancetype)initWithTimestamp:(uint64_t)timestamp attachmentStream:(nullable TSAttachmentStream *)attachmentStream thumbnailAttachmentPointer:(nullable TSAttachmentPointer *)thumbnailAttachmentPointer thumbnailDownloadFailed:(BOOL)thumbnailDownloadFailed + threadId:(NSString *)threadId { self = [super init]; if (!self) { @@ -66,6 +68,7 @@ - (instancetype)initWithTimestamp:(uint64_t)timestamp _attachmentStream = attachmentStream; _thumbnailAttachmentPointer = thumbnailAttachmentPointer; _thumbnailDownloadFailed = thumbnailDownloadFailed; + _threadId = threadId; return self; } @@ -73,6 +76,7 @@ - (instancetype)initWithTimestamp:(uint64_t)timestamp #pragma mark - Factory Methods + (instancetype)quotedReplyWithQuotedMessage:(TSQuotedMessage *)quotedMessage + threadId:(NSString *)threadId transaction:(YapDatabaseReadTransaction *)transaction { OWSAssertDebug(quotedMessage.quotedAttachments.count <= 1); @@ -112,10 +116,12 @@ + (instancetype)quotedReplyWithQuotedMessage:(TSQuotedMessage *)quotedMessage sourceFilename:attachmentInfo.sourceFilename attachmentStream:nil thumbnailAttachmentPointer:attachmentPointer - thumbnailDownloadFailed:thumbnailDownloadFailed]; + thumbnailDownloadFailed:thumbnailDownloadFailed + threadId:threadId]; } + (nullable instancetype)quotedReplyForSendingWithConversationViewItem:(id)conversationItem + threadId:(NSString *)threadId transaction:(YapDatabaseReadTransaction *)transaction; { OWSAssertDebug(conversationItem); @@ -160,7 +166,8 @@ + (nullable instancetype)quotedReplyForSendingWithConversationViewItem:(id Promise { + let url = URL(string: "\(server)/channels/\(channel)?include_annotations=1")! + let request = TSRequest(url: url) + return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in + guard let json = rawResponse as? JSON, + let data = json["data"] as? JSON, + let annotations = data["annotations"] as? [JSON], + let infoAnnotation = annotations.first, + let info = infoAnnotation["value"] as? JSON, + let name = info["name"] as? String else { + print("[Loki] Couldn't parse info for group chat with ID: \(channel) on server: \(server) from: \(rawResponse).") + throw Error.parsingFailed + } + return LokiPublicChannel(name: name) + } + } + // MARK: Public API (Obj-C) @objc(getMessagesForGroup:onServer:) public static func objc_getMessages(for group: UInt64, on server: String) -> AnyPromise { diff --git a/Signal/src/Loki/Group Chat/GroupChatPoller.swift b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatPoller.swift similarity index 97% rename from Signal/src/Loki/Group Chat/GroupChatPoller.swift rename to SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatPoller.swift index 21eee0ce92d..0196e110d5e 100644 --- a/Signal/src/Loki/Group Chat/GroupChatPoller.swift +++ b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatPoller.swift @@ -1,6 +1,6 @@ @objc(LKGroupChatPoller) -public final class GroupChatPoller : NSObject { +public final class LokiGroupChatPoller : NSObject { private let group: LokiGroupChat private var pollForNewMessagesTimer: Timer? = nil private var pollForDeletedMessagesTimer: Timer? = nil @@ -50,7 +50,7 @@ public final class GroupChatPoller : NSObject { let endIndex = senderHexEncodedPublicKey.endIndex let cutoffIndex = senderHexEncodedPublicKey.index(endIndex, offsetBy: -8) let senderDisplayName = "\(message.displayName) (...\(senderHexEncodedPublicKey[cutoffIndex.. Promise { + guard let ourHexEncodedPublicKey = ourHexEncodedPublicKey else { return Promise(error: Error.userPublicKeyNotFound) } + return LokiGroupChatAPI.getAuthToken(for: server).then { token in + return LokiGroupChatAPI.getChannelInfo(channel, on: server) + }.map { channelInfo -> LokiGroupChat in + return self.addChat(server: server, channel: channel, name: channelInfo.name) + } + } + + @discardableResult + @objc(addChatWithServer:channel:name:) + public func addChat(server: String, channel: UInt64, name: String) -> LokiGroupChat { + let chat = LokiGroupChat(channel: channel, server: server, displayName: name, isDeletable: true) + let model = TSGroupModel(title: chat.displayName, memberIds: [ourHexEncodedPublicKey!, chat.server], image: nil, groupId: chat.idAsData!) + + // Store the group chat mapping + self.storage.dbReadWriteConnection.readWrite { transaction in + let thread = TSGroupThread.getOrCreateThread(with: model, transaction: transaction) + + // Mute the thread + if let utc = TimeZone(identifier: "UTC") { + var calendar = Calendar.current + calendar.timeZone = utc + var dateComponents = DateComponents() + dateComponents.setValue(999, for: .year) + if let date = calendar.date(byAdding: dateComponents, to: Date()) { + thread.updateWithMuted(until: date, transaction: transaction) + } + } + + // Save the group chat + self.storage.setGroupChat(chat, for: thread.uniqueId!, in: transaction) + } + + // Update chats and pollers + self.refreshChatsAndPollers() + + return chat + } + + @objc(addChatWithServer:channel:) + public func objc_addChat(server: String, channel: UInt64) -> AnyPromise { + return AnyPromise.from(addChat(server: server, channel: channel)) + } + + private func refreshChatsAndPollers() { + storage.dbReadConnection.read { transaction in + let newChats = self.storage.getAllGroupChats(with: transaction) + + // Remove any chats that don't exist in the database + let removedChatThreadIds = self.chats.keys.filter { !newChats.keys.contains($0) } + removedChatThreadIds.forEach { threadID in + let poller = self.pollers.removeValue(forKey: threadID) + poller?.stop() + } + + // Only append to chats if we have a thread for the chat + self.chats = newChats.filter { (threadID, group) in + return TSGroupThread.fetch(uniqueId: threadID, transaction: transaction) != nil + } + } + + if (isPolling) { startPollersIfNeeded() } + } + + @objc private func onThreadDeleted(_ notification: Notification) { + guard let threadId = notification.userInfo?["threadId"] as? String else { return } + storage.dbReadWriteConnection.readWrite { transaction in + self.storage.removeGroupChat(for: threadId, in: transaction) + } + + refreshChatsAndPollers() + } +} diff --git a/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift b/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift index 5cbb79dfa1d..3a703960110 100644 --- a/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift +++ b/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift @@ -27,7 +27,7 @@ public final class LokiDatabaseUtilities : NSObject { @objc(getAllGroupChats:) public static func objc_getAllGroupChats(in transaction: YapDatabaseReadTransaction) -> [String: LokiGroupChat] { - return OWSPrimaryStorage.shared().getAllGroupChats(in: transaction) + return OWSPrimaryStorage.shared().getAllGroupChats(with: transaction) } @objc(getGroupChatForThreadID:transaction:) diff --git a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift index 42d56ecc12f..9423e42ebd6 100644 --- a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift +++ b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift @@ -55,7 +55,7 @@ public extension OWSPrimaryStorage { return getDeviceLink(for: slaveHexEncodedPublicKey, in: transaction)?.master.hexEncodedPublicKey } - public func getAllGroupChats(in transaction: YapDatabaseReadTransaction) -> [String: LokiGroupChat] { + public func getAllGroupChats(with transaction: YapDatabaseReadTransaction) -> [String: LokiGroupChat] { var dict = [String: LokiGroupChat]() transaction.enumerateKeysAndObjects(inCollection: groupChatCollection) { (threadID, object, _) in if let groupChat = object as? LokiGroupChat { diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 3b646fcebdb..ad76d01fbb3 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -503,7 +503,10 @@ - (void)sendMessageToService:(TSOutgoingMessage *)message if ([message isKindOfClass:[OWSOutgoingSyncMessage class]]) { [recipientIds addObject:self.tsAccountManager.localNumber]; } else if (thread.isGroupThread) { - [recipientIds addObject:LKGroupChatAPI.publicChatServer]; + [_primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { + LKGroupChat *groupChat = [LKDatabaseUtilities getGroupChatForThreadID:thread.uniqueId transaction:transaction]; + if (groupChat != nil) { [recipientIds addObject:groupChat.server]; } + }]; } else if ([thread isKindOfClass:[TSContactThread class]]) { NSString *recipientContactId = ((TSContactThread *)thread).contactIdentifier; From 28d5e9c7a0c41b561855da7fbb7f21754b82c885 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Wed, 9 Oct 2019 16:19:58 +1100 Subject: [PATCH 04/17] Added Public chat VC --- Signal.xcodeproj/project.pbxproj | 4 + Signal/src/Loki/NewPublicChatVC.swift | 97 +++++++++++++++++++ .../HomeView/HomeViewController.m | 27 +++++- .../translations/en.lproj/Localizable.strings | 7 ++ .../Loki/API/Group Chat/LokiGroupChat.swift | 2 +- .../API/Group Chat/LokiGroupChatAPI.swift | 17 ++++ .../Group Chat/LokiPublicChatManager.swift | 20 +++- 7 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 Signal/src/Loki/NewPublicChatVC.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 1658f38daee..cd9ad0abc06 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 241C6315231F64CE00B4198E /* CGFloat+Rounding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241C6312231F5F1D00B4198E /* CGFloat+Rounding.swift */; }; 241C6316231F64CE00B4198E /* UIColor+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241C6310231F5C4400B4198E /* UIColor+Helper.swift */; }; 24A830A22293CD0100F4CAC0 /* LokiP2PServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */; }; + 24BD2609234DA2050008EB0A /* NewPublicChatVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24BD2608234DA2050008EB0A /* NewPublicChatVC.swift */; }; 2AE2882E4C2B96BFFF9EE27C /* Pods_SignalShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F94C85CB0B235DA37F68ED0 /* Pods_SignalShareExtension.framework */; }; 3403B95D20EA9527001A1F44 /* OWSContactShareButtonsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */; }; 34074F61203D0CBE004596AE /* OWSSounds.m in Sources */ = {isa = PBXBuildFile; fileRef = 34074F5F203D0CBD004596AE /* OWSSounds.m */; }; @@ -678,6 +679,7 @@ 241C6310231F5C4400B4198E /* UIColor+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Helper.swift"; sourceTree = ""; }; 241C6312231F5F1D00B4198E /* CGFloat+Rounding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGFloat+Rounding.swift"; sourceTree = ""; }; 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LokiP2PServer.swift; sourceTree = ""; }; + 24BD2608234DA2050008EB0A /* NewPublicChatVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPublicChatVC.swift; sourceTree = ""; }; 264242150E87D10A357DB07B /* Pods_SignalMessaging.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalMessaging.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactShareButtonsView.m; sourceTree = ""; }; 3403B95C20EA9527001A1F44 /* OWSContactShareButtonsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactShareButtonsView.h; sourceTree = ""; }; @@ -2649,6 +2651,7 @@ B89841E222B7579F00B1BDC6 /* NewConversationVC.swift */, B8258491230FA5DA001B41CB /* ScanQRCodeVC.h */, B8258492230FA5E9001B41CB /* ScanQRCodeVC.m */, + 24BD2608234DA2050008EB0A /* NewPublicChatVC.swift */, ); path = Loki; sourceTree = ""; @@ -3841,6 +3844,7 @@ B821F2F82272CED3002C88C0 /* DisplayNameVC.swift in Sources */, 34D8C0271ED3673300188D7C /* DebugUIMessages.m in Sources */, B885D5F62334A32100EE0D8E /* UIView+Constraint.swift in Sources */, + 24BD2609234DA2050008EB0A /* NewPublicChatVC.swift in Sources */, 34DBF003206BD5A500025978 /* OWSMessageTextView.m in Sources */, 34D1F0B41F86D31D0066283D /* ConversationCollectionView.m in Sources */, 34B3F8821E8DF1700035BE1A /* NewContactThreadViewController.m in Sources */, diff --git a/Signal/src/Loki/NewPublicChatVC.swift b/Signal/src/Loki/NewPublicChatVC.swift new file mode 100644 index 00000000000..1fb11a53656 --- /dev/null +++ b/Signal/src/Loki/NewPublicChatVC.swift @@ -0,0 +1,97 @@ + +@objc(LKNewPublicChatVC) +final class NewPublicChatVC : OWSViewController { + + // MARK: Components + private lazy var serverUrlTextField: UITextField = { + let result = UITextField() + result.textColor = Theme.primaryColor + result.font = UIFont.ows_dynamicTypeBodyClamped + let placeholder = NSMutableAttributedString(string: NSLocalizedString("Enter a Server URL", comment: "")) + placeholder.addAttribute(.foregroundColor, value: Theme.placeholderColor, range: NSRange(location: 0, length: placeholder.length)) + result.attributedPlaceholder = placeholder + result.tintColor = UIColor.lokiGreen() + result.keyboardAppearance = .dark + return result + }() + + private lazy var addButton: OWSFlatButton = { + let addButtonFont = UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight() + let addButtonHeight = addButtonFont.pointSize * 48 / 17 + let addButton = OWSFlatButton.button(title: NSLocalizedString("Add", comment: ""), font: addButtonFont, titleColor: .white, backgroundColor: .lokiGreen(), target: self, selector: #selector(handleNextButtonTapped)) + addButton.autoSetDimension(.height, toSize: addButtonHeight) + return addButton + }() + + // MARK: Lifecycle + override func viewDidLoad() { + // Background color & margins + view.backgroundColor = Theme.backgroundColor + view.layoutMargins = .zero + // Navigation bar + navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(close)) + title = NSLocalizedString("Add Public Chat Server", comment: "") + // Separator + let separator = UIView() + separator.autoSetDimension(.height, toSize: 1 / UIScreen.main.scale) + separator.backgroundColor = Theme.hairlineColor + + updateButton(enabled: true) + + // Stack view + let stackView = UIStackView(arrangedSubviews: [ + serverUrlTextField, + UIView.vStretchingSpacer(), + addButton + ]) + stackView.axis = .vertical + stackView.alignment = .fill + stackView.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) + stackView.isLayoutMarginsRelativeArrangement = true + view.addSubview(stackView) + stackView.autoPinWidthToSuperview() + stackView.autoPin(toTopLayoutGuideOf: self, withInset: 0) + autoPinView(toBottomOfViewControllerOrKeyboard: stackView, avoidNotch: true) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + serverUrlTextField.becomeFirstResponder() + } + + // MARK: Interaction + @objc private func close() { + dismiss(animated: true, completion: nil) + } + + @objc private func handleNextButtonTapped() { + let serverURL = (serverUrlTextField.text?.trimmingCharacters(in: .whitespaces) ?? "").lowercased().replacingOccurrences(of: "http://", with: "https://") + guard let url = URL(string: serverURL), let scheme = url.scheme, scheme == "https", let _ = url.host else { + showAlert(title: NSLocalizedString("Invalid server URL provided", comment: ""), message: NSLocalizedString("Please make sure you have provided the full url", comment: "")) + return + } + + updateButton(enabled: false) + + // TODO: Upon adding we should fetch previous messages + LokiPublicChatManager.shared.addChat(server: serverURL, channel: 1) + .done(on: .main) { _ in + self.presentingViewController!.dismiss(animated: true, completion: nil) + } + .catch(on: .main) { e in + self.updateButton(enabled: true) + self.showAlert(title: NSLocalizedString("Failed to connect to server", comment: "")) + } + } + + private func showAlert(title: String, message: String = "") { + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) + presentAlert(alert) + } + + private func updateButton(enabled: Bool) { + addButton.setEnabled(enabled) + addButton.setTitle(enabled ? NSLocalizedString("Add", comment: "") : NSLocalizedString("Connecting to server", comment: "")) + } +} diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 1b20159b630..bcdfec4f5ab 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -787,11 +787,21 @@ - (void)updateBarButtonItems self.navigationItem.leftBarButtonItem = settingsButton; SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, settingsButton); - self.navigationItem.rightBarButtonItem = - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose - target:self - action:@selector(showNewConversationView) - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"compose")]; + UIBarButtonItem *newConversation = [[UIBarButtonItem alloc] + initWithBarButtonSystemItem:UIBarButtonSystemItemCompose + target:self + action:@selector(showNewConversationView) + accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"compose")]; + + UIBarButtonItem *newServer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd + target:self + action:@selector(showNewPublicChatView) + accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"addServer")]; + + self.navigationItem.rightBarButtonItems = @[ + newConversation, + newServer, + ]; } - (void)settingsButtonPressed:(id)sender @@ -861,6 +871,13 @@ - (void)showNewConversationView */ } +- (void)showNewPublicChatView +{ + LKNewPublicChatVC *newPublicChatVC = [LKNewPublicChatVC new]; + OWSNavigationController *navigationController = [[OWSNavigationController alloc] initWithRootViewController:newPublicChatVC]; + [self.navigationController presentViewController:navigationController animated:YES completion:nil]; +} + - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index addb842c853..b838d0ed920 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -2551,6 +2551,8 @@ "Type an optional password for added security" = "Type an optional password for added security"; "Password (Optional)" = "Password (Optional)"; "Next" = "Next"; +"Add" = "Add"; +"Connecting to server" = "Connecting to server..."; "Please save the seed below in a safe location. It can be used to restore your account if you lose access, or to migrate to a new device." = "Please save the seed below in a safe location. It can be used to restore your account if you lose access, or to migrate to a new device."; "Restore your account by entering your seed below." = "Restore your account by entering your seed below."; "Copy" = "Copy"; @@ -2569,6 +2571,7 @@ "Start a Conversation" = "Start a Conversation"; "Invalid public key" = "Invalid public key"; "No search results" = "No search results"; +"Failed to connect to server" = "Failed to connect to server"; "Calculating proof of work" = "Calculating proof of work"; "Failed to calculate proof of work." = "Failed to calculate proof of work."; "Share Public Key" = "Share Public Key"; @@ -2594,7 +2597,9 @@ "Loki Messenger" = "Loki Messenger"; "Privacy Policy" = "Privacy Policy"; "New Conversation" = "New Conversation"; +"Add Public Chat Server" = "Add Public Chat Server"; "Enter a Public Key" = "Enter a Public Key"; +"Enter a Server URL" = "Enter a Server URL"; "For example: 059abcf223aa8c10e3dc2d623688b75dd25896794717e4a9c486772664fc95e41e." = "For example: 059abcf223aa8c10e3dc2d623688b75dd25896794717e4a9c486772664fc95e41e."; "Invalid Public Key" = "Invalid Public Key"; "Please check the public key you entered and try again." = "Please check the public key you entered and try again."; @@ -2636,3 +2641,5 @@ "Your device has been linked successfully" = "Your device has been linked successfully"; "Link" = "Link"; "Anonymous" = "Anonymous"; +"Invalid server URL provided" = "Invalid server URL provided"; +"Please make sure you have provided the full url" = "Please make sure you have provided the full url. E.g https://public-chat-server.url/"; diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift index ee93c86aaf9..c07bbd6cf3c 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift +++ b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift @@ -22,7 +22,7 @@ public final class LokiGroupChat : NSObject, NSCoding { @objc public init(channel: UInt64, server: String, displayName: String, isDeletable: Bool) { self.channel = channel - self.server = server + self.server = server.lowercased() self.displayName = displayName self.isDeletable = isDeletable } diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatAPI.swift b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatAPI.swift index 4ca2b58e2b0..072c12cafb9 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatAPI.swift +++ b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatAPI.swift @@ -36,6 +36,12 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { } } + private static func removeLastMessageServerID(for group: UInt64, on server: String) { + storage.dbReadWriteConnection.readWrite { transaction in + transaction.removeObject(forKey: "\(server).\(group)", inCollection: lastMessageServerIDCollection) + } + } + private static func getLastDeletionServerID(for group: UInt64, on server: String) -> UInt? { var result: UInt? = nil storage.dbReadConnection.read { transaction in @@ -50,6 +56,12 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { } } + private static func removeLastDeletionServerID(for group: UInt64, on server: String) { + storage.dbReadWriteConnection.readWrite { transaction in + transaction.removeObject(forKey: "\(server).\(group)", inCollection: lastDeletionServerIDCollection) + } + } + // MARK: Public API public static func getMessages(for group: UInt64, on server: String) -> Promise<[LokiGroupMessage]> { print("[Loki] Getting messages for group chat with ID: \(group) on server: \(server).") @@ -228,6 +240,11 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { } } + public static func resetLastMessageCache(for group: UInt64, on server: String) { + removeLastMessageServerID(for: group, on: server) + removeLastDeletionServerID(for: group, on: server) + } + // MARK: Public API (Obj-C) @objc(getMessagesForGroup:onServer:) public static func objc_getMessages(for group: UInt64, on server: String) -> AnyPromise { diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift b/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift index 313fbbf6b48..18e3b27dcf6 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift +++ b/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift @@ -46,7 +46,10 @@ public final class LokiPublicChatManager: NSObject { } public func addChat(server: String, channel: UInt64) -> Promise { - guard let ourHexEncodedPublicKey = ourHexEncodedPublicKey else { return Promise(error: Error.userPublicKeyNotFound) } + if let existingChat = getChat(server: server, channel: channel) { + return Promise.value(self.addChat(server: server, channel: channel, name: existingChat.displayName)) + } + return LokiGroupChatAPI.getAuthToken(for: server).then { token in return LokiGroupChatAPI.getChannelInfo(channel, on: server) }.map { channelInfo -> LokiGroupChat in @@ -112,10 +115,23 @@ public final class LokiPublicChatManager: NSObject { @objc private func onThreadDeleted(_ notification: Notification) { guard let threadId = notification.userInfo?["threadId"] as? String else { return } + + // Reset the last message cache + if let chat = self.chats[threadId] { + LokiGroupChatAPI.resetLastMessageCache(for: chat.channel, on: chat.server) + } + + // Remove the chat from the db storage.dbReadWriteConnection.readWrite { transaction in self.storage.removeGroupChat(for: threadId, in: transaction) } - + refreshChatsAndPollers() } + + private func getChat(server: String, channel: UInt64) -> LokiGroupChat? { + return chats.values.first { chat in + return chat.server == server && chat.channel == channel + } + } } From ddc9ead05ceedeefdc5c1b875c9225911bf94b53 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 14 Oct 2019 13:04:06 +1100 Subject: [PATCH 05/17] Fix build --- Signal/src/Loki/MentionCandidateSelectionView.swift | 2 +- Signal/src/Loki/Utilities/MentionUtilities.swift | 2 +- SignalServiceKit/src/Loki/API/LokiAPI.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Signal/src/Loki/MentionCandidateSelectionView.swift b/Signal/src/Loki/MentionCandidateSelectionView.swift index 327df3e2584..c7fa86ad43d 100644 --- a/Signal/src/Loki/MentionCandidateSelectionView.swift +++ b/Signal/src/Loki/MentionCandidateSelectionView.swift @@ -121,7 +121,7 @@ private extension MentionCandidateSelectionView { displayNameLabel.text = mentionCandidate.displayName let profilePicture = OWSContactAvatarBuilder(signalId: mentionCandidate.hexEncodedPublicKey, colorName: .blue, diameter: 36).build() profilePictureImageView.image = profilePicture - let isUserModerator = LokiGroupChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, for: LokiGroupChatAPI.publicChatServerID, on: LokiGroupChatAPI.publicChatServer) + let isUserModerator = LokiGroupChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, for: 1, on: "https://chat.lokinet.org") // FIXME: Mentions need to work for every kind of chat moderatorIconImageView.isHidden = !isUserModerator || !hasGroupContext } } diff --git a/Signal/src/Loki/Utilities/MentionUtilities.swift b/Signal/src/Loki/Utilities/MentionUtilities.swift index 458c2bee14a..49c8304c494 100644 --- a/Signal/src/Loki/Utilities/MentionUtilities.swift +++ b/Signal/src/Loki/Utilities/MentionUtilities.swift @@ -23,7 +23,7 @@ public final class MentionUtilities : NSObject { userDisplayName = OWSProfileManager.shared().localProfileName() } else { OWSPrimaryStorage.shared().dbReadConnection.read { transaction in - let collection = "\(LokiGroupChatAPI.publicChatServer).\(LokiGroupChatAPI.publicChatServerID)" + let collection = "https://chat.lokinet.org.1" // FIXME: Mentions need to work for every kind of chat userDisplayName = transaction.object(forKey: hexEncodedPublicKey, inCollection: collection) as! String? } } diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift index 878f7a1e947..c19652ab1bb 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift @@ -310,7 +310,7 @@ public final class LokiAPI : NSObject { var candidates: [Mention] = [] // Gather candidates storage.dbReadConnection.read { transaction in - let collection = "\(LokiGroupChatAPI.publicChatServer).\(LokiGroupChatAPI.publicChatServerID)" + let collection = "https://chat.lokinet.org.1" // FIXME: Mentions need to work for every kind of chat candidates = cache.flatMap { hexEncodedPublicKey in guard let displayName = transaction.object(forKey: hexEncodedPublicKey, inCollection: collection) as! String? else { return nil } guard !displayName.hasPrefix("Anonymous") else { return nil } From 78ce3e23c24390215db7f3a960aa18df0cb91eb1 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 14 Oct 2019 13:41:32 +1100 Subject: [PATCH 06/17] Clean up NewPublicChatVC --- Signal/src/Loki/NewConversationVC.swift | 4 +- Signal/src/Loki/NewPublicChatVC.swift | 91 +++++++++---------- .../translations/en.lproj/Localizable.strings | 10 +- 3 files changed, 55 insertions(+), 50 deletions(-) diff --git a/Signal/src/Loki/NewConversationVC.swift b/Signal/src/Loki/NewConversationVC.swift index 7fd3fcfe481..46c7b2cc37f 100644 --- a/Signal/src/Loki/NewConversationVC.swift +++ b/Signal/src/Loki/NewConversationVC.swift @@ -6,11 +6,11 @@ final class NewConversationVC : OWSViewController, OWSQRScannerDelegate { private lazy var publicKeyTextField: UITextField = { let result = UITextField() result.textColor = Theme.primaryColor - result.font = UIFont.ows_dynamicTypeBodyClamped + result.font = .ows_dynamicTypeBodyClamped let placeholder = NSMutableAttributedString(string: NSLocalizedString("Enter a Public Key", comment: "")) placeholder.addAttribute(.foregroundColor, value: Theme.placeholderColor, range: NSRange(location: 0, length: placeholder.length)) result.attributedPlaceholder = placeholder - result.tintColor = UIColor.lokiGreen() + result.tintColor = .lokiGreen() result.keyboardAppearance = .dark return result }() diff --git a/Signal/src/Loki/NewPublicChatVC.swift b/Signal/src/Loki/NewPublicChatVC.swift index 1fb11a53656..dd85e5db338 100644 --- a/Signal/src/Loki/NewPublicChatVC.swift +++ b/Signal/src/Loki/NewPublicChatVC.swift @@ -3,25 +3,21 @@ final class NewPublicChatVC : OWSViewController { // MARK: Components - private lazy var serverUrlTextField: UITextField = { + private lazy var urlTextField: UITextField = { let result = UITextField() result.textColor = Theme.primaryColor - result.font = UIFont.ows_dynamicTypeBodyClamped - let placeholder = NSMutableAttributedString(string: NSLocalizedString("Enter a Server URL", comment: "")) + result.font = .ows_dynamicTypeBodyClamped + let placeholder = NSMutableAttributedString(string: NSLocalizedString("Enter a URL", comment: "")) placeholder.addAttribute(.foregroundColor, value: Theme.placeholderColor, range: NSRange(location: 0, length: placeholder.length)) result.attributedPlaceholder = placeholder - result.tintColor = UIColor.lokiGreen() + result.tintColor = .lokiGreen() result.keyboardAppearance = .dark + result.keyboardType = .URL + result.autocapitalizationType = .none return result }() - private lazy var addButton: OWSFlatButton = { - let addButtonFont = UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight() - let addButtonHeight = addButtonFont.pointSize * 48 / 17 - let addButton = OWSFlatButton.button(title: NSLocalizedString("Add", comment: ""), font: addButtonFont, titleColor: .white, backgroundColor: .lokiGreen(), target: self, selector: #selector(handleNextButtonTapped)) - addButton.autoSetDimension(.height, toSize: addButtonHeight) - return addButton - }() + private lazy var addButton = OWSFlatButton.button(title: NSLocalizedString("Add", comment: ""), font: UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight(), titleColor: .white, backgroundColor: .lokiGreen(), target: self, selector: #selector(handleAddButtonTapped)) // MARK: Lifecycle override func viewDidLoad() { @@ -30,20 +26,24 @@ final class NewPublicChatVC : OWSViewController { view.layoutMargins = .zero // Navigation bar navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(close)) - title = NSLocalizedString("Add Public Chat Server", comment: "") + title = NSLocalizedString("Add Public Chat", comment: "") // Separator let separator = UIView() separator.autoSetDimension(.height, toSize: 1 / UIScreen.main.scale) separator.backgroundColor = Theme.hairlineColor - - updateButton(enabled: true) - + // Explanation label + let explanationLabel = UILabel() + explanationLabel.textColor = Theme.primaryColor + explanationLabel.font = UIFont.ows_dynamicTypeSubheadlineClamped + explanationLabel.text = NSLocalizedString("Enter the URL of the public chat you'd like to join. The Loki Public Chat URL is https://chat.lokinet.org.", comment: "") + explanationLabel.numberOfLines = 0 + explanationLabel.lineBreakMode = .byWordWrapping + // Add button + let addButtonHeight = addButton.button.titleLabel!.font.pointSize * 48 / 17 + addButton.autoSetDimension(.height, toSize: addButtonHeight) + updateAddButton(isConnecting: false) // Stack view - let stackView = UIStackView(arrangedSubviews: [ - serverUrlTextField, - UIView.vStretchingSpacer(), - addButton - ]) + let stackView = UIStackView(arrangedSubviews: [ urlTextField, UIView.spacer(withHeight: 8), separator, UIView.spacer(withHeight: 24), explanationLabel, UIView.vStretchingSpacer(), addButton ]) stackView.axis = .vertical stackView.alignment = .fill stackView.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) @@ -56,7 +56,20 @@ final class NewPublicChatVC : OWSViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - serverUrlTextField.becomeFirstResponder() + urlTextField.becomeFirstResponder() + } + + // MARK: Updating + private func updateAddButton(isConnecting: Bool) { + addButton.setEnabled(!isConnecting) + addButton.setTitle(isConnecting ? NSLocalizedString("Connecting...", comment: "") : NSLocalizedString("Add", comment: "")) + } + + // MARK: General + private func showError(title: String, message: String = "") { + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) + presentAlert(alert) } // MARK: Interaction @@ -64,34 +77,20 @@ final class NewPublicChatVC : OWSViewController { dismiss(animated: true, completion: nil) } - @objc private func handleNextButtonTapped() { - let serverURL = (serverUrlTextField.text?.trimmingCharacters(in: .whitespaces) ?? "").lowercased().replacingOccurrences(of: "http://", with: "https://") - guard let url = URL(string: serverURL), let scheme = url.scheme, scheme == "https", let _ = url.host else { - showAlert(title: NSLocalizedString("Invalid server URL provided", comment: ""), message: NSLocalizedString("Please make sure you have provided the full url", comment: "")) - return + @objc private func handleAddButtonTapped() { + let uncheckedURL = (urlTextField.text?.trimmingCharacters(in: .whitespaces) ?? "").lowercased().replacingOccurrences(of: "http://", with: "https://") + guard let url = URL(string: uncheckedURL), let scheme = url.scheme, scheme == "https", url.host != nil else { + return showError(title: NSLocalizedString("Invalid URL", comment: ""), message: NSLocalizedString("Please check the URL you entered and try again.", comment: "")) } - - updateButton(enabled: false) - + updateAddButton(isConnecting: true) // TODO: Upon adding we should fetch previous messages - LokiPublicChatManager.shared.addChat(server: serverURL, channel: 1) - .done(on: .main) { _ in - self.presentingViewController!.dismiss(animated: true, completion: nil) + LokiPublicChatManager.shared.addChat(server: url.absoluteString, channel: 1) + .done(on: .main) { [weak self] _ in + self?.presentingViewController!.dismiss(animated: true, completion: nil) } - .catch(on: .main) { e in - self.updateButton(enabled: true) - self.showAlert(title: NSLocalizedString("Failed to connect to server", comment: "")) + .catch(on: .main) { [weak self] _ in + self?.updateAddButton(isConnecting: false) + self?.showError(title: NSLocalizedString("Couldn't Connect", comment: "")) } } - - private func showAlert(title: String, message: String = "") { - let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) - presentAlert(alert) - } - - private func updateButton(enabled: Bool) { - addButton.setEnabled(enabled) - addButton.setTitle(enabled ? NSLocalizedString("Add", comment: "") : NSLocalizedString("Connecting to server", comment: "")) - } } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index bc56b04cee5..af0bc2ac610 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -2640,7 +2640,13 @@ "Your device has been linked successfully" = "Your device has been linked successfully"; "Link" = "Link"; "Anonymous" = "Anonymous"; -"Invalid server URL provided" = "Invalid server URL provided"; -"Please make sure you have provided the full url" = "Please make sure you have provided the full url. E.g https://public-chat-server.url/"; +"Invalid URL" = "Invalid URL"; +"Please check the URL you entered and try again." = "Please check the URL you entered and try again."; "Please pick a shorter display name" = "Please pick a shorter display name"; "Please pick a display name" = "Please pick a display name"; +"Add Public Chat" = "Add Public Chat"; +"Enter a URL" = "Enter a URL"; +"Enter the URL of the public chat you'd like to join. The Loki Public Chat URL is https://chat.lokinet.org." = "Enter the URL of the public chat you'd like to join. The Loki Public Chat URL is https://chat.lokinet.org."; +"Connecting..." = "Connecting..."; +"Add" = "Add"; +"Couldn't Connect" = "Couldn't Connect"; From 0f5ac64363f9a61d3b3cc0314e16464fcfe87146 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 14 Oct 2019 13:45:50 +1100 Subject: [PATCH 07/17] Add missing messages fetch & display name update --- Signal/src/Loki/NewPublicChatVC.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Signal/src/Loki/NewPublicChatVC.swift b/Signal/src/Loki/NewPublicChatVC.swift index dd85e5db338..f815a149d07 100644 --- a/Signal/src/Loki/NewPublicChatVC.swift +++ b/Signal/src/Loki/NewPublicChatVC.swift @@ -83,9 +83,12 @@ final class NewPublicChatVC : OWSViewController { return showError(title: NSLocalizedString("Invalid URL", comment: ""), message: NSLocalizedString("Please check the URL you entered and try again.", comment: "")) } updateAddButton(isConnecting: true) - // TODO: Upon adding we should fetch previous messages - LokiPublicChatManager.shared.addChat(server: url.absoluteString, channel: 1) + let urlAsString = url.absoluteString + let displayName = OWSProfileManager.shared().localProfileName() + LokiPublicChatManager.shared.addChat(server: urlAsString, channel: 1) .done(on: .main) { [weak self] _ in + let _ = LokiGroupChatAPI.getMessages(for: 1, on: urlAsString) + let _ = LokiGroupChatAPI.setDisplayName(to: displayName, on: urlAsString) self?.presentingViewController!.dismiss(animated: true, completion: nil) } .catch(on: .main) { [weak self] _ in From 1d8bb5fa8a85de26c9c4c8f709eb3c36e93c817e Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 14 Oct 2019 13:55:03 +1100 Subject: [PATCH 08/17] Update MentionCandidateSelectionView for group chat changes --- .../Loki/MentionCandidateSelectionView.swift | 23 +++++++++++++++---- .../ConversationInputToolbar.m | 7 +++++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Signal/src/Loki/MentionCandidateSelectionView.swift b/Signal/src/Loki/MentionCandidateSelectionView.swift index c7fa86ad43d..b6c1798eddb 100644 --- a/Signal/src/Loki/MentionCandidateSelectionView.swift +++ b/Signal/src/Loki/MentionCandidateSelectionView.swift @@ -4,9 +4,16 @@ @objc(LKMentionCandidateSelectionView) final class MentionCandidateSelectionView : UIView, UITableViewDataSource, UITableViewDelegate { @objc var mentionCandidates: [Mention] = [] { didSet { tableView.reloadData() } } - @objc var hasGroupContext = false + @objc var publicChatServer: String? + var publicChatServerID: UInt64? @objc var delegate: MentionCandidateSelectionViewDelegate? + // MARK: Convenience + @objc(setPublicChatServerID:) + func setPublicChatServerID(to publicChatServerID: UInt64) { + self.publicChatServerID = publicChatServerID != 0 ? publicChatServerID : nil + } + // MARK: Components @objc lazy var tableView: UITableView = { // TODO: Make this private let result = UITableView() @@ -44,7 +51,8 @@ final class MentionCandidateSelectionView : UIView, UITableViewDataSource, UITab let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! Cell let mentionCandidate = mentionCandidates[indexPath.row] cell.mentionCandidate = mentionCandidate - cell.hasGroupContext = hasGroupContext + cell.publicChatServer = publicChatServer + cell.publicChatServerID = publicChatServerID return cell } @@ -61,7 +69,8 @@ private extension MentionCandidateSelectionView { final class Cell : UITableViewCell { var mentionCandidate = Mention(hexEncodedPublicKey: "", displayName: "") { didSet { update() } } - var hasGroupContext = false + var publicChatServer: String? + var publicChatServerID: UInt64? // MARK: Components private lazy var profilePictureImageView = AvatarImageView() @@ -121,8 +130,12 @@ private extension MentionCandidateSelectionView { displayNameLabel.text = mentionCandidate.displayName let profilePicture = OWSContactAvatarBuilder(signalId: mentionCandidate.hexEncodedPublicKey, colorName: .blue, diameter: 36).build() profilePictureImageView.image = profilePicture - let isUserModerator = LokiGroupChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, for: 1, on: "https://chat.lokinet.org") // FIXME: Mentions need to work for every kind of chat - moderatorIconImageView.isHidden = !isUserModerator || !hasGroupContext + if let publicChatServer = publicChatServer, let publicChatServerID = publicChatServerID { + let isUserModerator = LokiGroupChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, for: publicChatServerID, on: publicChatServer) + moderatorIconImageView.isHidden = !isUserModerator + } else { + moderatorIconImageView.isHidden = true + } } } } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m index 4839430a98d..141f448df7d 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m @@ -1093,7 +1093,12 @@ - (void)hideInputMethod - (void)showMentionCandidateSelectionViewFor:(NSArray *)mentionCandidates in:(TSThread *)thread { - self.mentionCandidateSelectionView.hasGroupContext = thread.isGroupThread; // Must happen before setting the users + __block LKGroupChat *groupChat; + [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + groupChat = [LKDatabaseUtilities getGroupChatForThreadID:thread.uniqueId transaction:transaction]; + }]; + self.mentionCandidateSelectionView.publicChatServer = groupChat.server; + [self.mentionCandidateSelectionView setPublicChatServerID:groupChat.channel]; self.mentionCandidateSelectionView.mentionCandidates = mentionCandidates; self.mentionCandidateSelectionViewSizeConstraint.constant = 6 + MIN(mentionCandidates.count, 4) * 52; [self setNeedsLayout]; From 294d44c7732c12922df254490f18024ac8f212f5 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 14 Oct 2019 13:58:56 +1100 Subject: [PATCH 09/17] Update MentionUtilities for group chat changes --- Signal/src/Loki/Utilities/MentionUtilities.swift | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Signal/src/Loki/Utilities/MentionUtilities.swift b/Signal/src/Loki/Utilities/MentionUtilities.swift index 49c8304c494..0a1247bdb00 100644 --- a/Signal/src/Loki/Utilities/MentionUtilities.swift +++ b/Signal/src/Loki/Utilities/MentionUtilities.swift @@ -9,6 +9,10 @@ public final class MentionUtilities : NSObject { } @objc public static func highlightMentions(in string: String, isOutgoingMessage: Bool, thread: TSThread, attributes: [NSAttributedString.Key:Any]) -> NSAttributedString { + var groupChat: LokiGroupChat? + OWSPrimaryStorage.shared().dbReadConnection.read { transaction in + groupChat = LokiDatabaseUtilities.objc_getGroupChat(for: thread.uniqueId!, in: transaction) + } var string = string let regex = try! NSRegularExpression(pattern: "@[0-9a-fA-F]*", options: []) let knownUserHexEncodedPublicKeys = LokiAPI.userHexEncodedPublicKeyCache[thread.uniqueId!] ?? [] // Should always be populated at this point @@ -22,9 +26,13 @@ public final class MentionUtilities : NSObject { if hexEncodedPublicKey == OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey { userDisplayName = OWSProfileManager.shared().localProfileName() } else { - OWSPrimaryStorage.shared().dbReadConnection.read { transaction in - let collection = "https://chat.lokinet.org.1" // FIXME: Mentions need to work for every kind of chat - userDisplayName = transaction.object(forKey: hexEncodedPublicKey, inCollection: collection) as! String? + if let groupChat = groupChat { + OWSPrimaryStorage.shared().dbReadConnection.read { transaction in + let collection = "\(groupChat.server).\(groupChat.channel)" + userDisplayName = transaction.object(forKey: hexEncodedPublicKey, inCollection: collection) as! String? + } + } else { + userDisplayName = nil // TODO: Implement } } if let userDisplayName = userDisplayName { From 7b0e2d06b078e5cee65975549e4f221d675927ff Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 14 Oct 2019 14:40:18 +1100 Subject: [PATCH 10/17] Partially implement feedback --- Signal.xcodeproj/project.pbxproj | 4 ++ Signal/src/Loki/NewPublicChatVC.swift | 5 ++- .../src/Loki/Utilities/MentionUtilities.swift | 19 ++++----- .../ConversationView/Cells/OWSMessageCell.m | 11 ++---- .../Cells/OWSQuotedMessageView.m | 10 ++--- .../ConversationView/ConversationViewItem.m | 2 +- .../ViewControllers/DebugUI/DebugUIMessages.m | 2 +- .../HomeView/HomeViewController.m | 18 +++------ .../translations/en.lproj/Localizable.strings | 3 -- .../Loki/DisplayNameUtilities.swift | 39 +++++++++++++++++++ .../ViewModels/OWSQuotedReplyModel.h | 2 - .../environment/VersionMigrations.m | 2 +- SignalMessaging/profiles/OWSProfileManager.m | 12 +++--- ...ChatPoller.swift => GroupChatPoller.swift} | 6 +-- .../Loki/API/Group Chat/LokiGroupChat.swift | 29 ++++++-------- .../API/Group Chat/LokiGroupChatAPI.swift | 4 +- .../Group Chat/LokiPublicChatManager.swift | 6 +-- SignalServiceKit/src/Loki/API/LokiAPI.swift | 13 ++++++- .../Loki/Database/LokiDatabaseUtilities.swift | 4 +- 19 files changed, 112 insertions(+), 79 deletions(-) create mode 100644 SignalMessaging/Loki/DisplayNameUtilities.swift rename SignalServiceKit/src/Loki/API/Group Chat/{LokiGroupChatPoller.swift => GroupChatPoller.swift} (97%) diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 2c08f62f4b7..31219927def 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -568,6 +568,7 @@ B82584A02315024B001B41CB /* RSSFeedPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = B825849F2315024B001B41CB /* RSSFeedPoller.swift */; }; B846365B22B7418B00AF1514 /* Identicon+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */; }; B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84664F4235022F30083A1CD /* MentionUtilities.swift */; }; + B84664F923541F070083A1CD /* DisplayNameUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84664F823541F070083A1CD /* DisplayNameUtilities.swift */; }; B86BD08123399883000F5AE3 /* QRCodeModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08023399883000F5AE3 /* QRCodeModal.swift */; }; B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; }; B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; }; @@ -1380,6 +1381,7 @@ B825849F2315024B001B41CB /* RSSFeedPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSSFeedPoller.swift; sourceTree = ""; }; B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Identicon+ObjC.swift"; sourceTree = ""; }; B84664F4235022F30083A1CD /* MentionUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionUtilities.swift; sourceTree = ""; }; + B84664F823541F070083A1CD /* DisplayNameUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayNameUtilities.swift; sourceTree = ""; }; B86BD08023399883000F5AE3 /* QRCodeModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeModal.swift; sourceTree = ""; }; B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = ""; }; B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = ""; }; @@ -2671,6 +2673,7 @@ B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */, 241C6312231F5F1D00B4198E /* CGFloat+Rounding.swift */, 241C6310231F5C4400B4198E /* UIColor+Helper.swift */, + B84664F823541F070083A1CD /* DisplayNameUtilities.swift */, ); path = Loki; sourceTree = ""; @@ -3575,6 +3578,7 @@ 450998681FD8C0FF00D89EB3 /* AttachmentSharing.m in Sources */, 347850711FDAEB17007B8332 /* OWSUserProfile.m in Sources */, 346129F81FD5F31400532771 /* OWS100RemoveTSRecipientsMigration.m in Sources */, + B84664F923541F070083A1CD /* DisplayNameUtilities.swift in Sources */, 34AC09DF211B39B100997B47 /* OWSNavigationController.m in Sources */, 34074F61203D0CBE004596AE /* OWSSounds.m in Sources */, 34BEDB1721C80BCA007B0EAE /* OWSAnyTouchGestureRecognizer.m in Sources */, diff --git a/Signal/src/Loki/NewPublicChatVC.swift b/Signal/src/Loki/NewPublicChatVC.swift index f815a149d07..2d0e952606f 100644 --- a/Signal/src/Loki/NewPublicChatVC.swift +++ b/Signal/src/Loki/NewPublicChatVC.swift @@ -83,11 +83,12 @@ final class NewPublicChatVC : OWSViewController { return showError(title: NSLocalizedString("Invalid URL", comment: ""), message: NSLocalizedString("Please check the URL you entered and try again.", comment: "")) } updateAddButton(isConnecting: true) + let channelID: UInt64 = 1 let urlAsString = url.absoluteString let displayName = OWSProfileManager.shared().localProfileName() - LokiPublicChatManager.shared.addChat(server: urlAsString, channel: 1) + LokiPublicChatManager.shared.addChat(server: urlAsString, channel: channelID) .done(on: .main) { [weak self] _ in - let _ = LokiGroupChatAPI.getMessages(for: 1, on: urlAsString) + let _ = LokiGroupChatAPI.getMessages(for: channelID, on: urlAsString) let _ = LokiGroupChatAPI.setDisplayName(to: displayName, on: urlAsString) self?.presentingViewController!.dismiss(animated: true, completion: nil) } diff --git a/Signal/src/Loki/Utilities/MentionUtilities.swift b/Signal/src/Loki/Utilities/MentionUtilities.swift index 0a1247bdb00..3268a045d2c 100644 --- a/Signal/src/Loki/Utilities/MentionUtilities.swift +++ b/Signal/src/Loki/Utilities/MentionUtilities.swift @@ -4,21 +4,21 @@ public final class MentionUtilities : NSObject { override private init() { } - @objc public static func highlightMentions(in string: String, thread: TSThread) -> String { - return highlightMentions(in: string, isOutgoingMessage: false, thread: thread, attributes: [:]).string // isOutgoingMessage and attributes are irrelevant + @objc public static func highlightMentions(in string: String, threadID: String) -> String { + return highlightMentions(in: string, isOutgoingMessage: false, threadID: threadID, attributes: [:]).string // isOutgoingMessage and attributes are irrelevant } - @objc public static func highlightMentions(in string: String, isOutgoingMessage: Bool, thread: TSThread, attributes: [NSAttributedString.Key:Any]) -> NSAttributedString { + @objc public static func highlightMentions(in string: String, isOutgoingMessage: Bool, threadID: String, attributes: [NSAttributedString.Key:Any]) -> NSAttributedString { var groupChat: LokiGroupChat? OWSPrimaryStorage.shared().dbReadConnection.read { transaction in - groupChat = LokiDatabaseUtilities.objc_getGroupChat(for: thread.uniqueId!, in: transaction) + groupChat = LokiDatabaseUtilities.getGroupChat(for: threadID, in: transaction) } var string = string let regex = try! NSRegularExpression(pattern: "@[0-9a-fA-F]*", options: []) - let knownUserHexEncodedPublicKeys = LokiAPI.userHexEncodedPublicKeyCache[thread.uniqueId!] ?? [] // Should always be populated at this point + let knownUserHexEncodedPublicKeys = LokiAPI.userHexEncodedPublicKeyCache[threadID] ?? [] // Should always be populated at this point var mentions: [NSRange] = [] var outerMatch = regex.firstMatch(in: string, options: .withoutAnchoringBounds, range: NSRange(location: 0, length: string.count)) - while let match = outerMatch, thread.isGroupThread() { + while let match = outerMatch { let hexEncodedPublicKey = String((string as NSString).substring(with: match.range).dropFirst()) // Drop the @ let matchEnd: Int if knownUserHexEncodedPublicKeys.contains(hexEncodedPublicKey) { @@ -27,12 +27,9 @@ public final class MentionUtilities : NSObject { userDisplayName = OWSProfileManager.shared().localProfileName() } else { if let groupChat = groupChat { - OWSPrimaryStorage.shared().dbReadConnection.read { transaction in - let collection = "\(groupChat.server).\(groupChat.channel)" - userDisplayName = transaction.object(forKey: hexEncodedPublicKey, inCollection: collection) as! String? - } + userDisplayName = DisplayNameUtilities.getGroupChatDisplayName(for: hexEncodedPublicKey, in: groupChat.channel, on: groupChat.server) } else { - userDisplayName = nil // TODO: Implement + userDisplayName = DisplayNameUtilities.getPrivateChatDisplayName(for: hexEncodedPublicKey) } } if let userDisplayName = userDisplayName { diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index a8f59839cc4..ecde97570b3 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -303,13 +303,10 @@ - (BOOL)updateAvatarView [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { groupChat = [LKDatabaseUtilities getGroupChatForThreadID:self.viewItem.interaction.uniqueThreadId transaction: transaction]; }]; - - if (groupChat != nil) { - BOOL isModerator = [LKGroupChatAPI isUserModerator:incomingMessage.authorId forGroup:groupChat.channel onServer:groupChat.server]; - UIImage *moderatorIcon = [UIImage imageNamed:@"Crown"]; - self.moderatorIconImageView.image = moderatorIcon; - self.moderatorIconImageView.hidden = !isModerator; - } + BOOL isModerator = [LKGroupChatAPI isUserModerator:incomingMessage.authorId forGroup:groupChat.channel onServer:groupChat.server]; + UIImage *moderatorIcon = [UIImage imageNamed:@"Crown"]; + self.moderatorIconImageView.image = moderatorIcon; + self.moderatorIconImageView.hidden = !isModerator; } [self.contentView addSubview:self.moderatorIconImageView]; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index 65409f09663..680a1df7dc2 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -554,11 +554,11 @@ - (UILabel *)configureQuotedAuthorLabel if (quotedAuthor == self.quotedMessage.authorId) { [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - LKGroupChat *chat = [LKDatabaseUtilities getGroupChatForThreadID:self.quotedMessage.threadId transaction:transaction]; - if (chat != nil) { - NSString *collection = [NSString stringWithFormat:@"%@.%@", chat.server, @(chat.channel)]; - NSString *displayName = [transaction stringForKey:self.quotedMessage.authorId inCollection:collection]; - if (displayName != nil) { quotedAuthor = displayName; } + LKGroupChat *groupChat = [LKDatabaseUtilities getGroupChatForThreadID:self.quotedMessage.threadId transaction:transaction]; + if (groupChat != nil) { + quotedAuthor = [LKDisplayNameUtilities getGroupChatDisplayNameFor:self.quotedMessage.authorId in:groupChat.channel on:groupChat.server using:transaction];; + } else { + quotedAuthor = [LKDisplayNameUtilities getPrivateChatDisplayNameFor:self.quotedMessage.authorId]; } }]; } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 5421291e95d..cdc1ee68a93 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -1262,7 +1262,7 @@ - (BOOL)userCanDeleteGroupMessage TSMessage *message = (TSMessage *)self.interaction; if (!message.isGroupChatMessage) return false; - // Make sure we have the details to contact the server + // Ensure we have the details needed to contact the server __block LKGroupChat *groupChat; [self.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { groupChat = [LKDatabaseUtilities getGroupChatForThreadID:groupThread.uniqueId transaction: transaction]; diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index 67b29b6fed2..23a2caee2e2 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -2016,7 +2016,7 @@ + (DebugUIMessagesAction *)fakeRandomTextAction:(TSThread *)thread transaction:transaction conversationStyle:conversationStyle]; quotedMessage = [ - [OWSQuotedReplyModel quotedReplyForSendingWithConversationViewItem:viewItem threadId:viewItem.interaction.uniqueThreadId transaction:transaction] + [OWSQuotedReplyModel quotedReplyForSendingWithConversationViewItem:viewItem threadId:viewItem.interaction.uniqueThreadId transaction:transaction] buildQuotedMessageForSending]; } else { TSOutgoingMessage *_Nullable messageToQuote = [self createFakeOutgoingMessage:thread diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index bcdfec4f5ab..23286342ff2 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -787,21 +787,15 @@ - (void)updateBarButtonItems self.navigationItem.leftBarButtonItem = settingsButton; SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, settingsButton); - UIBarButtonItem *newConversation = [[UIBarButtonItem alloc] + UIBarButtonItem *newPrivateChatButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self - action:@selector(showNewConversationView) + action:@selector(showNewConversationVC) accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"compose")]; - UIBarButtonItem *newServer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd - target:self - action:@selector(showNewPublicChatView) - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"addServer")]; + UIBarButtonItem *newGroupChatButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"group-avatar"] style:UIBarButtonItemStylePlain target:self action:@selector(showNewPublicChatVC)]; - self.navigationItem.rightBarButtonItems = @[ - newConversation, - newServer, - ]; + self.navigationItem.rightBarButtonItems = @[ newPrivateChatButton, newGroupChatButton ]; } - (void)settingsButtonPressed:(id)sender @@ -843,7 +837,7 @@ - (void)previewingContext:(id)previewingContext [self.navigationController pushViewController:vc animated:NO]; } -- (void)showNewConversationView +- (void)showNewConversationVC { LKNewConversationVC *newConversationVC = [LKNewConversationVC new]; OWSNavigationController *navigationController = [[OWSNavigationController alloc] initWithRootViewController:newConversationVC]; @@ -871,7 +865,7 @@ - (void)showNewConversationView */ } -- (void)showNewPublicChatView +- (void)showNewPublicChatVC { LKNewPublicChatVC *newPublicChatVC = [LKNewPublicChatVC new]; OWSNavigationController *navigationController = [[OWSNavigationController alloc] initWithRootViewController:newPublicChatVC]; diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index af0bc2ac610..ae25f740408 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -2552,7 +2552,6 @@ "Password (Optional)" = "Password (Optional)"; "Next" = "Next"; "Add" = "Add"; -"Connecting to server" = "Connecting to server..."; "Please save the seed below in a safe location. It can be used to restore your account if you lose access, or to migrate to a new device." = "Please save the seed below in a safe location. It can be used to restore your account if you lose access, or to migrate to a new device."; "Restore your account by entering your seed below." = "Restore your account by entering your seed below."; "Copy" = "Copy"; @@ -2571,7 +2570,6 @@ "Start a Conversation" = "Start a Conversation"; "Invalid public key" = "Invalid public key"; "No search results" = "No search results"; -"Failed to connect to server" = "Failed to connect to server"; "Calculating proof of work" = "Calculating proof of work"; "Failed to calculate proof of work." = "Failed to calculate proof of work."; "Share Public Key" = "Share Public Key"; @@ -2648,5 +2646,4 @@ "Enter a URL" = "Enter a URL"; "Enter the URL of the public chat you'd like to join. The Loki Public Chat URL is https://chat.lokinet.org." = "Enter the URL of the public chat you'd like to join. The Loki Public Chat URL is https://chat.lokinet.org."; "Connecting..." = "Connecting..."; -"Add" = "Add"; "Couldn't Connect" = "Couldn't Connect"; diff --git a/SignalMessaging/Loki/DisplayNameUtilities.swift b/SignalMessaging/Loki/DisplayNameUtilities.swift new file mode 100644 index 00000000000..7e864c10507 --- /dev/null +++ b/SignalMessaging/Loki/DisplayNameUtilities.swift @@ -0,0 +1,39 @@ + +@objc(LKDisplayNameUtilities) +public final class DisplayNameUtilities : NSObject { + + override private init() { } + + private static var userHexEncodedPublicKey: String { + return OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey + } + + private static var userDisplayName: String? { + return OWSProfileManager.shared().localProfileName() + } + + @objc public static func getPrivateChatDisplayName(for hexEncodedPublicKey: String) -> String? { + if hexEncodedPublicKey == userHexEncodedPublicKey { + return userDisplayName + } else { + return OWSProfileManager.shared().profileName(forRecipientId: hexEncodedPublicKey) + } + } + + @objc public static func getGroupChatDisplayName(for hexEncodedPublicKey: String, in channel: UInt64, on server: String) -> String? { + var result: String? + OWSPrimaryStorage.shared().dbReadConnection.read { transaction in + result = getGroupChatDisplayName(for: hexEncodedPublicKey, in: channel, on: server, using: transaction) + } + return result + } + + @objc public static func getGroupChatDisplayName(for hexEncodedPublicKey: String, in channel: UInt64, on server: String, using transaction: YapDatabaseReadTransaction) -> String? { + if hexEncodedPublicKey == userHexEncodedPublicKey { + return userDisplayName + } else { + let collection = "\(server).\(channel)" + return transaction.object(forKey: hexEncodedPublicKey, inCollection: collection) as! String? + } + } +} diff --git a/SignalMessaging/ViewModels/OWSQuotedReplyModel.h b/SignalMessaging/ViewModels/OWSQuotedReplyModel.h index cb8b950b2a4..471613b47f1 100644 --- a/SignalMessaging/ViewModels/OWSQuotedReplyModel.h +++ b/SignalMessaging/ViewModels/OWSQuotedReplyModel.h @@ -51,10 +51,8 @@ NS_ASSUME_NONNULL_BEGIN - (TSQuotedMessage *)buildQuotedMessageForSending; // Loki - @property (nonatomic, readonly) NSString *threadId; - @end NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/environment/VersionMigrations.m b/SignalMessaging/environment/VersionMigrations.m index 57ccac8acae..dec2b762c2a 100644 --- a/SignalMessaging/environment/VersionMigrations.m +++ b/SignalMessaging/environment/VersionMigrations.m @@ -167,7 +167,7 @@ + (void)clearBloomFilterCache } } -# pragma mark Loki - Upgrading to public chat manager +# pragma mark Loki - Upgrading to Public Chat Manager // Versions less than or equal to 1.2.0 didn't store group chat mappings + (void)updatePublicChatMapping diff --git a/SignalMessaging/profiles/OWSProfileManager.m b/SignalMessaging/profiles/OWSProfileManager.m index cc0a5be77dc..c9edb900f99 100644 --- a/SignalMessaging/profiles/OWSProfileManager.m +++ b/SignalMessaging/profiles/OWSProfileManager.m @@ -535,13 +535,15 @@ - (void)updateServiceWithProfileName:(nullable NSString *)localProfileName OWSAssertDebug(successBlock); OWSAssertDebug(failureBlock); - __block NSDictionary *chats; - [SSKEnvironment.shared.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { - chats = [LKDatabaseUtilities getAllGroupChats:transaction]; + __block NSDictionary *groupChats; + [SSKEnvironment.shared.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + groupChats = [LKDatabaseUtilities getAllGroupChats:transaction]; }]; - for (LKGroupChat *chat in chats.allValues) { - [[LKGroupChatAPI setDisplayName:localProfileName on:chat.server] retainUntilComplete]; + NSSet *servers = [NSSet setWithArray:[groupChats.allValues map:^NSString *(LKGroupChat *groupChat) { return groupChat.server; }]]; + + for (NSString *server in servers) { + [[LKGroupChatAPI setDisplayName:localProfileName on:server] retainUntilComplete]; } successBlock(); diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatPoller.swift b/SignalServiceKit/src/Loki/API/Group Chat/GroupChatPoller.swift similarity index 97% rename from SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatPoller.swift rename to SignalServiceKit/src/Loki/API/Group Chat/GroupChatPoller.swift index 0196e110d5e..20435b8d48b 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatPoller.swift +++ b/SignalServiceKit/src/Loki/API/Group Chat/GroupChatPoller.swift @@ -1,6 +1,6 @@ @objc(LKGroupChatPoller) -public final class LokiGroupChatPoller : NSObject { +public final class GroupChatPoller : NSObject { private let group: LokiGroupChat private var pollForNewMessagesTimer: Timer? = nil private var pollForDeletedMessagesTimer: Timer? = nil @@ -50,7 +50,7 @@ public final class LokiGroupChatPoller : NSObject { let endIndex = senderHexEncodedPublicKey.endIndex let cutoffIndex = senderHexEncodedPublicKey.index(endIndex, offsetBy: -8) let senderDisplayName = "\(message.displayName) (...\(senderHexEncodedPublicKey[cutoffIndex.. Promise { + public static func getChannelInfo(_ channel: UInt64, on server: String) -> Promise { let url = URL(string: "\(server)/channels/\(channel)?include_annotations=1")! let request = TSRequest(url: url) return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in @@ -236,7 +236,7 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { print("[Loki] Couldn't parse info for group chat with ID: \(channel) on server: \(server) from: \(rawResponse).") throw Error.parsingFailed } - return LokiPublicChannel(name: name) + return name } } diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift b/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift index 18e3b27dcf6..cbc98435263 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift +++ b/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift @@ -11,7 +11,7 @@ public final class LokiPublicChatManager: NSObject { @objc public static let shared = LokiPublicChatManager() private var chats: [String: LokiGroupChat] = [:] - private var pollers: [String: LokiGroupChatPoller] = [:] + private var pollers: [String: GroupChatPoller] = [:] private var isPolling = false private let storage = OWSPrimaryStorage.shared() @@ -32,7 +32,7 @@ public final class LokiPublicChatManager: NSObject { if let poller = pollers[threadID] { poller.startIfNeeded() } else { - let poller = LokiGroupChatPoller(for: groupChat) + let poller = GroupChatPoller(for: groupChat) poller.startIfNeeded() pollers[threadID] = poller } @@ -53,7 +53,7 @@ public final class LokiPublicChatManager: NSObject { return LokiGroupChatAPI.getAuthToken(for: server).then { token in return LokiGroupChatAPI.getChannelInfo(channel, on: server) }.map { channelInfo -> LokiGroupChat in - return self.addChat(server: server, channel: channel, name: channelInfo.name) + return self.addChat(server: server, channel: channel, name: channelInfo) } } diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift index c19652ab1bb..d2b28897ec5 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift @@ -309,10 +309,19 @@ public final class LokiAPI : NSObject { guard let cache = userHexEncodedPublicKeyCache[threadID] else { return [] } var candidates: [Mention] = [] // Gather candidates + var groupChat: LokiGroupChat? + OWSPrimaryStorage.shared().dbReadConnection.read { transaction in + groupChat = LokiDatabaseUtilities.getGroupChat(for: threadID, in: transaction) + } storage.dbReadConnection.read { transaction in - let collection = "https://chat.lokinet.org.1" // FIXME: Mentions need to work for every kind of chat candidates = cache.flatMap { hexEncodedPublicKey in - guard let displayName = transaction.object(forKey: hexEncodedPublicKey, inCollection: collection) as! String? else { return nil } + let uncheckedDisplayName: String? + if let groupChat = groupChat { + uncheckedDisplayName = DisplayNameUtilities.getGroupChatDisplayName(for: hexEncodedPublicKey, in: groupChat.channel, on: groupChat.server) + } else { + uncheckedDisplayName = DisplayNameUtilities.getPrivateChatDisplayName(for: hexEncodedPublicKey) + } + guard let displayName = uncheckedDisplayName else { return nil } guard !displayName.hasPrefix("Anonymous") else { return nil } return Mention(hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName) } diff --git a/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift b/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift index 3a703960110..90e883e4d66 100644 --- a/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift +++ b/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift @@ -26,12 +26,12 @@ public final class LokiDatabaseUtilities : NSObject { } @objc(getAllGroupChats:) - public static func objc_getAllGroupChats(in transaction: YapDatabaseReadTransaction) -> [String: LokiGroupChat] { + public static func objc_getAllGroupChats(in transaction: YapDatabaseReadTransaction) -> [String:LokiGroupChat] { return OWSPrimaryStorage.shared().getAllGroupChats(with: transaction) } @objc(getGroupChatForThreadID:transaction:) - public static func objc_getGroupChat(for threadID: String, in transaction: YapDatabaseReadTransaction) -> LokiGroupChat? { + public static func getGroupChat(for threadID: String, in transaction: YapDatabaseReadTransaction) -> LokiGroupChat? { return OWSPrimaryStorage.shared().getGroupChat(for: threadID, in: transaction) } From 48883bf1f12f3340f8353f52be3843c2b0a256ef Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Tue, 15 Oct 2019 09:21:23 +1100 Subject: [PATCH 11/17] Fix build --- Signal.xcodeproj/project.pbxproj | 4 ---- .../Cells/OWSMessageBubbleView.m | 4 ++-- .../ViewControllers/HomeView/HomeViewCell.m | 2 +- .../Loki/API/Group Chat/LokiGroupChat.swift | 10 +++++++++ .../Group Chat/LokiPublicChatManager.swift | 18 ++++++++++----- .../Loki/Database/LokiDatabaseUtilities.swift | 8 +++---- .../Database/OWSPrimaryStorage+Loki.swift | 22 ++++++++++--------- .../Utilities}/DisplayNameUtilities.swift | 4 ++-- .../src/Messages/OWSMessageSender.m | 6 ++--- .../src/Protocols/ProfileManagerProtocol.h | 1 + 10 files changed, 46 insertions(+), 33 deletions(-) rename {SignalMessaging/Loki => SignalServiceKit/src/Loki/Utilities}/DisplayNameUtilities.swift (89%) diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 31219927def..2c08f62f4b7 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -568,7 +568,6 @@ B82584A02315024B001B41CB /* RSSFeedPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = B825849F2315024B001B41CB /* RSSFeedPoller.swift */; }; B846365B22B7418B00AF1514 /* Identicon+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */; }; B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84664F4235022F30083A1CD /* MentionUtilities.swift */; }; - B84664F923541F070083A1CD /* DisplayNameUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84664F823541F070083A1CD /* DisplayNameUtilities.swift */; }; B86BD08123399883000F5AE3 /* QRCodeModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08023399883000F5AE3 /* QRCodeModal.swift */; }; B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; }; B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; }; @@ -1381,7 +1380,6 @@ B825849F2315024B001B41CB /* RSSFeedPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSSFeedPoller.swift; sourceTree = ""; }; B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Identicon+ObjC.swift"; sourceTree = ""; }; B84664F4235022F30083A1CD /* MentionUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionUtilities.swift; sourceTree = ""; }; - B84664F823541F070083A1CD /* DisplayNameUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayNameUtilities.swift; sourceTree = ""; }; B86BD08023399883000F5AE3 /* QRCodeModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeModal.swift; sourceTree = ""; }; B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = ""; }; B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = ""; }; @@ -2673,7 +2671,6 @@ B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */, 241C6312231F5F1D00B4198E /* CGFloat+Rounding.swift */, 241C6310231F5C4400B4198E /* UIColor+Helper.swift */, - B84664F823541F070083A1CD /* DisplayNameUtilities.swift */, ); path = Loki; sourceTree = ""; @@ -3578,7 +3575,6 @@ 450998681FD8C0FF00D89EB3 /* AttachmentSharing.m in Sources */, 347850711FDAEB17007B8332 /* OWSUserProfile.m in Sources */, 346129F81FD5F31400532771 /* OWS100RemoveTSRecipientsMigration.m in Sources */, - B84664F923541F070083A1CD /* DisplayNameUtilities.swift in Sources */, 34AC09DF211B39B100997B47 /* OWSNavigationController.m in Sources */, 34074F61203D0CBE004596AE /* OWSSounds.m in Sources */, 34BEDB1721C80BCA007B0EAE /* OWSAnyTouchGestureRecognizer.m in Sources */, diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index 588fb06f8f9..c4f79d3435b 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -707,7 +707,7 @@ + (void)loadForTextDisplay:(OWSMessageTextView *)textView NSString *text = displayableText.displayText; - NSMutableAttributedString *attributedText = [LKMentionUtilities highlightMentionsIn:text isOutgoingMessage:isOutgoingMessage thread:thread attributes:@{ NSFontAttributeName : font, NSForegroundColorAttributeName : textColor }].mutableCopy; + NSMutableAttributedString *attributedText = [LKMentionUtilities highlightMentionsIn:text isOutgoingMessage:isOutgoingMessage threadID:thread.uniqueId attributes:@{ NSFontAttributeName : font, NSForegroundColorAttributeName : textColor }].mutableCopy; if (searchText.length >= ConversationSearchController.kMinimumSearchTextLength) { NSString *searchableText = [FullTextSearchFinder normalizeWithText:searchText]; @@ -1158,7 +1158,7 @@ - (DisplayableText *)getDisplayableQuotedText if (!self.viewItem.hasQuotedText) { return nil; } NSString *rawText = self.viewItem.displayableQuotedText.fullText; TSThread *thread = self.viewItem.interaction.thread; - NSString *text = [LKMentionUtilities highlightMentionsIn:rawText thread:thread]; + NSString *text = [LKMentionUtilities highlightMentionsIn:rawText threadID:thread.uniqueId]; return [DisplayableText displayableText:text]; } diff --git a/Signal/src/ViewControllers/HomeView/HomeViewCell.m b/Signal/src/ViewControllers/HomeView/HomeViewCell.m index ac1cf197667..7f82513b611 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewCell.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewCell.m @@ -396,7 +396,7 @@ - (NSAttributedString *)attributedSnippetForThread:(ThreadViewModel *)thread isB NSString *displayableText = thread.lastMessageText; if (displayableText) { [LKAPI populateUserHexEncodedPublicKeyCacheIfNeededFor:thread.threadRecord.uniqueId in:nil]; // TODO: Terrible place to do this, but okay for now - displayableText = [LKMentionUtilities highlightMentionsIn:displayableText thread:thread.threadRecord]; + displayableText = [LKMentionUtilities highlightMentionsIn:displayableText threadID:thread.threadRecord.uniqueId]; [snippetText appendAttributedString:[[NSAttributedString alloc] initWithString:displayableText attributes:@{ diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift index 8acda744599..4d565b80d95 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift +++ b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift @@ -8,6 +8,16 @@ public final class LokiGroupChat : NSObject, NSCoding { @objc public let displayName: String @objc public let isDeletable: Bool + @objc public static var defaultChats: [LokiGroupChat] { + var chats = [LokiGroupChat(channel: UInt64(1), server: "https://chat.lokinet.org", displayName: NSLocalizedString("Loki Public Chat", comment: ""), isDeletable: true)!] + + #if DEBUG + chats.append(LokiGroupChat(channel: UInt64(1), server: "https://chat-dev.lokinet.org", displayName: "Loki Dev Chat", isDeletable: true)!) + #endif + + return chats + } + @objc public init?(channel: UInt64, server: String, displayName: String, isDeletable: Bool) { let id = "\(server).\(channel)" self.id = id diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift b/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift index cbc98435263..5ec16ecdc5e 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift +++ b/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift @@ -5,6 +5,7 @@ public final class LokiPublicChatManager: NSObject { // MARK: Error public enum Error : Swift.Error { + case chatCreationFailed case userPublicKeyNotFound } @@ -47,21 +48,26 @@ public final class LokiPublicChatManager: NSObject { public func addChat(server: String, channel: UInt64) -> Promise { if let existingChat = getChat(server: server, channel: channel) { - return Promise.value(self.addChat(server: server, channel: channel, name: existingChat.displayName)) + if let chat = self.addChat(server: server, channel: channel, name: existingChat.displayName) { + return Promise.value(chat) + } else { + return Promise(error: Error.chatCreationFailed) + } } return LokiGroupChatAPI.getAuthToken(for: server).then { token in return LokiGroupChatAPI.getChannelInfo(channel, on: server) }.map { channelInfo -> LokiGroupChat in - return self.addChat(server: server, channel: channel, name: channelInfo) + guard let chat = self.addChat(server: server, channel: channel, name: channelInfo) else { throw Error.chatCreationFailed } + return chat } } @discardableResult @objc(addChatWithServer:channel:name:) - public func addChat(server: String, channel: UInt64, name: String) -> LokiGroupChat { - let chat = LokiGroupChat(channel: channel, server: server, displayName: name, isDeletable: true) - let model = TSGroupModel(title: chat.displayName, memberIds: [ourHexEncodedPublicKey!, chat.server], image: nil, groupId: chat.idAsData!) + public func addChat(server: String, channel: UInt64, name: String) -> LokiGroupChat? { + guard let chat = LokiGroupChat(channel: channel, server: server, displayName: name, isDeletable: true) else { return nil } + let model = TSGroupModel(title: chat.displayName, memberIds: [ourHexEncodedPublicKey!, chat.server], image: nil, groupId: chat.idAsData) // Store the group chat mapping self.storage.dbReadWriteConnection.readWrite { transaction in @@ -95,7 +101,7 @@ public final class LokiPublicChatManager: NSObject { private func refreshChatsAndPollers() { storage.dbReadConnection.read { transaction in - let newChats = self.storage.getAllGroupChats(with: transaction) + let newChats = self.storage.getAllGroupChats(in: transaction) // Remove any chats that don't exist in the database let removedChatThreadIds = self.chats.keys.filter { !newChats.keys.contains($0) } diff --git a/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift b/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift index 90e883e4d66..3f80b43929c 100644 --- a/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift +++ b/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift @@ -26,8 +26,8 @@ public final class LokiDatabaseUtilities : NSObject { } @objc(getAllGroupChats:) - public static func objc_getAllGroupChats(in transaction: YapDatabaseReadTransaction) -> [String:LokiGroupChat] { - return OWSPrimaryStorage.shared().getAllGroupChats(with: transaction) + public static func getAllGroupChats(in transaction: YapDatabaseReadTransaction) -> [String:LokiGroupChat] { + return OWSPrimaryStorage.shared().getAllGroupChats(in: transaction) } @objc(getGroupChatForThreadID:transaction:) @@ -36,12 +36,12 @@ public final class LokiDatabaseUtilities : NSObject { } @objc(setGroupChat:threadID:transaction:) - public static func objc_setGroupChat(_ groupChat: LokiGroupChat, for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { + public static func setGroupChat(_ groupChat: LokiGroupChat, for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { return OWSPrimaryStorage.shared().setGroupChat(groupChat, for: threadID, in: transaction) } @objc(removeGroupChatForThreadID:transaction:) - public static func objc_removeGroupChat(for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { + public static func removeGroupChat(for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { return OWSPrimaryStorage.shared().removeGroupChat(for: threadID, in: transaction) } } diff --git a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift index 9423e42ebd6..478720c85b3 100644 --- a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift +++ b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift @@ -1,9 +1,7 @@ public extension OWSPrimaryStorage { - private var groupChatCollection: String { - return "LokiGroupChatCollection" - } + // MARK: Device Links private func getDeviceLinkCollection(for primaryDevice: String) -> String { return "LokiDeviceLinkCollection-\(primaryDevice)" } @@ -55,14 +53,18 @@ public extension OWSPrimaryStorage { return getDeviceLink(for: slaveHexEncodedPublicKey, in: transaction)?.master.hexEncodedPublicKey } - public func getAllGroupChats(with transaction: YapDatabaseReadTransaction) -> [String: LokiGroupChat] { - var dict = [String: LokiGroupChat]() - transaction.enumerateKeysAndObjects(inCollection: groupChatCollection) { (threadID, object, _) in - if let groupChat = object as? LokiGroupChat { - dict[threadID] = groupChat - } + // MARK: Group Chats + private var groupChatCollection: String { + return "LokiGroupChatCollection" + } + + public func getAllGroupChats(in transaction: YapDatabaseReadTransaction) -> [String:LokiGroupChat] { + var result = [String:LokiGroupChat]() + transaction.enumerateKeysAndObjects(inCollection: groupChatCollection) { threadID, object, _ in + guard let groupChat = object as? LokiGroupChat else { return } + result[threadID] = groupChat } - return dict + return result } public func getGroupChat(for threadID: String, in transaction: YapDatabaseReadTransaction) -> LokiGroupChat? { diff --git a/SignalMessaging/Loki/DisplayNameUtilities.swift b/SignalServiceKit/src/Loki/Utilities/DisplayNameUtilities.swift similarity index 89% rename from SignalMessaging/Loki/DisplayNameUtilities.swift rename to SignalServiceKit/src/Loki/Utilities/DisplayNameUtilities.swift index 7e864c10507..e02cf92c238 100644 --- a/SignalMessaging/Loki/DisplayNameUtilities.swift +++ b/SignalServiceKit/src/Loki/Utilities/DisplayNameUtilities.swift @@ -9,14 +9,14 @@ public final class DisplayNameUtilities : NSObject { } private static var userDisplayName: String? { - return OWSProfileManager.shared().localProfileName() + return SSKEnvironment.shared.profileManager.localProfileName()! } @objc public static func getPrivateChatDisplayName(for hexEncodedPublicKey: String) -> String? { if hexEncodedPublicKey == userHexEncodedPublicKey { return userDisplayName } else { - return OWSProfileManager.shared().profileName(forRecipientId: hexEncodedPublicKey) + return SSKEnvironment.shared.profileManager.profileName(forRecipientId: hexEncodedPublicKey) } } diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index ad76d01fbb3..d3f07b217fb 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -503,9 +503,9 @@ - (void)sendMessageToService:(TSOutgoingMessage *)message if ([message isKindOfClass:[OWSOutgoingSyncMessage class]]) { [recipientIds addObject:self.tsAccountManager.localNumber]; } else if (thread.isGroupThread) { - [_primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { + [self.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { LKGroupChat *groupChat = [LKDatabaseUtilities getGroupChatForThreadID:thread.uniqueId transaction:transaction]; - if (groupChat != nil) { [recipientIds addObject:groupChat.server]; } + [recipientIds addObject:groupChat.server]; }]; } else if ([thread isKindOfClass:[TSContactThread class]]) { NSString *recipientContactId = ((TSContactThread *)thread).contactIdentifier; @@ -1188,12 +1188,10 @@ - (void)sendMessage:(OWSMessageSend *)messageSend [self messageSendDidFail:messageSend deviceMessages:deviceMessages statusCode:statusCode error:error responseData:responseData]; }; - // Get the group chat info if we have it __block LKGroupChat *groupChat; [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { groupChat = [LKDatabaseUtilities getGroupChatForThreadID:message.uniqueThreadId transaction: transaction]; }]; - if (groupChat != nil) { NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey; NSString *displayName = SSKEnvironment.shared.profileManager.localProfileName; diff --git a/SignalServiceKit/src/Protocols/ProfileManagerProtocol.h b/SignalServiceKit/src/Protocols/ProfileManagerProtocol.h index 18946ce22ff..ce187165ab6 100644 --- a/SignalServiceKit/src/Protocols/ProfileManagerProtocol.h +++ b/SignalServiceKit/src/Protocols/ProfileManagerProtocol.h @@ -13,6 +13,7 @@ NS_ASSUME_NONNULL_BEGIN - (OWSAES256Key *)localProfileKey; - (nullable NSString *)localProfileName; +- (nullable NSString *)profileNameForRecipientId:(NSString *)recipientId; - (nullable NSData *)profileKeyDataForRecipientId:(NSString *)recipientId; - (void)setProfileKeyData:(NSData *)profileKeyData forRecipientId:(NSString *)recipientId; From 131c27d089fbcf6e7972012651ae38b08532780d Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Tue, 15 Oct 2019 09:43:58 +1100 Subject: [PATCH 12/17] Fix duplication --- .../src/Loki/Onboarding/DisplayNameVC.swift | 4 +-- .../Group Chat/LokiPublicChatManager.swift | 6 ++--- SignalServiceKit/src/Loki/API/LokiAPI.swift | 2 +- .../Loki/Database/LokiDatabaseUtilities.swift | 18 ++++++++++--- .../Database/OWSPrimaryStorage+Loki.swift | 27 ------------------- .../src/Messages/OWSMessageSender.m | 2 +- 6 files changed, 21 insertions(+), 38 deletions(-) diff --git a/Signal/src/Loki/Onboarding/DisplayNameVC.swift b/Signal/src/Loki/Onboarding/DisplayNameVC.swift index 65eaed8441f..58c5bf7e33e 100644 --- a/Signal/src/Loki/Onboarding/DisplayNameVC.swift +++ b/Signal/src/Loki/Onboarding/DisplayNameVC.swift @@ -4,12 +4,12 @@ final class DisplayNameVC : OnboardingBaseViewController { private lazy var userNameTextField: UITextField = { let result = UITextField() result.textColor = Theme.primaryColor - result.font = UIFont.ows_dynamicTypeBodyClamped + result.font = .ows_dynamicTypeBodyClamped result.textAlignment = .center let placeholder = NSMutableAttributedString(string: NSLocalizedString("Display Name", comment: "")) placeholder.addAttribute(.foregroundColor, value: Theme.placeholderColor, range: NSRange(location: 0, length: placeholder.length)) result.attributedPlaceholder = placeholder - result.tintColor = UIColor.lokiGreen() + result.tintColor = .lokiGreen() result.accessibilityIdentifier = "onboarding.accountDetailsStep.userNameTextField" result.keyboardAppearance = .dark return result diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift b/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift index 5ec16ecdc5e..772b0ad61dc 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift +++ b/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift @@ -85,7 +85,7 @@ public final class LokiPublicChatManager: NSObject { } // Save the group chat - self.storage.setGroupChat(chat, for: thread.uniqueId!, in: transaction) + LokiDatabaseUtilities.setGroupChat(chat, for: thread.uniqueId!, in: transaction) } // Update chats and pollers @@ -101,7 +101,7 @@ public final class LokiPublicChatManager: NSObject { private func refreshChatsAndPollers() { storage.dbReadConnection.read { transaction in - let newChats = self.storage.getAllGroupChats(in: transaction) + let newChats = LokiDatabaseUtilities.getAllGroupChats(in: transaction) // Remove any chats that don't exist in the database let removedChatThreadIds = self.chats.keys.filter { !newChats.keys.contains($0) } @@ -129,7 +129,7 @@ public final class LokiPublicChatManager: NSObject { // Remove the chat from the db storage.dbReadWriteConnection.readWrite { transaction in - self.storage.removeGroupChat(for: threadId, in: transaction) + LokiDatabaseUtilities.removeGroupChat(for: threadId, in: transaction) } refreshChatsAndPollers() diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift index d2b28897ec5..cb654cfd888 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift @@ -310,7 +310,7 @@ public final class LokiAPI : NSObject { var candidates: [Mention] = [] // Gather candidates var groupChat: LokiGroupChat? - OWSPrimaryStorage.shared().dbReadConnection.read { transaction in + storage.dbReadConnection.read { transaction in groupChat = LokiDatabaseUtilities.getGroupChat(for: threadID, in: transaction) } storage.dbReadConnection.read { transaction in diff --git a/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift b/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift index 3f80b43929c..3e87962894f 100644 --- a/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift +++ b/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift @@ -4,6 +4,7 @@ public final class LokiDatabaseUtilities : NSObject { private override init() { } + // MARK: Quotes @objc(getServerIDForQuoteWithID:quoteeHexEncodedPublicKey:threadID:transaction:) public static func getServerID(quoteID: UInt64, quoteeHexEncodedPublicKey: String, threadID: String, transaction: YapDatabaseReadTransaction) -> UInt64 { guard let message = TSInteraction.interactions(withTimestamp: quoteID, filter: { interaction in @@ -20,28 +21,37 @@ public final class LokiDatabaseUtilities : NSObject { return message.groupChatServerID } + // MARK: Device Links @objc(getMasterHexEncodedPublicKeyFor:in:) public static func objc_getMasterHexEncodedPublicKey(for slaveHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> String? { return OWSPrimaryStorage.shared().getMasterHexEncodedPublicKey(for: slaveHexEncodedPublicKey, in: transaction) } + // MARK: Group Chats + private static let groupChatCollection = "LokiGroupChatCollection" + @objc(getAllGroupChats:) public static func getAllGroupChats(in transaction: YapDatabaseReadTransaction) -> [String:LokiGroupChat] { - return OWSPrimaryStorage.shared().getAllGroupChats(in: transaction) + var result = [String:LokiGroupChat]() + transaction.enumerateKeysAndObjects(inCollection: groupChatCollection) { threadID, object, _ in + guard let groupChat = object as? LokiGroupChat else { return } + result[threadID] = groupChat + } + return result } @objc(getGroupChatForThreadID:transaction:) public static func getGroupChat(for threadID: String, in transaction: YapDatabaseReadTransaction) -> LokiGroupChat? { - return OWSPrimaryStorage.shared().getGroupChat(for: threadID, in: transaction) + return transaction.object(forKey: threadID, inCollection: groupChatCollection) as? LokiGroupChat } @objc(setGroupChat:threadID:transaction:) public static func setGroupChat(_ groupChat: LokiGroupChat, for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { - return OWSPrimaryStorage.shared().setGroupChat(groupChat, for: threadID, in: transaction) + transaction.setObject(groupChat, forKey: threadID, inCollection: groupChatCollection) } @objc(removeGroupChatForThreadID:transaction:) public static func removeGroupChat(for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { - return OWSPrimaryStorage.shared().removeGroupChat(for: threadID, in: transaction) + transaction.removeObject(forKey: threadID, inCollection: groupChatCollection) } } diff --git a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift index 478720c85b3..71ba0eedc9b 100644 --- a/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift +++ b/SignalServiceKit/src/Loki/Database/OWSPrimaryStorage+Loki.swift @@ -1,7 +1,6 @@ public extension OWSPrimaryStorage { - // MARK: Device Links private func getDeviceLinkCollection(for primaryDevice: String) -> String { return "LokiDeviceLinkCollection-\(primaryDevice)" } @@ -52,30 +51,4 @@ public extension OWSPrimaryStorage { public func getMasterHexEncodedPublicKey(for slaveHexEncodedPublicKey: String, in transaction: YapDatabaseReadTransaction) -> String? { return getDeviceLink(for: slaveHexEncodedPublicKey, in: transaction)?.master.hexEncodedPublicKey } - - // MARK: Group Chats - private var groupChatCollection: String { - return "LokiGroupChatCollection" - } - - public func getAllGroupChats(in transaction: YapDatabaseReadTransaction) -> [String:LokiGroupChat] { - var result = [String:LokiGroupChat]() - transaction.enumerateKeysAndObjects(inCollection: groupChatCollection) { threadID, object, _ in - guard let groupChat = object as? LokiGroupChat else { return } - result[threadID] = groupChat - } - return result - } - - public func getGroupChat(for threadID: String, in transaction: YapDatabaseReadTransaction) -> LokiGroupChat? { - return transaction.object(forKey: threadID, inCollection: groupChatCollection) as? LokiGroupChat - } - - public func setGroupChat(_ groupChat: LokiGroupChat, for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { - transaction.setObject(groupChat, forKey: threadID, inCollection: groupChatCollection) - } - - public func removeGroupChat(for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { - transaction.removeObject(forKey: threadID, inCollection: groupChatCollection) - } } diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index d3f07b217fb..4b796818f38 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -503,7 +503,7 @@ - (void)sendMessageToService:(TSOutgoingMessage *)message if ([message isKindOfClass:[OWSOutgoingSyncMessage class]]) { [recipientIds addObject:self.tsAccountManager.localNumber]; } else if (thread.isGroupThread) { - [self.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { + [self.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { LKGroupChat *groupChat = [LKDatabaseUtilities getGroupChatForThreadID:thread.uniqueId transaction:transaction]; [recipientIds addObject:groupChat.server]; }]; From b4af9d16d599e4d1c1539c53cab3cf26bc07087c Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Tue, 15 Oct 2019 10:29:41 +1100 Subject: [PATCH 13/17] Refactor --- Signal/src/AppDelegate.m | 2 +- .../Loki/MentionCandidateSelectionView.swift | 2 +- Signal/src/Loki/NewPublicChatVC.swift | 4 +- .../src/Loki/Utilities/MentionUtilities.swift | 8 +- .../ConversationView/Cells/OWSMessageCell.m | 14 ++- .../Cells/OWSQuotedMessageView.m | 6 +- .../ConversationInputToolbar.m | 10 +- .../ConversationView/ConversationViewItem.m | 16 +-- .../environment/VersionMigrations.m | 4 +- SignalMessaging/profiles/OWSProfileManager.m | 8 +- SignalServiceKit/src/Loki/API/LokiAPI.swift | 8 +- .../LokiChannelInfo.swift | 0 .../LokiPublicChat.swift} | 10 +- .../LokiPublicChatAPI.swift} | 108 +++++++++--------- .../API/Public Chat/LokiPublicChatInfo.swift | 4 + .../LokiPublicChatManager.swift | 30 ++--- .../LokiPublicChatMessage.swift} | 10 +- .../LokiRSSFeed.swift | 0 .../PublicChatPoller.swift} | 18 +-- .../Loki/Database/LokiDatabaseUtilities.swift | 34 +++--- .../Loki/Utilities/DisplayNameUtilities.swift | 6 +- .../src/Messages/Interactions/TSMessage.h | 4 +- .../src/Messages/OWSMessageSender.m | 18 +-- 23 files changed, 168 insertions(+), 156 deletions(-) rename SignalServiceKit/src/Loki/API/{Group Chat => Public Chat}/LokiChannelInfo.swift (100%) rename SignalServiceKit/src/Loki/API/{Group Chat/LokiGroupChat.swift => Public Chat/LokiPublicChat.swift} (77%) rename SignalServiceKit/src/Loki/API/{Group Chat/LokiGroupChatAPI.swift => Public Chat/LokiPublicChatAPI.swift} (70%) create mode 100644 SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatInfo.swift rename SignalServiceKit/src/Loki/API/{Group Chat => Public Chat}/LokiPublicChatManager.swift (82%) rename SignalServiceKit/src/Loki/API/{Group Chat/LokiGroupMessage.swift => Public Chat/LokiPublicChatMessage.swift} (91%) rename SignalServiceKit/src/Loki/API/{Group Chat => Public Chat}/LokiRSSFeed.swift (100%) rename SignalServiceKit/src/Loki/API/{Group Chat/GroupChatPoller.swift => Public Chat/PublicChatPoller.swift} (91%) diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index ba5626c2d63..3afa2da8607 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -1537,7 +1537,7 @@ - (LKRSSFeed *)lokiMessengerUpdatesFeed - (void)createGroupChatsIfNeeded { // Setup our default public chats - for (LKGroupChat *chat in LKGroupChat.defaultChats) { + for (LKPublicChat *chat in LKPublicChat.defaultChats) { NSString *userDefaultsKey = [@"isGroupChatSetUp." stringByAppendingString:chat.id]; BOOL isChatSetUp = [NSUserDefaults.standardUserDefaults boolForKey:userDefaultsKey]; if (!isChatSetUp || !chat.isDeletable) { diff --git a/Signal/src/Loki/MentionCandidateSelectionView.swift b/Signal/src/Loki/MentionCandidateSelectionView.swift index b6c1798eddb..1f2f16c4d66 100644 --- a/Signal/src/Loki/MentionCandidateSelectionView.swift +++ b/Signal/src/Loki/MentionCandidateSelectionView.swift @@ -131,7 +131,7 @@ private extension MentionCandidateSelectionView { let profilePicture = OWSContactAvatarBuilder(signalId: mentionCandidate.hexEncodedPublicKey, colorName: .blue, diameter: 36).build() profilePictureImageView.image = profilePicture if let publicChatServer = publicChatServer, let publicChatServerID = publicChatServerID { - let isUserModerator = LokiGroupChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, for: publicChatServerID, on: publicChatServer) + let isUserModerator = LokiPublicChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, for: publicChatServerID, on: publicChatServer) moderatorIconImageView.isHidden = !isUserModerator } else { moderatorIconImageView.isHidden = true diff --git a/Signal/src/Loki/NewPublicChatVC.swift b/Signal/src/Loki/NewPublicChatVC.swift index 2d0e952606f..205c18a6539 100644 --- a/Signal/src/Loki/NewPublicChatVC.swift +++ b/Signal/src/Loki/NewPublicChatVC.swift @@ -88,8 +88,8 @@ final class NewPublicChatVC : OWSViewController { let displayName = OWSProfileManager.shared().localProfileName() LokiPublicChatManager.shared.addChat(server: urlAsString, channel: channelID) .done(on: .main) { [weak self] _ in - let _ = LokiGroupChatAPI.getMessages(for: channelID, on: urlAsString) - let _ = LokiGroupChatAPI.setDisplayName(to: displayName, on: urlAsString) + let _ = LokiPublicChatAPI.getMessages(for: channelID, on: urlAsString) + let _ = LokiPublicChatAPI.setDisplayName(to: displayName, on: urlAsString) self?.presentingViewController!.dismiss(animated: true, completion: nil) } .catch(on: .main) { [weak self] _ in diff --git a/Signal/src/Loki/Utilities/MentionUtilities.swift b/Signal/src/Loki/Utilities/MentionUtilities.swift index 3268a045d2c..a2e72f31f28 100644 --- a/Signal/src/Loki/Utilities/MentionUtilities.swift +++ b/Signal/src/Loki/Utilities/MentionUtilities.swift @@ -9,9 +9,9 @@ public final class MentionUtilities : NSObject { } @objc public static func highlightMentions(in string: String, isOutgoingMessage: Bool, threadID: String, attributes: [NSAttributedString.Key:Any]) -> NSAttributedString { - var groupChat: LokiGroupChat? + var publicChat: LokiPublicChat? OWSPrimaryStorage.shared().dbReadConnection.read { transaction in - groupChat = LokiDatabaseUtilities.getGroupChat(for: threadID, in: transaction) + publicChat = LokiDatabaseUtilities.getPublicChat(for: threadID, in: transaction) } var string = string let regex = try! NSRegularExpression(pattern: "@[0-9a-fA-F]*", options: []) @@ -26,8 +26,8 @@ public final class MentionUtilities : NSObject { if hexEncodedPublicKey == OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey { userDisplayName = OWSProfileManager.shared().localProfileName() } else { - if let groupChat = groupChat { - userDisplayName = DisplayNameUtilities.getGroupChatDisplayName(for: hexEncodedPublicKey, in: groupChat.channel, on: groupChat.server) + if let publicChat = publicChat { + userDisplayName = DisplayNameUtilities.getPublicChatDisplayName(for: hexEncodedPublicKey, in: publicChat.channel, on: publicChat.server) } else { userDisplayName = DisplayNameUtilities.getPrivateChatDisplayName(for: hexEncodedPublicKey) } diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index ecde97570b3..980db554460 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -299,14 +299,16 @@ - (BOOL)updateAvatarView [self.contentView addSubview:self.avatarView]; if (self.viewItem.isGroupThread && !self.viewItem.isRSSFeed) { - __block LKGroupChat *groupChat; + __block LKPublicChat *publicChat; [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - groupChat = [LKDatabaseUtilities getGroupChatForThreadID:self.viewItem.interaction.uniqueThreadId transaction: transaction]; + publicChat = [LKDatabaseUtilities getPublicChatForThreadID:self.viewItem.interaction.uniqueThreadId transaction: transaction]; }]; - BOOL isModerator = [LKGroupChatAPI isUserModerator:incomingMessage.authorId forGroup:groupChat.channel onServer:groupChat.server]; - UIImage *moderatorIcon = [UIImage imageNamed:@"Crown"]; - self.moderatorIconImageView.image = moderatorIcon; - self.moderatorIconImageView.hidden = !isModerator; + if (publicChat != nil) { + BOOL isModerator = [LKPublicChatAPI isUserModerator:incomingMessage.authorId forGroup:publicChat.channel onServer:publicChat.server]; + UIImage *moderatorIcon = [UIImage imageNamed:@"Crown"]; + self.moderatorIconImageView.image = moderatorIcon; + self.moderatorIconImageView.hidden = !isModerator; + } } [self.contentView addSubview:self.moderatorIconImageView]; diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index 680a1df7dc2..386fc4d0bea 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -554,9 +554,9 @@ - (UILabel *)configureQuotedAuthorLabel if (quotedAuthor == self.quotedMessage.authorId) { [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - LKGroupChat *groupChat = [LKDatabaseUtilities getGroupChatForThreadID:self.quotedMessage.threadId transaction:transaction]; - if (groupChat != nil) { - quotedAuthor = [LKDisplayNameUtilities getGroupChatDisplayNameFor:self.quotedMessage.authorId in:groupChat.channel on:groupChat.server using:transaction];; + LKPublicChat *publicChat = [LKDatabaseUtilities getPublicChatForThreadID:self.quotedMessage.threadId transaction:transaction]; + if (publicChat != nil) { + quotedAuthor = [LKDisplayNameUtilities getPublicChatDisplayNameFor:self.quotedMessage.authorId in:publicChat.channel on:publicChat.server using:transaction]; } else { quotedAuthor = [LKDisplayNameUtilities getPrivateChatDisplayNameFor:self.quotedMessage.authorId]; } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m index 141f448df7d..c31d0791572 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m @@ -1093,12 +1093,14 @@ - (void)hideInputMethod - (void)showMentionCandidateSelectionViewFor:(NSArray *)mentionCandidates in:(TSThread *)thread { - __block LKGroupChat *groupChat; + __block LKPublicChat *publicChat; [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - groupChat = [LKDatabaseUtilities getGroupChatForThreadID:thread.uniqueId transaction:transaction]; + publicChat = [LKDatabaseUtilities getPublicChatForThreadID:thread.uniqueId transaction:transaction]; }]; - self.mentionCandidateSelectionView.publicChatServer = groupChat.server; - [self.mentionCandidateSelectionView setPublicChatServerID:groupChat.channel]; + if (publicChat != nil) { + self.mentionCandidateSelectionView.publicChatServer = publicChat.server; + [self.mentionCandidateSelectionView setPublicChatServerID:publicChat.channel]; + } self.mentionCandidateSelectionView.mentionCandidates = mentionCandidates; self.mentionCandidateSelectionViewSizeConstraint.constant = 6 + MIN(mentionCandidates.count, 4) * 52; [self setNeedsLayout]; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index cdc1ee68a93..71ced588edd 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -1187,15 +1187,15 @@ - (void)deleteAction TSMessage *message = (TSMessage *)self.interaction; if (!message.isGroupChatMessage) return; - __block LKGroupChat *groupChat; + __block LKPublicChat *publicChat; [self.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - groupChat = [LKDatabaseUtilities getGroupChatForThreadID:groupThread.uniqueId transaction: transaction]; + publicChat = [LKDatabaseUtilities getPublicChatForThreadID:groupThread.uniqueId transaction: transaction]; }]; - if (groupChat == nil) return; + if (publicChat == nil) return; // Delete the message BOOL isSentByUser = (interationType == OWSInteractionType_OutgoingMessage); - [[LKGroupChatAPI deleteMessageWithID:message.groupChatServerID forGroup:groupChat.channel onServer:groupChat.server isSentByUser:isSentByUser].catch(^(NSError *error) { + [[LKPublicChatAPI deleteMessageWithID:message.groupChatServerID forGroup:publicChat.channel onServer:publicChat.server isSentByUser:isSentByUser].catch(^(NSError *error) { // Roll back [self.interaction save]; }) retainUntilComplete]; @@ -1263,15 +1263,15 @@ - (BOOL)userCanDeleteGroupMessage if (!message.isGroupChatMessage) return false; // Ensure we have the details needed to contact the server - __block LKGroupChat *groupChat; + __block LKPublicChat *publicChat; [self.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - groupChat = [LKDatabaseUtilities getGroupChatForThreadID:groupThread.uniqueId transaction: transaction]; + publicChat = [LKDatabaseUtilities getPublicChatForThreadID:groupThread.uniqueId transaction: transaction]; }]; - if (groupChat == nil) return false; + if (publicChat == nil) return false; // Only allow deletion on incoming messages if the user has moderation permission if (interationType == OWSInteractionType_IncomingMessage) { - BOOL isModerator = [LKGroupChatAPI isUserModerator:self.userHexEncodedPublicKey forGroup:groupChat.channel onServer:groupChat.server]; + BOOL isModerator = [LKPublicChatAPI isUserModerator:self.userHexEncodedPublicKey forGroup:publicChat.channel onServer:publicChat.server]; if (!isModerator) return false; } diff --git a/SignalMessaging/environment/VersionMigrations.m b/SignalMessaging/environment/VersionMigrations.m index dec2b762c2a..a195c144461 100644 --- a/SignalMessaging/environment/VersionMigrations.m +++ b/SignalMessaging/environment/VersionMigrations.m @@ -173,10 +173,10 @@ + (void)clearBloomFilterCache + (void)updatePublicChatMapping { [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) { - for (LKGroupChat *chat in LKGroupChat.defaultChats) { + for (LKPublicChat *chat in LKPublicChat.defaultChats) { TSGroupThread *thread = [TSGroupThread threadWithGroupId:chat.idAsData transaction:transaction]; if (thread != nil) { - [LKDatabaseUtilities setGroupChat:chat threadID:thread.uniqueId transaction:transaction]; + [LKDatabaseUtilities setPublicChat:chat threadID:thread.uniqueId transaction:transaction]; } } }]; diff --git a/SignalMessaging/profiles/OWSProfileManager.m b/SignalMessaging/profiles/OWSProfileManager.m index c9edb900f99..9b26be0b2ad 100644 --- a/SignalMessaging/profiles/OWSProfileManager.m +++ b/SignalMessaging/profiles/OWSProfileManager.m @@ -535,15 +535,15 @@ - (void)updateServiceWithProfileName:(nullable NSString *)localProfileName OWSAssertDebug(successBlock); OWSAssertDebug(failureBlock); - __block NSDictionary *groupChats; + __block NSDictionary *publicChats; [SSKEnvironment.shared.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - groupChats = [LKDatabaseUtilities getAllGroupChats:transaction]; + publicChats = [LKDatabaseUtilities getAllPublicChats:transaction]; }]; - NSSet *servers = [NSSet setWithArray:[groupChats.allValues map:^NSString *(LKGroupChat *groupChat) { return groupChat.server; }]]; + NSSet *servers = [NSSet setWithArray:[publicChats.allValues map:^NSString *(LKPublicChat *publicChat) { return publicChat.server; }]]; for (NSString *server in servers) { - [[LKGroupChatAPI setDisplayName:localProfileName on:server] retainUntilComplete]; + [[LKPublicChatAPI setDisplayName:localProfileName on:server] retainUntilComplete]; } successBlock(); diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift index cb654cfd888..f022d475fe7 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift @@ -309,15 +309,15 @@ public final class LokiAPI : NSObject { guard let cache = userHexEncodedPublicKeyCache[threadID] else { return [] } var candidates: [Mention] = [] // Gather candidates - var groupChat: LokiGroupChat? + var publicChat: LokiPublicChat? storage.dbReadConnection.read { transaction in - groupChat = LokiDatabaseUtilities.getGroupChat(for: threadID, in: transaction) + publicChat = LokiDatabaseUtilities.getPublicChat(for: threadID, in: transaction) } storage.dbReadConnection.read { transaction in candidates = cache.flatMap { hexEncodedPublicKey in let uncheckedDisplayName: String? - if let groupChat = groupChat { - uncheckedDisplayName = DisplayNameUtilities.getGroupChatDisplayName(for: hexEncodedPublicKey, in: groupChat.channel, on: groupChat.server) + if let publicChat = publicChat { + uncheckedDisplayName = DisplayNameUtilities.getPublicChatDisplayName(for: hexEncodedPublicKey, in: publicChat.channel, on: publicChat.server) } else { uncheckedDisplayName = DisplayNameUtilities.getPrivateChatDisplayName(for: hexEncodedPublicKey) } diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiChannelInfo.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiChannelInfo.swift similarity index 100% rename from SignalServiceKit/src/Loki/API/Group Chat/LokiChannelInfo.swift rename to SignalServiceKit/src/Loki/API/Public Chat/LokiChannelInfo.swift diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChat.swift similarity index 77% rename from SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift rename to SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChat.swift index 4d565b80d95..7720dae795c 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift +++ b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChat.swift @@ -1,6 +1,6 @@ -@objc(LKGroupChat) -public final class LokiGroupChat : NSObject, NSCoding { +@objc(LKPublicChat) +public final class LokiPublicChat : NSObject, NSCoding { @objc public let id: String @objc public let idAsData: Data @objc public let channel: UInt64 @@ -8,11 +8,11 @@ public final class LokiGroupChat : NSObject, NSCoding { @objc public let displayName: String @objc public let isDeletable: Bool - @objc public static var defaultChats: [LokiGroupChat] { - var chats = [LokiGroupChat(channel: UInt64(1), server: "https://chat.lokinet.org", displayName: NSLocalizedString("Loki Public Chat", comment: ""), isDeletable: true)!] + @objc public static var defaultChats: [LokiPublicChat] { + var chats = [LokiPublicChat(channel: UInt64(1), server: "https://chat.lokinet.org", displayName: NSLocalizedString("Loki Public Chat", comment: ""), isDeletable: true)!] #if DEBUG - chats.append(LokiGroupChat(channel: UInt64(1), server: "https://chat-dev.lokinet.org", displayName: "Loki Dev Chat", isDeletable: true)!) + chats.append(LokiPublicChat(channel: UInt64(1), server: "https://chat-dev.lokinet.org", displayName: "Loki Dev Chat", isDeletable: true)!) #endif return chats diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatAPI.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatAPI.swift similarity index 70% rename from SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatAPI.swift rename to SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatAPI.swift index 6c1b77ae60e..f258cd8f7c7 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatAPI.swift +++ b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatAPI.swift @@ -1,7 +1,7 @@ import PromiseKit -@objc(LKGroupChatAPI) -public final class LokiGroupChatAPI : LokiDotNetAPI { +@objc(LKPublicChatAPI) +public final class LokiPublicChatAPI : LokiDotNetAPI { private static var moderators: [String:[UInt64:Set]] = [:] // Server URL to (channel ID to set of moderator IDs) // MARK: Settings @@ -9,8 +9,8 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { private static let maxRetryCount: UInt = 8 // MARK: Public Chat - @objc public static let publicChatMessageType = "network.loki.messenger.publicChat" @objc private static let channelInfoType = "net.patter-app.settings" + @objc public static let publicChatMessageType = "network.loki.messenger.publicChat" // MARK: Convenience private static var userDisplayName: String { @@ -18,9 +18,9 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { } // MARK: Database - override internal class var authTokenCollection: String { "LokiGroupChatAuthTokenCollection" } - private static let lastMessageServerIDCollection = "LokiGroupChatLastMessageServerIDCollection" - private static let lastDeletionServerIDCollection = "LokiGroupChatLastDeletionServerIDCollection" + override internal class var authTokenCollection: String { "LokiGroupChatAuthTokenCollection" } // Should ideally be LokiPublicChatAuthTokenCollection + private static let lastMessageServerIDCollection = "LokiGroupChatLastMessageServerIDCollection" // Should ideally be LokiPublicChatLastMessageServerIDCollection + private static let lastDeletionServerIDCollection = "LokiGroupChatLastDeletionServerIDCollection" // Should ideally be LokiPublicChatLastDeletionServerIDCollection private static func getLastMessageServerID(for group: UInt64, on server: String) -> UInt? { var result: UInt? = nil @@ -63,19 +63,19 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { } // MARK: Public API - public static func getMessages(for group: UInt64, on server: String) -> Promise<[LokiGroupMessage]> { - print("[Loki] Getting messages for group chat with ID: \(group) on server: \(server).") + public static func getMessages(for channel: UInt64, on server: String) -> Promise<[LokiPublicChatMessage]> { + print("[Loki] Getting messages for public chat channel with ID: \(channel) on server: \(server).") var queryParameters = "include_annotations=1" - if let lastMessageServerID = getLastMessageServerID(for: group, on: server) { + if let lastMessageServerID = getLastMessageServerID(for: channel, on: server) { queryParameters += "&since_id=\(lastMessageServerID)" } else { queryParameters += "&count=-\(fallbackBatchCount)" } - let url = URL(string: "\(server)/channels/\(group)/messages?\(queryParameters)")! + let url = URL(string: "\(server)/channels/\(channel)/messages?\(queryParameters)")! let request = TSRequest(url: url) return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in guard let json = rawResponse as? JSON, let rawMessages = json["data"] as? [JSON] else { - print("[Loki] Couldn't parse messages for group chat with ID: \(group) on server: \(server) from: \(rawResponse).") + print("[Loki] Couldn't parse messages for public chat channel with ID: \(channel) on server: \(server) from: \(rawResponse).") throw Error.parsingFailed } return rawMessages.flatMap { message in @@ -85,23 +85,23 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { let serverID = message["id"] as? UInt64, let hexEncodedSignatureData = value["sig"] as? String, let signatureVersion = value["sigver"] as? UInt64, let body = message["text"] as? String, let user = message["user"] as? JSON, let hexEncodedPublicKey = user["username"] as? String, let timestamp = value["timestamp"] as? UInt64 else { - print("[Loki] Couldn't parse message for group chat with ID: \(group) on server: \(server) from: \(message).") + print("[Loki] Couldn't parse message for public chat channel with ID: \(channel) on server: \(server) from: \(message).") return nil } let displayName = user["name"] as? String ?? NSLocalizedString("Anonymous", comment: "") - let lastMessageServerID = getLastMessageServerID(for: group, on: server) - if serverID > (lastMessageServerID ?? 0) { setLastMessageServerID(for: group, on: server, to: serverID) } - let quote: LokiGroupMessage.Quote? + let lastMessageServerID = getLastMessageServerID(for: channel, on: server) + if serverID > (lastMessageServerID ?? 0) { setLastMessageServerID(for: channel, on: server, to: serverID) } + let quote: LokiPublicChatMessage.Quote? if let quoteAsJSON = value["quote"] as? JSON, let quotedMessageTimestamp = quoteAsJSON["id"] as? UInt64, let quoteeHexEncodedPublicKey = quoteAsJSON["author"] as? String, let quotedMessageBody = quoteAsJSON["text"] as? String { let quotedMessageServerID = message["reply_to"] as? UInt64 - quote = LokiGroupMessage.Quote(quotedMessageTimestamp: quotedMessageTimestamp, quoteeHexEncodedPublicKey: quoteeHexEncodedPublicKey, quotedMessageBody: quotedMessageBody, quotedMessageServerID: quotedMessageServerID) + quote = LokiPublicChatMessage.Quote(quotedMessageTimestamp: quotedMessageTimestamp, quoteeHexEncodedPublicKey: quoteeHexEncodedPublicKey, quotedMessageBody: quotedMessageBody, quotedMessageServerID: quotedMessageServerID) } else { quote = nil } - let signature = LokiGroupMessage.Signature(data: Data(hex: hexEncodedSignatureData), version: signatureVersion) - let result = LokiGroupMessage(serverID: serverID, hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName, body: body, type: publicChatMessageType, timestamp: timestamp, quote: quote, signature: signature) + let signature = LokiPublicChatMessage.Signature(data: Data(hex: hexEncodedSignatureData), version: signatureVersion) + let result = LokiPublicChatMessage(serverID: serverID, hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName, body: body, type: publicChatMessageType, timestamp: timestamp, quote: quote, signature: signature) guard result.hasValidSignature() else { - print("[Loki] Ignoring group chat message with invalid signature.") + print("[Loki] Ignoring public chat message with invalid signature.") return nil } return result @@ -109,11 +109,11 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { } } - public static func sendMessage(_ message: LokiGroupMessage, to group: UInt64, on server: String) -> Promise { + public static func sendMessage(_ message: LokiPublicChatMessage, to channel: UInt64, on server: String) -> Promise { guard let signedMessage = message.sign(with: userKeyPair.privateKey) else { return Promise(error: Error.signingFailed) } - return getAuthToken(for: server).then { token -> Promise in - print("[Loki] Sending message to group chat with ID: \(group) on server: \(server).") - let url = URL(string: "\(server)/channels/\(group)/messages")! + return getAuthToken(for: server).then { token -> Promise in + print("[Loki] Sending message to public chat channel with ID: \(channel) on server: \(server).") + let url = URL(string: "\(server)/channels/\(channel)/messages")! let parameters = signedMessage.toJSON() let request = TSRequest(url: url, method: "POST", parameters: parameters) request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ] @@ -124,13 +124,13 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" guard let json = rawResponse as? JSON, let messageAsJSON = json["data"] as? JSON, let serverID = messageAsJSON["id"] as? UInt64, let body = messageAsJSON["text"] as? String, let dateAsString = messageAsJSON["created_at"] as? String, let date = dateFormatter.date(from: dateAsString) else { - print("[Loki] Couldn't parse message for group chat with ID: \(group) on server: \(server) from: \(rawResponse).") + print("[Loki] Couldn't parse message for public chat channel with ID: \(channel) on server: \(server) from: \(rawResponse).") throw Error.parsingFailed } let timestamp = UInt64(date.timeIntervalSince1970) * 1000 - return LokiGroupMessage(serverID: serverID, hexEncodedPublicKey: userHexEncodedPublicKey, displayName: displayName, body: body, type: publicChatMessageType, timestamp: timestamp, quote: signedMessage.quote, signature: signedMessage.signature) + return LokiPublicChatMessage(serverID: serverID, hexEncodedPublicKey: userHexEncodedPublicKey, displayName: displayName, body: body, type: publicChatMessageType, timestamp: timestamp, quote: signedMessage.quote, signature: signedMessage.signature) } - }.recover { error -> Promise in + }.recover { error -> Promise in if let error = error as? NetworkManagerError, error.statusCode == 401 { print("[Loki] Group chat auth token for: \(server) expired; dropping it.") storage.dbReadWriteConnection.removeObject(forKey: server, inCollection: authTokenCollection) @@ -139,44 +139,44 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { }.retryingIfNeeded(maxRetryCount: maxRetryCount).map { message in Analytics.shared.track("Group Message Sent") return message - }.recover { error -> Promise in + }.recover { error -> Promise in Analytics.shared.track("Failed to Send Group Message") throw error } } - public static func getDeletedMessageServerIDs(for group: UInt64, on server: String) -> Promise<[UInt64]> { - print("[Loki] Getting deleted messages for group chat with ID: \(group) on server: \(server).") + public static func getDeletedMessageServerIDs(for channel: UInt64, on server: String) -> Promise<[UInt64]> { + print("[Loki] Getting deleted messages for public chat channel with ID: \(channel) on server: \(server).") let queryParameters: String - if let lastDeletionServerID = getLastDeletionServerID(for: group, on: server) { + if let lastDeletionServerID = getLastDeletionServerID(for: channel, on: server) { queryParameters = "since_id=\(lastDeletionServerID)" } else { queryParameters = "count=\(fallbackBatchCount)" } - let url = URL(string: "\(server)/loki/v1/channel/\(group)/deletes?\(queryParameters)")! + let url = URL(string: "\(server)/loki/v1/channel/\(channel)/deletes?\(queryParameters)")! let request = TSRequest(url: url) return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in guard let json = rawResponse as? JSON, let deletions = json["data"] as? [JSON] else { - print("[Loki] Couldn't parse deleted messages for group chat with ID: \(group) on server: \(server) from: \(rawResponse).") + print("[Loki] Couldn't parse deleted messages for public chat channel with ID: \(channel) on server: \(server) from: \(rawResponse).") throw Error.parsingFailed } return deletions.flatMap { deletion in guard let serverID = deletion["id"] as? UInt64, let messageServerID = deletion["message_id"] as? UInt64 else { - print("[Loki] Couldn't parse deleted message for group chat with ID: \(group) on server: \(server) from: \(deletion).") + print("[Loki] Couldn't parse deleted message for public chat channel with ID: \(channel) on server: \(server) from: \(deletion).") return nil } - let lastDeletionServerID = getLastDeletionServerID(for: group, on: server) - if serverID > (lastDeletionServerID ?? 0) { setLastDeletionServerID(for: group, on: server, to: serverID) } + let lastDeletionServerID = getLastDeletionServerID(for: channel, on: server) + if serverID > (lastDeletionServerID ?? 0) { setLastDeletionServerID(for: channel, on: server, to: serverID) } return messageServerID } } } - public static func deleteMessage(with messageID: UInt, for group: UInt64, on server: String, isSentByUser: Bool) -> Promise { + public static func deleteMessage(with messageID: UInt, for channel: UInt64, on server: String, isSentByUser: Bool) -> Promise { return getAuthToken(for: server).then { token -> Promise in let isModerationRequest = !isSentByUser - print("[Loki] Deleting message with ID: \(messageID) for group chat with ID: \(group) on server: \(server) (isModerationRequest = \(isModerationRequest)).") - let urlAsString = isSentByUser ? "\(server)/channels/\(group)/messages/\(messageID)" : "\(server)/loki/v1/moderation/message/\(messageID)" + print("[Loki] Deleting message with ID: \(messageID) for public chat channel with ID: \(channel) on server: \(server) (isModerationRequest = \(isModerationRequest)).") + let urlAsString = isSentByUser ? "\(server)/channels/\(channel)/messages/\(messageID)" : "\(server)/loki/v1/moderation/message/\(messageID)" let url = URL(string: urlAsString)! let request = TSRequest(url: url, method: "DELETE", parameters: [:]) request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ] @@ -186,27 +186,27 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { } } - public static func getModerators(for group: UInt64, on server: String) -> Promise> { - let url = URL(string: "\(server)/loki/v1/channel/\(group)/get_moderators")! + public static func getModerators(for channel: UInt64, on server: String) -> Promise> { + let url = URL(string: "\(server)/loki/v1/channel/\(channel)/get_moderators")! let request = TSRequest(url: url) return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in guard let json = rawResponse as? JSON, let moderators = json["moderators"] as? [String] else { - print("[Loki] Couldn't parse moderators for group chat with ID: \(group) on server: \(server) from: \(rawResponse).") + print("[Loki] Couldn't parse moderators for public chat channel with ID: \(channel) on server: \(server) from: \(rawResponse).") throw Error.parsingFailed } let moderatorAsSet = Set(moderators); if self.moderators.keys.contains(server) { - self.moderators[server]![group] = moderatorAsSet + self.moderators[server]![channel] = moderatorAsSet } else { - self.moderators[server] = [ group : moderatorAsSet ] + self.moderators[server] = [ channel : moderatorAsSet ] } return moderatorAsSet } } @objc (isUserModerator:forGroup:onServer:) - public static func isUserModerator(_ hexEncodedPublicString: String, for group: UInt64, on server: String) -> Bool { - return moderators[server]?[group]?.contains(hexEncodedPublicString) ?? false + public static func isUserModerator(_ hexEncodedPublicString: String, for channel: UInt64, on server: String) -> Bool { + return moderators[server]?[channel]?.contains(hexEncodedPublicString) ?? false } public static func setDisplayName(to newDisplayName: String?, on server: String) -> Promise { @@ -216,27 +216,27 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { let url = URL(string: "\(server)/users/me")! let request = TSRequest(url: url, method: "PATCH", parameters: parameters) request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ] - return TSNetworkManager.shared().makePromise(request: request).retryingIfNeeded(maxRetryCount: 3).map { _ in }.recover { error in + return TSNetworkManager.shared().makePromise(request: request).map { _ in }.recover { error in print("Couldn't update display name due to error: \(error).") throw error } - } + }.retryingIfNeeded(maxRetryCount: 3) } - public static func getChannelInfo(_ channel: UInt64, on server: String) -> Promise { + public static func getInfo(for channel: UInt64, on server: String) -> Promise { let url = URL(string: "\(server)/channels/\(channel)?include_annotations=1")! let request = TSRequest(url: url) return TSNetworkManager.shared().makePromise(request: request).map { $0.responseObject }.map { rawResponse in guard let json = rawResponse as? JSON, let data = json["data"] as? JSON, let annotations = data["annotations"] as? [JSON], - let infoAnnotation = annotations.first, - let info = infoAnnotation["value"] as? JSON, - let name = info["name"] as? String else { - print("[Loki] Couldn't parse info for group chat with ID: \(channel) on server: \(server) from: \(rawResponse).") + let annotation = annotations.first, + let info = annotation["value"] as? JSON, + let displayName = info["name"] as? String else { + print("[Loki] Couldn't parse info for public chat channel with ID: \(channel) on server: \(server) from: \(rawResponse).") throw Error.parsingFailed } - return name + return LokiPublicChatInfo(displayName: displayName) } } @@ -252,7 +252,7 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { } @objc(sendMessage:toGroup:onServer:) - public static func objc_sendMessage(_ message: LokiGroupMessage, to group: UInt64, on server: String) -> AnyPromise { + public static func objc_sendMessage(_ message: LokiPublicChatMessage, to group: UInt64, on server: String) -> AnyPromise { return AnyPromise.from(sendMessage(message, to: group, on: server)) } diff --git a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatInfo.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatInfo.swift new file mode 100644 index 00000000000..8195b24ac47 --- /dev/null +++ b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatInfo.swift @@ -0,0 +1,4 @@ + +public struct LokiPublicChatInfo { + public let displayName: String +} diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift similarity index 82% rename from SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift rename to SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift index 772b0ad61dc..5825e7de95b 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift +++ b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift @@ -11,7 +11,7 @@ public final class LokiPublicChatManager: NSObject { @objc public static let shared = LokiPublicChatManager() - private var chats: [String: LokiGroupChat] = [:] + private var chats: [String: LokiPublicChat] = [:] private var pollers: [String: GroupChatPoller] = [:] private var isPolling = false @@ -29,11 +29,11 @@ public final class LokiPublicChatManager: NSObject { } @objc public func startPollersIfNeeded() { - for (threadID, groupChat) in chats { + for (threadID, publicChat) in chats { if let poller = pollers[threadID] { poller.startIfNeeded() } else { - let poller = GroupChatPoller(for: groupChat) + let poller = GroupChatPoller(for: publicChat) poller.startIfNeeded() pollers[threadID] = poller } @@ -46,7 +46,7 @@ public final class LokiPublicChatManager: NSObject { isPolling = false } - public func addChat(server: String, channel: UInt64) -> Promise { + public func addChat(server: String, channel: UInt64) -> Promise { if let existingChat = getChat(server: server, channel: channel) { if let chat = self.addChat(server: server, channel: channel, name: existingChat.displayName) { return Promise.value(chat) @@ -55,18 +55,18 @@ public final class LokiPublicChatManager: NSObject { } } - return LokiGroupChatAPI.getAuthToken(for: server).then { token in - return LokiGroupChatAPI.getChannelInfo(channel, on: server) - }.map { channelInfo -> LokiGroupChat in - guard let chat = self.addChat(server: server, channel: channel, name: channelInfo) else { throw Error.chatCreationFailed } + return LokiPublicChatAPI.getAuthToken(for: server).then { token in + return LokiPublicChatAPI.getInfo(for: channel, on: server) + }.map { channelInfo -> LokiPublicChat in + guard let chat = self.addChat(server: server, channel: channel, name: channelInfo.displayName) else { throw Error.chatCreationFailed } return chat } } @discardableResult @objc(addChatWithServer:channel:name:) - public func addChat(server: String, channel: UInt64, name: String) -> LokiGroupChat? { - guard let chat = LokiGroupChat(channel: channel, server: server, displayName: name, isDeletable: true) else { return nil } + public func addChat(server: String, channel: UInt64, name: String) -> LokiPublicChat? { + guard let chat = LokiPublicChat(channel: channel, server: server, displayName: name, isDeletable: true) else { return nil } let model = TSGroupModel(title: chat.displayName, memberIds: [ourHexEncodedPublicKey!, chat.server], image: nil, groupId: chat.idAsData) // Store the group chat mapping @@ -85,7 +85,7 @@ public final class LokiPublicChatManager: NSObject { } // Save the group chat - LokiDatabaseUtilities.setGroupChat(chat, for: thread.uniqueId!, in: transaction) + LokiDatabaseUtilities.setPublicChat(chat, for: thread.uniqueId!, in: transaction) } // Update chats and pollers @@ -101,7 +101,7 @@ public final class LokiPublicChatManager: NSObject { private func refreshChatsAndPollers() { storage.dbReadConnection.read { transaction in - let newChats = LokiDatabaseUtilities.getAllGroupChats(in: transaction) + let newChats = LokiDatabaseUtilities.getAllPublicChats(in: transaction) // Remove any chats that don't exist in the database let removedChatThreadIds = self.chats.keys.filter { !newChats.keys.contains($0) } @@ -124,18 +124,18 @@ public final class LokiPublicChatManager: NSObject { // Reset the last message cache if let chat = self.chats[threadId] { - LokiGroupChatAPI.resetLastMessageCache(for: chat.channel, on: chat.server) + LokiPublicChatAPI.resetLastMessageCache(for: chat.channel, on: chat.server) } // Remove the chat from the db storage.dbReadWriteConnection.readWrite { transaction in - LokiDatabaseUtilities.removeGroupChat(for: threadId, in: transaction) + LokiDatabaseUtilities.removePublicChat(for: threadId, in: transaction) } refreshChatsAndPollers() } - private func getChat(server: String, channel: UInt64) -> LokiGroupChat? { + private func getChat(server: String, channel: UInt64) -> LokiPublicChat? { return chats.values.first { chat in return chat.server == server && chat.channel == channel } diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupMessage.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatMessage.swift similarity index 91% rename from SignalServiceKit/src/Loki/API/Group Chat/LokiGroupMessage.swift rename to SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatMessage.swift index 0d90171c805..9baf2e072e0 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupMessage.swift +++ b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatMessage.swift @@ -1,7 +1,7 @@ import PromiseKit @objc(LKGroupMessage) -public final class LokiGroupMessage : NSObject { +public final class LokiPublicChatMessage : NSObject { public let serverID: UInt64? public let hexEncodedPublicKey: String public let displayName: String @@ -62,18 +62,18 @@ public final class LokiGroupMessage : NSObject { } // MARK: Crypto - internal func sign(with privateKey: Data) -> LokiGroupMessage? { + internal func sign(with privateKey: Data) -> LokiPublicChatMessage? { guard let data = getValidationData(for: signatureVersion) else { - print("[Loki] Failed to sign group chat message.") + print("[Loki] Failed to sign public chat message.") return nil } let userKeyPair = OWSIdentityManager.shared().identityKeyPair()! guard let signatureData = try? Ed25519.sign(data, with: userKeyPair) else { - print("[Loki] Failed to sign group chat message.") + print("[Loki] Failed to sign public chat message.") return nil } let signature = Signature(data: signatureData, version: signatureVersion) - return LokiGroupMessage(serverID: serverID, hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName, body: body, type: type, timestamp: timestamp, quote: quote, signature: signature) + return LokiPublicChatMessage(serverID: serverID, hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName, body: body, type: type, timestamp: timestamp, quote: quote, signature: signature) } internal func hasValidSignature() -> Bool { diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiRSSFeed.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiRSSFeed.swift similarity index 100% rename from SignalServiceKit/src/Loki/API/Group Chat/LokiRSSFeed.swift rename to SignalServiceKit/src/Loki/API/Public Chat/LokiRSSFeed.swift diff --git a/SignalServiceKit/src/Loki/API/Group Chat/GroupChatPoller.swift b/SignalServiceKit/src/Loki/API/Public Chat/PublicChatPoller.swift similarity index 91% rename from SignalServiceKit/src/Loki/API/Group Chat/GroupChatPoller.swift rename to SignalServiceKit/src/Loki/API/Public Chat/PublicChatPoller.swift index 20435b8d48b..5ac65b9f33a 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/GroupChatPoller.swift +++ b/SignalServiceKit/src/Loki/API/Public Chat/PublicChatPoller.swift @@ -1,7 +1,7 @@ -@objc(LKGroupChatPoller) +@objc(LKPublicChatPoller) public final class GroupChatPoller : NSObject { - private let group: LokiGroupChat + private let group: LokiPublicChat private var pollForNewMessagesTimer: Timer? = nil private var pollForDeletedMessagesTimer: Timer? = nil private var pollForModeratorsTimer: Timer? = nil @@ -15,7 +15,7 @@ public final class GroupChatPoller : NSObject { // MARK: Lifecycle @objc(initForGroup:) - public init(for group: LokiGroupChat) { + public init(for group: LokiPublicChat) { self.group = group super.init() } @@ -45,7 +45,7 @@ public final class GroupChatPoller : NSObject { let group = self.group let userHexEncodedPublicKey = self.userHexEncodedPublicKey // Processing logic for incoming messages - func processIncomingMessage(_ message: LokiGroupMessage) { + func processIncomingMessage(_ message: LokiPublicChatMessage) { let senderHexEncodedPublicKey = message.hexEncodedPublicKey let endIndex = senderHexEncodedPublicKey.endIndex let cutoffIndex = senderHexEncodedPublicKey.index(endIndex, offsetBy: -8) @@ -80,7 +80,7 @@ public final class GroupChatPoller : NSObject { } } // Processing logic for outgoing messages - func processOutgoingMessage(_ message: LokiGroupMessage) { + func processOutgoingMessage(_ message: LokiPublicChatMessage) { guard let messageServerID = message.serverID else { return } let storage = OWSPrimaryStorage.shared() var isDuplicate = false @@ -102,7 +102,7 @@ public final class GroupChatPoller : NSObject { storage.dbReadWriteConnection.readWrite { transaction in message.update(withSentRecipient: group.server, wasSentByUD: false, transaction: transaction) message.saveGroupChatServerID(messageServerID, in: transaction) - guard let messageID = message.uniqueId else { return print("[Loki] Failed to save group message.") } + guard let messageID = message.uniqueId else { return print("[Loki] Failed to save public chat message.") } storage.setIDForMessageWithServerID(UInt(messageServerID), to: messageID, in: transaction) } if let linkPreviewURL = OWSLinkPreview.previewUrl(forMessageBodyText: message.body, selectedRange: nil) { @@ -110,7 +110,7 @@ public final class GroupChatPoller : NSObject { } } // Poll - let _ = LokiGroupChatAPI.getMessages(for: group.channel, on: group.server).done(on: .main) { messages in + let _ = LokiPublicChatAPI.getMessages(for: group.channel, on: group.server).done(on: .main) { messages in messages.forEach { message in if message.hexEncodedPublicKey != userHexEncodedPublicKey { processIncomingMessage(message) @@ -123,7 +123,7 @@ public final class GroupChatPoller : NSObject { private func pollForDeletedMessages() { let group = self.group - let _ = LokiGroupChatAPI.getDeletedMessageServerIDs(for: group.channel, on: group.server).done { deletedMessageServerIDs in + let _ = LokiPublicChatAPI.getDeletedMessageServerIDs(for: group.channel, on: group.server).done { deletedMessageServerIDs in let storage = OWSPrimaryStorage.shared() storage.dbReadWriteConnection.readWrite { transaction in let deletedMessageIDs = deletedMessageServerIDs.compactMap { storage.getIDForMessage(withServerID: UInt($0), in: transaction) } @@ -135,6 +135,6 @@ public final class GroupChatPoller : NSObject { } private func pollForModerators() { - let _ = LokiGroupChatAPI.getModerators(for: group.channel, on: group.server) + let _ = LokiPublicChatAPI.getModerators(for: group.channel, on: group.server) } } diff --git a/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift b/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift index 3e87962894f..8e2c2abf568 100644 --- a/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift +++ b/SignalServiceKit/src/Loki/Database/LokiDatabaseUtilities.swift @@ -27,31 +27,31 @@ public final class LokiDatabaseUtilities : NSObject { return OWSPrimaryStorage.shared().getMasterHexEncodedPublicKey(for: slaveHexEncodedPublicKey, in: transaction) } - // MARK: Group Chats - private static let groupChatCollection = "LokiGroupChatCollection" + // MARK: Public Chats + private static let publicChatCollection = "LokiPublicChatCollection" - @objc(getAllGroupChats:) - public static func getAllGroupChats(in transaction: YapDatabaseReadTransaction) -> [String:LokiGroupChat] { - var result = [String:LokiGroupChat]() - transaction.enumerateKeysAndObjects(inCollection: groupChatCollection) { threadID, object, _ in - guard let groupChat = object as? LokiGroupChat else { return } - result[threadID] = groupChat + @objc(getAllPublicChats:) + public static func getAllPublicChats(in transaction: YapDatabaseReadTransaction) -> [String:LokiPublicChat] { + var result = [String:LokiPublicChat]() + transaction.enumerateKeysAndObjects(inCollection: publicChatCollection) { threadID, object, _ in + guard let publicChat = object as? LokiPublicChat else { return } + result[threadID] = publicChat } return result } - @objc(getGroupChatForThreadID:transaction:) - public static func getGroupChat(for threadID: String, in transaction: YapDatabaseReadTransaction) -> LokiGroupChat? { - return transaction.object(forKey: threadID, inCollection: groupChatCollection) as? LokiGroupChat + @objc(getPublicChatForThreadID:transaction:) + public static func getPublicChat(for threadID: String, in transaction: YapDatabaseReadTransaction) -> LokiPublicChat? { + return transaction.object(forKey: threadID, inCollection: publicChatCollection) as? LokiPublicChat } - @objc(setGroupChat:threadID:transaction:) - public static func setGroupChat(_ groupChat: LokiGroupChat, for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { - transaction.setObject(groupChat, forKey: threadID, inCollection: groupChatCollection) + @objc(setPublicChat:threadID:transaction:) + public static func setPublicChat(_ publicChat: LokiPublicChat, for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { + transaction.setObject(publicChat, forKey: threadID, inCollection: publicChatCollection) } - @objc(removeGroupChatForThreadID:transaction:) - public static func removeGroupChat(for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { - transaction.removeObject(forKey: threadID, inCollection: groupChatCollection) + @objc(removePublicChatForThreadID:transaction:) + public static func removePublicChat(for threadID: String, in transaction: YapDatabaseReadWriteTransaction) { + transaction.removeObject(forKey: threadID, inCollection: publicChatCollection) } } diff --git a/SignalServiceKit/src/Loki/Utilities/DisplayNameUtilities.swift b/SignalServiceKit/src/Loki/Utilities/DisplayNameUtilities.swift index e02cf92c238..8b76e226cb8 100644 --- a/SignalServiceKit/src/Loki/Utilities/DisplayNameUtilities.swift +++ b/SignalServiceKit/src/Loki/Utilities/DisplayNameUtilities.swift @@ -20,15 +20,15 @@ public final class DisplayNameUtilities : NSObject { } } - @objc public static func getGroupChatDisplayName(for hexEncodedPublicKey: String, in channel: UInt64, on server: String) -> String? { + @objc public static func getPublicChatDisplayName(for hexEncodedPublicKey: String, in channel: UInt64, on server: String) -> String? { var result: String? OWSPrimaryStorage.shared().dbReadConnection.read { transaction in - result = getGroupChatDisplayName(for: hexEncodedPublicKey, in: channel, on: server, using: transaction) + result = getPublicChatDisplayName(for: hexEncodedPublicKey, in: channel, on: server, using: transaction) } return result } - @objc public static func getGroupChatDisplayName(for hexEncodedPublicKey: String, in channel: UInt64, on server: String, using transaction: YapDatabaseReadTransaction) -> String? { + @objc public static func getPublicChatDisplayName(for hexEncodedPublicKey: String, in channel: UInt64, on server: String, using transaction: YapDatabaseReadTransaction) -> String? { if hexEncodedPublicKey == userHexEncodedPublicKey { return userDisplayName } else { diff --git a/SignalServiceKit/src/Messages/Interactions/TSMessage.h b/SignalServiceKit/src/Messages/Interactions/TSMessage.h index 682deb47d6b..70be6e19bd0 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSMessage.h +++ b/SignalServiceKit/src/Messages/Interactions/TSMessage.h @@ -46,8 +46,8 @@ typedef NS_ENUM(NSInteger, LKMessageFriendRequestStatus) { @property (nonatomic, readonly) BOOL hasFriendRequestStatusMessage; @property (nonatomic) BOOL isP2P; // Group chat -@property (nonatomic) uint64_t groupChatServerID; -@property (nonatomic, readonly) BOOL isGroupChatMessage; +@property (nonatomic) uint64_t groupChatServerID; // Should ideally be publicChatServerID +@property (nonatomic, readonly) BOOL isGroupChatMessage; // Should ideally be isPublicChatMessage - (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE; diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 4b796818f38..2d07a0f24e5 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -504,8 +504,12 @@ - (void)sendMessageToService:(TSOutgoingMessage *)message [recipientIds addObject:self.tsAccountManager.localNumber]; } else if (thread.isGroupThread) { [self.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - LKGroupChat *groupChat = [LKDatabaseUtilities getGroupChatForThreadID:thread.uniqueId transaction:transaction]; - [recipientIds addObject:groupChat.server]; + LKPublicChat *publicChat = [LKDatabaseUtilities getPublicChatForThreadID:thread.uniqueId transaction:transaction]; + if (publicChat != nil) { + [recipientIds addObject:publicChat.server]; + } else { + // TODO: Handle + } }]; } else if ([thread isKindOfClass:[TSContactThread class]]) { NSString *recipientContactId = ((TSContactThread *)thread).contactIdentifier; @@ -1188,11 +1192,11 @@ - (void)sendMessage:(OWSMessageSend *)messageSend [self messageSendDidFail:messageSend deviceMessages:deviceMessages statusCode:statusCode error:error responseData:responseData]; }; - __block LKGroupChat *groupChat; + __block LKPublicChat *publicChat; [OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { - groupChat = [LKDatabaseUtilities getGroupChatForThreadID:message.uniqueThreadId transaction: transaction]; + publicChat = [LKDatabaseUtilities getPublicChatForThreadID:message.uniqueThreadId transaction: transaction]; }]; - if (groupChat != nil) { + if (publicChat != nil) { NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey; NSString *displayName = SSKEnvironment.shared.profileManager.localProfileName; if (displayName == nil) { displayName = @"Anonymous"; } @@ -1205,9 +1209,9 @@ - (void)sendMessage:(OWSMessageSend *)messageSend quotedMessageServerID = [LKDatabaseUtilities getServerIDForQuoteWithID:quoteID quoteeHexEncodedPublicKey:quoteeHexEncodedPublicKey threadID:messageSend.thread.uniqueId transaction:transaction]; }]; } - LKGroupMessage *groupMessage = [[LKGroupMessage alloc] initWithHexEncodedPublicKey:userHexEncodedPublicKey displayName:displayName body:message.body type:LKGroupChatAPI.publicChatMessageType + LKGroupMessage *groupMessage = [[LKGroupMessage alloc] initWithHexEncodedPublicKey:userHexEncodedPublicKey displayName:displayName body:message.body type:LKPublicChatAPI.publicChatMessageType timestamp:message.timestamp quotedMessageTimestamp:quoteID quoteeHexEncodedPublicKey:quoteeHexEncodedPublicKey quotedMessageBody:quote.body quotedMessageServerID:quotedMessageServerID signatureData:nil signatureVersion:0]; - [[LKGroupChatAPI sendMessage:groupMessage toGroup:groupChat.channel onServer:groupChat.server] + [[LKPublicChatAPI sendMessage:groupMessage toGroup:publicChat.channel onServer:publicChat.server] .thenOn(OWSDispatch.sendingQueue, ^(LKGroupMessage *groupMessage) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [message saveGroupChatServerID:groupMessage.serverID in:transaction]; From 8e5e113ecfcd9e2a577dce2a44e7b89c40d83bc9 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Tue, 15 Oct 2019 10:50:06 +1100 Subject: [PATCH 14/17] Clean --- Signal.xcodeproj/project.pbxproj | 16 +++------- Signal/src/AppDelegate.h | 3 +- Signal/src/AppDelegate.m | 12 ++------ ...edPoller.swift => LokiRSSFeedPoller.swift} | 2 +- .../Loki/MentionCandidateSelectionView.swift | 16 +++++----- .../src/Loki/Onboarding/DisplayNameVC.swift | 5 ++++ .../ConversationInputToolbar.m | 2 +- .../HomeView/HomeViewController.m | 4 +-- .../environment/VersionMigrations.m | 4 +-- ...{LongPoller.swift => LokiLongPoller.swift} | 2 +- .../API/{Public Chat => }/LokiRSSFeed.swift | 0 .../Loki/API/Public Chat/LokiPublicChat.swift | 10 ------- .../API/Public Chat/LokiPublicChatAPI.swift | 15 ++++++++-- .../Public Chat/LokiPublicChatManager.swift | 6 ++-- ...oller.swift => LokiPublicChatPoller.swift} | 30 +++++++++---------- 15 files changed, 58 insertions(+), 69 deletions(-) rename Signal/src/Loki/{Group Chat/RSSFeedPoller.swift => LokiRSSFeedPoller.swift} (98%) rename SignalServiceKit/src/Loki/API/{LongPoller.swift => LokiLongPoller.swift} (98%) rename SignalServiceKit/src/Loki/API/{Public Chat => }/LokiRSSFeed.swift (100%) rename SignalServiceKit/src/Loki/API/Public Chat/{PublicChatPoller.swift => LokiPublicChatPoller.swift} (88%) diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 2c08f62f4b7..e7ef3e96a4f 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -565,7 +565,7 @@ B821F2F82272CED3002C88C0 /* DisplayNameVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B821F2F72272CED3002C88C0 /* DisplayNameVC.swift */; }; B821F2FA2272CEEE002C88C0 /* SeedVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B821F2F92272CEEE002C88C0 /* SeedVC.swift */; }; B8258493230FA5E9001B41CB /* ScanQRCodeVC.m in Sources */ = {isa = PBXBuildFile; fileRef = B8258492230FA5E9001B41CB /* ScanQRCodeVC.m */; }; - B82584A02315024B001B41CB /* RSSFeedPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = B825849F2315024B001B41CB /* RSSFeedPoller.swift */; }; + B82584A02315024B001B41CB /* LokiRSSFeedPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = B825849F2315024B001B41CB /* LokiRSSFeedPoller.swift */; }; B846365B22B7418B00AF1514 /* Identicon+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */; }; B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84664F4235022F30083A1CD /* MentionUtilities.swift */; }; B86BD08123399883000F5AE3 /* QRCodeModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08023399883000F5AE3 /* QRCodeModal.swift */; }; @@ -1377,7 +1377,7 @@ B821F2F92272CEEE002C88C0 /* SeedVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedVC.swift; sourceTree = ""; }; B8258491230FA5DA001B41CB /* ScanQRCodeVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScanQRCodeVC.h; sourceTree = ""; }; B8258492230FA5E9001B41CB /* ScanQRCodeVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScanQRCodeVC.m; sourceTree = ""; }; - B825849F2315024B001B41CB /* RSSFeedPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSSFeedPoller.swift; sourceTree = ""; }; + B825849F2315024B001B41CB /* LokiRSSFeedPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LokiRSSFeedPoller.swift; sourceTree = ""; }; B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Identicon+ObjC.swift"; sourceTree = ""; }; B84664F4235022F30083A1CD /* MentionUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionUtilities.swift; sourceTree = ""; }; B86BD08023399883000F5AE3 /* QRCodeModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeModal.swift; sourceTree = ""; }; @@ -2647,7 +2647,6 @@ B8439518228510E9000563FE /* Loki */ = { isa = PBXGroup; children = ( - B86BD0892339A278000F5AE3 /* Group Chat */, B86BD0872339A1ED000F5AE3 /* Onboarding */, B86BD08223399ABF000F5AE3 /* Settings */, B86BD0882339A253000F5AE3 /* Utilities */, @@ -2655,6 +2654,7 @@ B8162F0422892C5F00D46544 /* FriendRequestViewDelegate.swift */, 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */, B89841E222B7579F00B1BDC6 /* NewConversationVC.swift */, + B825849F2315024B001B41CB /* LokiRSSFeedPoller.swift */, B8258491230FA5DA001B41CB /* ScanQRCodeVC.h */, B8258492230FA5E9001B41CB /* ScanQRCodeVC.m */, 24BD2608234DA2050008EB0A /* NewPublicChatVC.swift */, @@ -2706,14 +2706,6 @@ path = Utilities; sourceTree = ""; }; - B86BD0892339A278000F5AE3 /* Group Chat */ = { - isa = PBXGroup; - children = ( - B825849F2315024B001B41CB /* RSSFeedPoller.swift */, - ); - path = "Group Chat"; - sourceTree = ""; - }; D221A07E169C9E5E00537ABF = { isa = PBXGroup; children = ( @@ -3786,7 +3778,7 @@ 4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */, 4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */, 3496955E219B605E00DCFE74 /* PhotoLibrary.swift in Sources */, - B82584A02315024B001B41CB /* RSSFeedPoller.swift in Sources */, + B82584A02315024B001B41CB /* LokiRSSFeedPoller.swift in Sources */, 24A830A22293CD0100F4CAC0 /* LokiP2PServer.swift in Sources */, 349ED990221B0194008045B0 /* Onboarding2FAViewController.swift in Sources */, 45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */, diff --git a/Signal/src/AppDelegate.h b/Signal/src/AppDelegate.h index 273023d2ab2..3cf39a2811f 100644 --- a/Signal/src/AppDelegate.h +++ b/Signal/src/AppDelegate.h @@ -10,9 +10,8 @@ extern NSString *const AppDelegateStoryboardMain; - (void)startLongPollerIfNeeded; - (void)stopLongPollerIfNeeded; -- (void)createGroupChatsIfNeeded; +- (void)setUpDefaultPublicChatsIfNeeded; - (void)createRSSFeedsIfNeeded; -- (void)startGroupChatPollersIfNeeded; - (void)startRSSFeedPollersIfNeeded; @end diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 3afa2da8607..abe7bbd7701 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -1534,11 +1534,10 @@ - (LKRSSFeed *)lokiMessengerUpdatesFeed return [[LKRSSFeed alloc] initWithId:@"loki.network.messenger-updates.feed" server:@"https://loki.network/category/messenger-updates/feed/" displayName:NSLocalizedString(@"Loki Messenger Updates", @"") isDeletable:false]; } -- (void)createGroupChatsIfNeeded +- (void)setUpDefaultPublicChatsIfNeeded { - // Setup our default public chats - for (LKPublicChat *chat in LKPublicChat.defaultChats) { - NSString *userDefaultsKey = [@"isGroupChatSetUp." stringByAppendingString:chat.id]; + for (LKPublicChat *chat in LKPublicChatAPI.defaultChats) { + NSString *userDefaultsKey = [@"isGroupChatSetUp." stringByAppendingString:chat.id]; // Should ideally be isPublicChatSetUp BOOL isChatSetUp = [NSUserDefaults.standardUserDefaults boolForKey:userDefaultsKey]; if (!isChatSetUp || !chat.isDeletable) { [LKPublicChatManager.shared addChatWithServer:chat.server channel:chat.channel name:chat.displayName]; @@ -1593,11 +1592,6 @@ - (void)createRSSFeedPollersIfNeeded } } -- (void)startGroupChatPollersIfNeeded -{ - [LKPublicChatManager.shared startPollersIfNeeded]; -} - - (void)startRSSFeedPollersIfNeeded { [self createRSSFeedPollersIfNeeded]; diff --git a/Signal/src/Loki/Group Chat/RSSFeedPoller.swift b/Signal/src/Loki/LokiRSSFeedPoller.swift similarity index 98% rename from Signal/src/Loki/Group Chat/RSSFeedPoller.swift rename to Signal/src/Loki/LokiRSSFeedPoller.swift index 5636e1386f2..2f2d3818e98 100644 --- a/Signal/src/Loki/Group Chat/RSSFeedPoller.swift +++ b/Signal/src/Loki/LokiRSSFeedPoller.swift @@ -1,7 +1,7 @@ import FeedKit @objc(LKRSSFeedPoller) -public final class RSSFeedPoller : NSObject { +public final class LokiRSSFeedPoller : NSObject { private let feed: LokiRSSFeed private var timer: Timer? = nil private var hasStarted = false diff --git a/Signal/src/Loki/MentionCandidateSelectionView.swift b/Signal/src/Loki/MentionCandidateSelectionView.swift index 1f2f16c4d66..26dc3b29e88 100644 --- a/Signal/src/Loki/MentionCandidateSelectionView.swift +++ b/Signal/src/Loki/MentionCandidateSelectionView.swift @@ -5,13 +5,13 @@ final class MentionCandidateSelectionView : UIView, UITableViewDataSource, UITableViewDelegate { @objc var mentionCandidates: [Mention] = [] { didSet { tableView.reloadData() } } @objc var publicChatServer: String? - var publicChatServerID: UInt64? + var publicChatChannel: UInt64? @objc var delegate: MentionCandidateSelectionViewDelegate? // MARK: Convenience - @objc(setPublicChatServerID:) - func setPublicChatServerID(to publicChatServerID: UInt64) { - self.publicChatServerID = publicChatServerID != 0 ? publicChatServerID : nil + @objc(setPublicChatChannel:) + func setPublicChatChannel(to publicChatChannel: UInt64) { + self.publicChatChannel = publicChatChannel != 0 ? publicChatChannel : nil } // MARK: Components @@ -52,7 +52,7 @@ final class MentionCandidateSelectionView : UIView, UITableViewDataSource, UITab let mentionCandidate = mentionCandidates[indexPath.row] cell.mentionCandidate = mentionCandidate cell.publicChatServer = publicChatServer - cell.publicChatServerID = publicChatServerID + cell.publicChatChannel = publicChatChannel return cell } @@ -70,7 +70,7 @@ private extension MentionCandidateSelectionView { final class Cell : UITableViewCell { var mentionCandidate = Mention(hexEncodedPublicKey: "", displayName: "") { didSet { update() } } var publicChatServer: String? - var publicChatServerID: UInt64? + var publicChatChannel: UInt64? // MARK: Components private lazy var profilePictureImageView = AvatarImageView() @@ -130,8 +130,8 @@ private extension MentionCandidateSelectionView { displayNameLabel.text = mentionCandidate.displayName let profilePicture = OWSContactAvatarBuilder(signalId: mentionCandidate.hexEncodedPublicKey, colorName: .blue, diameter: 36).build() profilePictureImageView.image = profilePicture - if let publicChatServer = publicChatServer, let publicChatServerID = publicChatServerID { - let isUserModerator = LokiPublicChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, for: publicChatServerID, on: publicChatServer) + if let server = publicChatServer, let channel = publicChatChannel { + let isUserModerator = LokiPublicChatAPI.isUserModerator(mentionCandidate.hexEncodedPublicKey, for: channel, on: server) moderatorIconImageView.isHidden = !isUserModerator } else { moderatorIconImageView.isHidden = true diff --git a/Signal/src/Loki/Onboarding/DisplayNameVC.swift b/Signal/src/Loki/Onboarding/DisplayNameVC.swift index 58c5bf7e33e..ae6640cc251 100644 --- a/Signal/src/Loki/Onboarding/DisplayNameVC.swift +++ b/Signal/src/Loki/Onboarding/DisplayNameVC.swift @@ -64,6 +64,11 @@ final class DisplayNameVC : OnboardingBaseViewController { TSAccountManager.sharedInstance().didRegister() UserDefaults.standard.set(true, forKey: "didUpdateForMainnet") onboardingController.verificationDidComplete(fromView: self) + let appDelegate = UIApplication.shared.delegate as! AppDelegate + appDelegate.setUpDefaultPublicChatsIfNeeded() + appDelegate.createRSSFeedsIfNeeded() + LokiPublicChatManager.shared.startPollersIfNeeded() + appDelegate.startRSSFeedPollersIfNeeded() OWSProfileManager.shared().updateLocalProfileName(displayName, avatarImage: nil, success: { }, failure: { }) // Try to save the user name but ignore the result } } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m index c31d0791572..29f9961a5c7 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m @@ -1099,7 +1099,7 @@ - (void)showMentionCandidateSelectionViewFor:(NSArray *)mentionCand }]; if (publicChat != nil) { self.mentionCandidateSelectionView.publicChatServer = publicChat.server; - [self.mentionCandidateSelectionView setPublicChatServerID:publicChat.channel]; + [self.mentionCandidateSelectionView setPublicChatChannel:publicChat.channel]; } self.mentionCandidateSelectionView.mentionCandidates = mentionCandidates; self.mentionCandidateSelectionViewSizeConstraint.constant = 6 + MIN(mentionCandidates.count, 4) * 52; diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 23286342ff2..c7bd82edcb1 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -693,9 +693,9 @@ - (void)viewDidLoad } if (OWSIdentityManager.sharedManager.identityKeyPair != nil) { AppDelegate *appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate; - [appDelegate createGroupChatsIfNeeded]; + [appDelegate setUpDefaultPublicChatsIfNeeded]; [appDelegate createRSSFeedsIfNeeded]; - [appDelegate startGroupChatPollersIfNeeded]; + [LKPublicChatManager.shared startPollersIfNeeded]; [appDelegate startRSSFeedPollersIfNeeded]; } } diff --git a/SignalMessaging/environment/VersionMigrations.m b/SignalMessaging/environment/VersionMigrations.m index a195c144461..5d6288e6af2 100644 --- a/SignalMessaging/environment/VersionMigrations.m +++ b/SignalMessaging/environment/VersionMigrations.m @@ -169,11 +169,11 @@ + (void)clearBloomFilterCache # pragma mark Loki - Upgrading to Public Chat Manager -// Versions less than or equal to 1.2.0 didn't store group chat mappings +// Versions less than or equal to 1.2.0 didn't store public chat mappings + (void)updatePublicChatMapping { [OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) { - for (LKPublicChat *chat in LKPublicChat.defaultChats) { + for (LKPublicChat *chat in LKPublicChatAPI.defaultChats) { TSGroupThread *thread = [TSGroupThread threadWithGroupId:chat.idAsData transaction:transaction]; if (thread != nil) { [LKDatabaseUtilities setPublicChat:chat threadID:thread.uniqueId transaction:transaction]; diff --git a/SignalServiceKit/src/Loki/API/LongPoller.swift b/SignalServiceKit/src/Loki/API/LokiLongPoller.swift similarity index 98% rename from SignalServiceKit/src/Loki/API/LongPoller.swift rename to SignalServiceKit/src/Loki/API/LokiLongPoller.swift index 805db762da3..1acb6f7dbeb 100644 --- a/SignalServiceKit/src/Loki/API/LongPoller.swift +++ b/SignalServiceKit/src/Loki/API/LokiLongPoller.swift @@ -1,7 +1,7 @@ import PromiseKit @objc(LKLongPoller) -public final class LongPoller : NSObject { +public final class LokiLongPoller : NSObject { private let onMessagesReceived: ([SSKProtoEnvelope]) -> Void private let storage = OWSPrimaryStorage.shared() private var hasStarted = false diff --git a/SignalServiceKit/src/Loki/API/Public Chat/LokiRSSFeed.swift b/SignalServiceKit/src/Loki/API/LokiRSSFeed.swift similarity index 100% rename from SignalServiceKit/src/Loki/API/Public Chat/LokiRSSFeed.swift rename to SignalServiceKit/src/Loki/API/LokiRSSFeed.swift diff --git a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChat.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChat.swift index 7720dae795c..b8958b67f8d 100644 --- a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChat.swift +++ b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChat.swift @@ -8,16 +8,6 @@ public final class LokiPublicChat : NSObject, NSCoding { @objc public let displayName: String @objc public let isDeletable: Bool - @objc public static var defaultChats: [LokiPublicChat] { - var chats = [LokiPublicChat(channel: UInt64(1), server: "https://chat.lokinet.org", displayName: NSLocalizedString("Loki Public Chat", comment: ""), isDeletable: true)!] - - #if DEBUG - chats.append(LokiPublicChat(channel: UInt64(1), server: "https://chat-dev.lokinet.org", displayName: "Loki Dev Chat", isDeletable: true)!) - #endif - - return chats - } - @objc public init?(channel: UInt64, server: String, displayName: String, isDeletable: Bool) { let id = "\(server).\(channel)" self.id = id diff --git a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatAPI.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatAPI.swift index f258cd8f7c7..7701c972257 100644 --- a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatAPI.swift +++ b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatAPI.swift @@ -12,6 +12,15 @@ public final class LokiPublicChatAPI : LokiDotNetAPI { @objc private static let channelInfoType = "net.patter-app.settings" @objc public static let publicChatMessageType = "network.loki.messenger.publicChat" + @objc public static let defaultChats: [LokiPublicChat] = { + var result: [LokiPublicChat] = [] + result.append(LokiPublicChat(channel: 1, server: "https://chat.lokinet.org", displayName: NSLocalizedString("Loki Public Chat", comment: ""), isDeletable: true)!) + #if DEBUG + result.append(LokiPublicChat(channel: 1, server: "https://chat-dev.lokinet.org", displayName: "Loki Dev Chat", isDeletable: true)!) + #endif + return result + }() + // MARK: Convenience private static var userDisplayName: String { return SSKEnvironment.shared.contactsManager.displayName(forPhoneIdentifier: userHexEncodedPublicKey) ?? "Anonymous" @@ -240,9 +249,9 @@ public final class LokiPublicChatAPI : LokiDotNetAPI { } } - public static func resetLastMessageCache(for group: UInt64, on server: String) { - removeLastMessageServerID(for: group, on: server) - removeLastDeletionServerID(for: group, on: server) + public static func clearCaches(for channel: UInt64, on server: String) { + removeLastMessageServerID(for: channel, on: server) + removeLastDeletionServerID(for: channel, on: server) } // MARK: Public API (Obj-C) diff --git a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift index 5825e7de95b..fe67d10edf4 100644 --- a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift +++ b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift @@ -12,7 +12,7 @@ public final class LokiPublicChatManager: NSObject { @objc public static let shared = LokiPublicChatManager() private var chats: [String: LokiPublicChat] = [:] - private var pollers: [String: GroupChatPoller] = [:] + private var pollers: [String: LokiPublicChatPoller] = [:] private var isPolling = false private let storage = OWSPrimaryStorage.shared() @@ -33,7 +33,7 @@ public final class LokiPublicChatManager: NSObject { if let poller = pollers[threadID] { poller.startIfNeeded() } else { - let poller = GroupChatPoller(for: publicChat) + let poller = LokiPublicChatPoller(for: publicChat) poller.startIfNeeded() pollers[threadID] = poller } @@ -124,7 +124,7 @@ public final class LokiPublicChatManager: NSObject { // Reset the last message cache if let chat = self.chats[threadId] { - LokiPublicChatAPI.resetLastMessageCache(for: chat.channel, on: chat.server) + LokiPublicChatAPI.clearCaches(for: chat.channel, on: chat.server) } // Remove the chat from the db diff --git a/SignalServiceKit/src/Loki/API/Public Chat/PublicChatPoller.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatPoller.swift similarity index 88% rename from SignalServiceKit/src/Loki/API/Public Chat/PublicChatPoller.swift rename to SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatPoller.swift index 5ac65b9f33a..47159fbf8a5 100644 --- a/SignalServiceKit/src/Loki/API/Public Chat/PublicChatPoller.swift +++ b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatPoller.swift @@ -1,7 +1,7 @@ @objc(LKPublicChatPoller) -public final class GroupChatPoller : NSObject { - private let group: LokiPublicChat +public final class LokiPublicChatPoller : NSObject { + private let publicChat: LokiPublicChat private var pollForNewMessagesTimer: Timer? = nil private var pollForDeletedMessagesTimer: Timer? = nil private var pollForModeratorsTimer: Timer? = nil @@ -14,9 +14,9 @@ public final class GroupChatPoller : NSObject { private let pollForModeratorsInterval: TimeInterval = 10 * 60 // MARK: Lifecycle - @objc(initForGroup:) - public init(for group: LokiPublicChat) { - self.group = group + @objc(initForPublicChat:) + public init(for publicChat: LokiPublicChat) { + self.publicChat = publicChat super.init() } @@ -42,7 +42,7 @@ public final class GroupChatPoller : NSObject { // MARK: Polling private func pollForNewMessages() { // Prepare - let group = self.group + let publicChat = self.publicChat let userHexEncodedPublicKey = self.userHexEncodedPublicKey // Processing logic for incoming messages func processIncomingMessage(_ message: LokiPublicChatMessage) { @@ -50,9 +50,9 @@ public final class GroupChatPoller : NSObject { let endIndex = senderHexEncodedPublicKey.endIndex let cutoffIndex = senderHexEncodedPublicKey.index(endIndex, offsetBy: -8) let senderDisplayName = "\(message.displayName) (...\(senderHexEncodedPublicKey[cutoffIndex.. Date: Tue, 15 Oct 2019 10:59:09 +1100 Subject: [PATCH 15/17] Re-order files --- Signal.xcodeproj/project.pbxproj | 32 ++++++++++++------- .../{ => Messaging}/FriendRequestView.swift | 0 .../FriendRequestViewDelegate.swift | 0 .../Loki/{ => Messaging}/LokiP2PServer.swift | 0 .../{ => Messaging}/LokiRSSFeedPoller.swift | 0 .../MentionCandidateSelectionView.swift | 0 ...entionCandidateSelectionViewDelegate.swift | 0 .../{ => Messaging}/NewConversationVC.swift | 0 .../{ => Messaging}/NewPublicChatVC.swift | 0 .../src/Loki/{ => Messaging}/ScanQRCodeVC.h | 0 .../src/Loki/{ => Messaging}/ScanQRCodeVC.m | 0 11 files changed, 20 insertions(+), 12 deletions(-) rename Signal/src/Loki/{ => Messaging}/FriendRequestView.swift (100%) rename Signal/src/Loki/{ => Messaging}/FriendRequestViewDelegate.swift (100%) rename Signal/src/Loki/{ => Messaging}/LokiP2PServer.swift (100%) rename Signal/src/Loki/{ => Messaging}/LokiRSSFeedPoller.swift (100%) rename Signal/src/Loki/{ => Messaging}/MentionCandidateSelectionView.swift (100%) rename Signal/src/Loki/{ => Messaging}/MentionCandidateSelectionViewDelegate.swift (100%) rename Signal/src/Loki/{ => Messaging}/NewConversationVC.swift (100%) rename Signal/src/Loki/{ => Messaging}/NewPublicChatVC.swift (100%) rename Signal/src/Loki/{ => Messaging}/ScanQRCodeVC.h (100%) rename Signal/src/Loki/{ => Messaging}/ScanQRCodeVC.m (100%) diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index e7ef3e96a4f..d0f072ebbfc 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -2647,19 +2647,10 @@ B8439518228510E9000563FE /* Loki */ = { isa = PBXGroup; children = ( + B8BFFF392355426100102A27 /* Messaging */, B86BD0872339A1ED000F5AE3 /* Onboarding */, B86BD08223399ABF000F5AE3 /* Settings */, B86BD0882339A253000F5AE3 /* Utilities */, - B8162F0222891AD600D46544 /* FriendRequestView.swift */, - B8162F0422892C5F00D46544 /* FriendRequestViewDelegate.swift */, - 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */, - B89841E222B7579F00B1BDC6 /* NewConversationVC.swift */, - B825849F2315024B001B41CB /* LokiRSSFeedPoller.swift */, - B8258491230FA5DA001B41CB /* ScanQRCodeVC.h */, - B8258492230FA5E9001B41CB /* ScanQRCodeVC.m */, - 24BD2608234DA2050008EB0A /* NewPublicChatVC.swift */, - B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */, - B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */, ); path = Loki; sourceTree = ""; @@ -2680,9 +2671,9 @@ children = ( B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */, B894D0702339D6F300B4D94D /* DeviceLinkingModalDelegate.swift */, + B894D0742339EDCF00B4D94D /* NukeDataModal.swift */, B86BD08023399883000F5AE3 /* QRCodeModal.swift */, B86BD08523399CEF000F5AE3 /* SeedModal.swift */, - B894D0742339EDCF00B4D94D /* NukeDataModal.swift */, ); path = Settings; sourceTree = ""; @@ -2699,13 +2690,30 @@ B86BD0882339A253000F5AE3 /* Utilities */ = { isa = PBXGroup; children = ( + B84664F4235022F30083A1CD /* MentionUtilities.swift */, B86BD08323399ACF000F5AE3 /* Modal.swift */, B885D5F52334A32100EE0D8E /* UIView+Constraint.swift */, - B84664F4235022F30083A1CD /* MentionUtilities.swift */, ); path = Utilities; sourceTree = ""; }; + B8BFFF392355426100102A27 /* Messaging */ = { + isa = PBXGroup; + children = ( + B8162F0222891AD600D46544 /* FriendRequestView.swift */, + B8162F0422892C5F00D46544 /* FriendRequestViewDelegate.swift */, + 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */, + B825849F2315024B001B41CB /* LokiRSSFeedPoller.swift */, + B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */, + B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */, + B89841E222B7579F00B1BDC6 /* NewConversationVC.swift */, + 24BD2608234DA2050008EB0A /* NewPublicChatVC.swift */, + B8258491230FA5DA001B41CB /* ScanQRCodeVC.h */, + B8258492230FA5E9001B41CB /* ScanQRCodeVC.m */, + ); + path = Messaging; + sourceTree = ""; + }; D221A07E169C9E5E00537ABF = { isa = PBXGroup; children = ( diff --git a/Signal/src/Loki/FriendRequestView.swift b/Signal/src/Loki/Messaging/FriendRequestView.swift similarity index 100% rename from Signal/src/Loki/FriendRequestView.swift rename to Signal/src/Loki/Messaging/FriendRequestView.swift diff --git a/Signal/src/Loki/FriendRequestViewDelegate.swift b/Signal/src/Loki/Messaging/FriendRequestViewDelegate.swift similarity index 100% rename from Signal/src/Loki/FriendRequestViewDelegate.swift rename to Signal/src/Loki/Messaging/FriendRequestViewDelegate.swift diff --git a/Signal/src/Loki/LokiP2PServer.swift b/Signal/src/Loki/Messaging/LokiP2PServer.swift similarity index 100% rename from Signal/src/Loki/LokiP2PServer.swift rename to Signal/src/Loki/Messaging/LokiP2PServer.swift diff --git a/Signal/src/Loki/LokiRSSFeedPoller.swift b/Signal/src/Loki/Messaging/LokiRSSFeedPoller.swift similarity index 100% rename from Signal/src/Loki/LokiRSSFeedPoller.swift rename to Signal/src/Loki/Messaging/LokiRSSFeedPoller.swift diff --git a/Signal/src/Loki/MentionCandidateSelectionView.swift b/Signal/src/Loki/Messaging/MentionCandidateSelectionView.swift similarity index 100% rename from Signal/src/Loki/MentionCandidateSelectionView.swift rename to Signal/src/Loki/Messaging/MentionCandidateSelectionView.swift diff --git a/Signal/src/Loki/MentionCandidateSelectionViewDelegate.swift b/Signal/src/Loki/Messaging/MentionCandidateSelectionViewDelegate.swift similarity index 100% rename from Signal/src/Loki/MentionCandidateSelectionViewDelegate.swift rename to Signal/src/Loki/Messaging/MentionCandidateSelectionViewDelegate.swift diff --git a/Signal/src/Loki/NewConversationVC.swift b/Signal/src/Loki/Messaging/NewConversationVC.swift similarity index 100% rename from Signal/src/Loki/NewConversationVC.swift rename to Signal/src/Loki/Messaging/NewConversationVC.swift diff --git a/Signal/src/Loki/NewPublicChatVC.swift b/Signal/src/Loki/Messaging/NewPublicChatVC.swift similarity index 100% rename from Signal/src/Loki/NewPublicChatVC.swift rename to Signal/src/Loki/Messaging/NewPublicChatVC.swift diff --git a/Signal/src/Loki/ScanQRCodeVC.h b/Signal/src/Loki/Messaging/ScanQRCodeVC.h similarity index 100% rename from Signal/src/Loki/ScanQRCodeVC.h rename to Signal/src/Loki/Messaging/ScanQRCodeVC.h diff --git a/Signal/src/Loki/ScanQRCodeVC.m b/Signal/src/Loki/Messaging/ScanQRCodeVC.m similarity index 100% rename from Signal/src/Loki/ScanQRCodeVC.m rename to Signal/src/Loki/Messaging/ScanQRCodeVC.m From b1b378e18eba1c00f6dbcba6bb5a12222cf94dba Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Tue, 15 Oct 2019 10:59:36 +1100 Subject: [PATCH 16/17] Update Pods --- Pods | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pods b/Pods index 56980ceea7c..1bec50723da 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit 56980ceea7cc0964b92f98831e79fea03e76e801 +Subproject commit 1bec50723dafad9d8932e13d36d4a215341f5ab0 From bb9f59ee17432fb2380f287227befc3b954d7a47 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Tue, 15 Oct 2019 11:03:06 +1100 Subject: [PATCH 17/17] Clean --- .../Public Chat/LokiPublicChatManager.swift | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift index fe67d10edf4..ef260a994fe 100644 --- a/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift +++ b/SignalServiceKit/src/Loki/API/Public Chat/LokiPublicChatManager.swift @@ -1,9 +1,16 @@ import PromiseKit @objc(LKPublicChatManager) -public final class LokiPublicChatManager: NSObject { +public final class LokiPublicChatManager : NSObject { + private let storage = OWSPrimaryStorage.shared() + private var chats: [String:LokiPublicChat] = [:] + private var pollers: [String:LokiPublicChatPoller] = [:] + private var isPolling = false + + private var userHexEncodedPublicKey: String? { + return OWSIdentityManager.shared().identityKeyPair()?.hexEncodedPublicKey + } - // MARK: Error public enum Error : Swift.Error { case chatCreationFailed case userPublicKeyNotFound @@ -11,13 +18,6 @@ public final class LokiPublicChatManager: NSObject { @objc public static let shared = LokiPublicChatManager() - private var chats: [String: LokiPublicChat] = [:] - private var pollers: [String: LokiPublicChatPoller] = [:] - private var isPolling = false - - private let storage = OWSPrimaryStorage.shared() - private var ourHexEncodedPublicKey: String? { return OWSIdentityManager.shared().identityKeyPair()?.hexEncodedPublicKey } - private override init() { super.init() NotificationCenter.default.addObserver(self, selector: #selector(onThreadDeleted(_:)), name: .threadDeleted, object: nil) @@ -48,13 +48,12 @@ public final class LokiPublicChatManager: NSObject { public func addChat(server: String, channel: UInt64) -> Promise { if let existingChat = getChat(server: server, channel: channel) { - if let chat = self.addChat(server: server, channel: channel, name: existingChat.displayName) { - return Promise.value(chat) + if let newChat = self.addChat(server: server, channel: channel, name: existingChat.displayName) { + return Promise.value(newChat) } else { return Promise(error: Error.chatCreationFailed) } } - return LokiPublicChatAPI.getAuthToken(for: server).then { token in return LokiPublicChatAPI.getInfo(for: channel, on: server) }.map { channelInfo -> LokiPublicChat in @@ -67,7 +66,7 @@ public final class LokiPublicChatManager: NSObject { @objc(addChatWithServer:channel:name:) public func addChat(server: String, channel: UInt64, name: String) -> LokiPublicChat? { guard let chat = LokiPublicChat(channel: channel, server: server, displayName: name, isDeletable: true) else { return nil } - let model = TSGroupModel(title: chat.displayName, memberIds: [ourHexEncodedPublicKey!, chat.server], image: nil, groupId: chat.idAsData) + let model = TSGroupModel(title: chat.displayName, memberIds: [userHexEncodedPublicKey!, chat.server], image: nil, groupId: chat.idAsData) // Store the group chat mapping self.storage.dbReadWriteConnection.readWrite { transaction in