Added PublicChatManager.

Added migrations.
pull/57/head
Mikunj 5 years ago
parent 683a5c1edc
commit d8d33287d1

@ -1 +1 @@
Subproject commit a6c8eadf5ecff074bc7d07f4cd02b7054e3d2b65
Subproject commit 68a1e49959447a8ef4b4de77edec53375c598268

@ -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 = "<group>"; };
B8258492230FA5E9001B41CB /* ScanQRCodeVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScanQRCodeVC.m; sourceTree = "<group>"; };
B825849F2315024B001B41CB /* RSSFeedPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSSFeedPoller.swift; sourceTree = "<group>"; };
B845B4D3230CD09000D759F0 /* GroupChatPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupChatPoller.swift; sourceTree = "<group>"; };
B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Identicon+ObjC.swift"; sourceTree = "<group>"; };
B86BD08023399883000F5AE3 /* QRCodeModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeModal.swift; sourceTree = "<group>"; };
B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = "<group>"; };
@ -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 */,

@ -69,7 +69,6 @@ static NSTimeInterval launchStartedAt;
// 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 @@ static NSTimeInterval launchStartedAt;
[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 @@ static NSTimeInterval launchStartedAt;
- (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 @@ static NSTimeInterval launchStartedAt;
}
}
- (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 @@ static NSTimeInterval launchStartedAt;
- (void)startGroupChatPollersIfNeeded
{
[self createGroupChatPollersIfNeeded];
if (self.lokiPublicChatPoller != nil) { [self.lokiPublicChatPoller startIfNeeded]; }
[LKPublicChatManager.shared startPollersIfNeeded];
}
- (void)startRSSFeedPollersIfNeeded
@ -1635,10 +1609,6 @@ static NSTimeInterval launchStartedAt;
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;

@ -13,6 +13,7 @@
#import <SignalMessaging/UIView+OWS.h>
#import <SignalServiceKit/TSAttachmentStream.h>
#import <SignalServiceKit/TSMessage.h>
#import <SignalServiceKit/SignalServiceKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
@ -553,10 +554,12 @@ const CGFloat kRemotelySourcedContentRowSpacing = 3;
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; }
}
}];
}

@ -2598,6 +2598,7 @@ typedef enum : NSUInteger {
__block OWSQuotedReplyModel *quotedReply;
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
quotedReply = [OWSQuotedReplyModel quotedReplyForSendingWithConversationViewItem:conversationItem
threadId:conversationItem.interaction.uniqueThreadId
transaction:transaction];
}];

@ -620,7 +620,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
// 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 =

@ -2016,7 +2016,7 @@ NS_ASSUME_NONNULL_BEGIN
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 @@ NS_ASSUME_NONNULL_BEGIN
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);

@ -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<ConversationViewItem>)conversationItem
threadId:(NSString *)threadId
transaction:(YapDatabaseReadTransaction *)transaction;
- (TSQuotedMessage *)buildQuotedMessageForSending;
// Loki
@property (nonatomic, readonly) NSString *threadId;
@end

@ -31,7 +31,8 @@ NS_ASSUME_NONNULL_BEGIN
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 @@ NS_ASSUME_NONNULL_BEGIN
attachmentStream:(nullable TSAttachmentStream *)attachmentStream
thumbnailAttachmentPointer:(nullable TSAttachmentPointer *)thumbnailAttachmentPointer
thumbnailDownloadFailed:(BOOL)thumbnailDownloadFailed
threadId:(NSString *)threadId
{
self = [super init];
if (!self) {
@ -66,6 +68,7 @@ NS_ASSUME_NONNULL_BEGIN
_attachmentStream = attachmentStream;
_thumbnailAttachmentPointer = thumbnailAttachmentPointer;
_thumbnailDownloadFailed = thumbnailDownloadFailed;
_threadId = threadId;
return self;
}
@ -73,6 +76,7 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - Factory Methods
+ (instancetype)quotedReplyWithQuotedMessage:(TSQuotedMessage *)quotedMessage
threadId:(NSString *)threadId
transaction:(YapDatabaseReadTransaction *)transaction
{
OWSAssertDebug(quotedMessage.quotedAttachments.count <= 1);
@ -112,10 +116,12 @@ NS_ASSUME_NONNULL_BEGIN
sourceFilename:attachmentInfo.sourceFilename
attachmentStream:nil
thumbnailAttachmentPointer:attachmentPointer
thumbnailDownloadFailed:thumbnailDownloadFailed];
thumbnailDownloadFailed:thumbnailDownloadFailed
threadId:threadId];
}
+ (nullable instancetype)quotedReplyForSendingWithConversationViewItem:(id<ConversationViewItem>)conversationItem
threadId:(NSString *)threadId
transaction:(YapDatabaseReadTransaction *)transaction;
{
OWSAssertDebug(conversationItem);
@ -160,7 +166,8 @@ NS_ASSUME_NONNULL_BEGIN
sourceFilename:nil
attachmentStream:nil
thumbnailAttachmentPointer:nil
thumbnailDownloadFailed:NO];
thumbnailDownloadFailed:NO
threadId:@""];
}
NSString *_Nullable quotedText = message.body;
@ -237,7 +244,8 @@ NS_ASSUME_NONNULL_BEGIN
sourceFilename:quotedAttachment.sourceFilename
attachmentStream:quotedAttachment
thumbnailAttachmentPointer:nil
thumbnailDownloadFailed:NO];
thumbnailDownloadFailed:NO
threadId:threadId];
}
#pragma mark - Instance Methods

@ -93,6 +93,11 @@ NS_ASSUME_NONNULL_BEGIN
if ([self isVersion:previousVersion atLeast:@"2.0.0" andLessThan:@"2.3.0"] && [self.tsAccountManager isRegistered]) {
[self clearBloomFilterCache];
}
// Loki
if ([self isVersion:previousVersion lessThan:@"1.2.1"] && [self.tsAccountManager isRegistered]) {
[self updatePublicChatMapping];
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[[OWSDatabaseMigrationRunner alloc] init] runAllOutstandingWithCompletion:completion];
@ -162,6 +167,21 @@ NS_ASSUME_NONNULL_BEGIN
}
}
# 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
{
[OWSPrimaryStorage.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) {
for (LKGroupChat *chat in LKGroupChat.defaultChats) {
TSGroupThread *thread = [TSGroupThread threadWithGroupId:chat.idAsData transaction:transaction];
if (thread != nil) {
[LKDatabaseUtilities setGroupChat:chat threadID:thread.uniqueId transaction:transaction];
}
}
}];
}
@end
NS_ASSUME_NONNULL_END

@ -535,14 +535,16 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
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();
})
.catchOn(dispatch_get_main_queue(), ^(NSError *error) {
failureBlock(error);
}) retainUntilComplete];
__block NSDictionary *chats;
[SSKEnvironment.shared.primaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
chats = [LKDatabaseUtilities getAllGroupChats:transaction];
}];
for (LKGroupChat *chat in chats.allValues) {
[[LKGroupChatAPI setDisplayName:localProfileName on:chat.server] retainUntilComplete];
}
successBlock();
}
- (void)fetchLocalUsersProfile

@ -0,0 +1,3 @@
public struct LokiPublicChannel {
public let name: String
}

@ -1,10 +1,20 @@
@objc(LKGroupChat)
public final class LokiGroupChat : NSObject, NSCoding {
@objc public var id: String {
return "\(server).\(channel)"
@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 var id: String { return "\(server).\(channel)" }
@objc public var idAsData: Data? { return id.data(using: .utf8) }
@objc public let channel: UInt64
@objc public let server: String
@objc public let displayName: String

@ -9,13 +9,8 @@ public final class LokiGroupChatAPI : LokiDotNetAPI {
private static let maxRetryCount: UInt = 8
// MARK: Public Chat
#if DEBUG
@objc public static let publicChatServer = "https://chat-dev.lokinet.org"
#else
@objc public static let publicChatServer = "https://chat.lokinet.org"
#endif
@objc public static let publicChatMessageType = "network.loki.messenger.publicChat"
@objc public static let publicChatServerID: UInt64 = 1
@objc private static let channelInfoType = "net.patter-app.settings"
// MARK: Convenience
private static var userDisplayName: String {
@ -209,13 +204,30 @@ 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).map { _ in }.recover { error in
return TSNetworkManager.shared().makePromise(request: request).retryingIfNeeded(maxRetryCount: 3).map { _ in }.recover { error in
print("Couldn't update display name due to error: \(error).")
throw error
}
}
}
public static func getChannelInfo(_ channel: UInt64, on server: String) -> Promise<LokiPublicChannel> {
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 {

@ -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..<endIndex]))"
let id = group.id.data(using: String.Encoding.utf8)!
let id = group.idAsData!
let groupContext = SSKProtoGroupContext.builder(id: id, type: .deliver)
groupContext.setName(group.displayName)
let dataMessage = SSKProtoDataMessage.builder()
@ -89,7 +89,7 @@ public final class GroupChatPoller : NSObject {
isDuplicate = id != nil
}
guard !isDuplicate else { return }
guard let groupID = group.id.data(using: .utf8) else { return }
guard let groupID = group.idAsData else { return }
let thread = TSGroupThread.getOrCreateThread(withGroupId: groupID)
let signalQuote: TSQuotedMessage?
if let quote = message.quote {

@ -0,0 +1,121 @@
import PromiseKit
@objc(LKPublicChatManager)
public final class LokiPublicChatManager: NSObject {
// MARK: Error
public enum Error : Swift.Error {
case userPublicKeyNotFound
}
@objc public static let shared = LokiPublicChatManager()
private var chats: [String: LokiGroupChat] = [:]
private var pollers: [String: LokiGroupChatPoller] = [:]
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)
refreshChatsAndPollers()
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc public func startPollersIfNeeded() {
for (threadID, groupChat) in chats {
if let poller = pollers[threadID] {
poller.startIfNeeded()
} else {
let poller = LokiGroupChatPoller(for: groupChat)
poller.startIfNeeded()
pollers[threadID] = poller
}
}
isPolling = true
}
@objc public func stopPollers() {
for poller in pollers.values { poller.stop() }
isPolling = false
}
public func addChat(server: String, channel: UInt64) -> Promise<LokiGroupChat> {
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()
}
}

@ -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:)

@ -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 {

@ -503,7 +503,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
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;

Loading…
Cancel
Save