commit
b61b440063
1
Podfile
1
Podfile
|
@ -73,6 +73,7 @@ target 'Signal' do
|
|||
|
||||
# Loki
|
||||
pod 'GCDWebServer', '~> 3.0'
|
||||
pod 'FeedKit', '~> 8.1'
|
||||
|
||||
target 'SignalTests' do
|
||||
inherit! :search_paths
|
||||
|
|
10
Podfile.lock
10
Podfile.lock
|
@ -36,6 +36,7 @@ PODS:
|
|||
- Curve25519Kit/Tests (2.1.0):
|
||||
- CocoaLumberjack
|
||||
- SignalCoreKit
|
||||
- FeedKit (8.1.1)
|
||||
- GCDWebServer (3.5.2):
|
||||
- GCDWebServer/Core (= 3.5.2)
|
||||
- GCDWebServer/Core (3.5.2)
|
||||
|
@ -198,6 +199,7 @@ DEPENDENCIES:
|
|||
- CryptoSwift
|
||||
- Curve25519Kit (from `https://github.com/signalapp/Curve25519Kit`)
|
||||
- Curve25519Kit/Tests (from `https://github.com/signalapp/Curve25519Kit`)
|
||||
- FeedKit (~> 8.1)
|
||||
- GCDWebServer (~> 3.0)
|
||||
- GRKOpenSSLFramework (from `https://github.com/signalapp/GRKOpenSSLFramework`)
|
||||
- HKDFKit (from `https://github.com/signalapp/HKDFKit.git`)
|
||||
|
@ -224,6 +226,7 @@ SPEC REPOS:
|
|||
- AFNetworking
|
||||
- CocoaLumberjack
|
||||
- CryptoSwift
|
||||
- FeedKit
|
||||
- GCDWebServer
|
||||
- IGIdenticon
|
||||
- libPhoneNumber-iOS
|
||||
|
@ -297,6 +300,7 @@ SPEC CHECKSUMS:
|
|||
CocoaLumberjack: 2f44e60eb91c176d471fdba43b9e3eae6a721947
|
||||
CryptoSwift: d81eeaa59dc5a8d03720fe919a6fd07b51f7439f
|
||||
Curve25519Kit: b3e77b7152ebe95fee2b3fb6c955449492bc14f7
|
||||
FeedKit: 3418eed25f0b493b205b4de1b8511ac21d413fa9
|
||||
GCDWebServer: ead88cd14596dd4eae4f5830b8877c87c8728990
|
||||
GRKOpenSSLFramework: 8a3735ad41e7dc1daff460467bccd32ca5d6ae3e
|
||||
HKDFKit: 3b6dbbb9d59c221cc6c52c3aa915700cbf24e376
|
||||
|
@ -309,7 +313,7 @@ SPEC CHECKSUMS:
|
|||
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
||||
SignalCoreKit: c2d8132cdedb95d35eb2f8ae7eac0957695d0a8b
|
||||
SignalMetadataKit: 6fa5e9a53c7f104568662521a2f3874672ff7a02
|
||||
SignalServiceKit: 5c5b63a39d5054201ab59ef6daf0fa0a1a0c7887
|
||||
SignalServiceKit: 102576f58e17a5fe3093899adce7e7c192a7bee0
|
||||
SQLCipher: efbdb52cdbe340bcd892b1b14297df4e07241b7f
|
||||
SSZipArchive: 8e859da2520142e09166bc9161967db296e9d02f
|
||||
Starscream: ef3ece99d765eeccb67de105bfa143f929026cf5
|
||||
|
@ -317,6 +321,6 @@ SPEC CHECKSUMS:
|
|||
YapDatabase: b418a4baa6906e8028748938f9159807fd039af4
|
||||
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
|
||||
|
||||
PODFILE CHECKSUM: 10152a1fffafd51206b62fdd8cac86a5de8cf083
|
||||
PODFILE CHECKSUM: 95f41137d4fe8c5b8a27de951b328f8c9531d166
|
||||
|
||||
COCOAPODS: 1.7.2
|
||||
COCOAPODS: 1.5.3
|
||||
|
|
2
Pods
2
Pods
|
@ -1 +1 @@
|
|||
Subproject commit d9ab8b13002bf6ebc932ca4f45df56b577b6a188
|
||||
Subproject commit 20b736ae28ecd42b5fc13f583a010ac9354be507
|
|
@ -3276,12 +3276,13 @@
|
|||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Signal/Pods-Signal-frameworks.sh",
|
||||
"${SRCROOT}/Pods/Target Support Files/Pods-Signal/Pods-Signal-frameworks.sh",
|
||||
"${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/AxolotlKit/AxolotlKit.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/CryptoSwift/CryptoSwift.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Curve25519Kit/Curve25519Kit.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/FeedKit/FeedKit.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/GCDWebServer/GCDWebServer.framework",
|
||||
"${PODS_ROOT}/GRKOpenSSLFramework/OpenSSL-iOS/bin/openssl.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/HKDFKit/HKDFKit.framework",
|
||||
|
@ -3309,6 +3310,7 @@
|
|||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaLumberjack.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptoSwift.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Curve25519Kit.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FeedKit.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GCDWebServer.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HKDFKit.framework",
|
||||
|
@ -3331,7 +3333,7 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Signal/Pods-Signal-frameworks.sh\"\n";
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Signal/Pods-Signal-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
6565655F4068F9E5CDC5687F /* [CP] Check Pods Manifest.lock */ = {
|
||||
|
@ -3358,7 +3360,7 @@
|
|||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-SignalTests/Pods-SignalTests-frameworks.sh",
|
||||
"${SRCROOT}/Pods/Target Support Files/Pods-SignalTests/Pods-SignalTests-frameworks.sh",
|
||||
"${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/AxolotlKit/AxolotlKit.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework",
|
||||
|
@ -3409,7 +3411,7 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SignalTests/Pods-SignalTests-frameworks.sh\"\n";
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SignalTests/Pods-SignalTests-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
F4C416F20E3CB0B25DC10C56 /* [CP] Check Pods Manifest.lock */ = {
|
||||
|
|
|
@ -8,6 +8,7 @@ extern NSString *const AppDelegateStoryboardMain;
|
|||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
- (void)startPublicChatPollingIfNeeded;
|
||||
- (void)createGroupChatsIfNeeded;
|
||||
- (void)startGroupChatPollersIfNeeded;
|
||||
|
||||
@end
|
||||
|
|
|
@ -65,6 +65,8 @@ static NSTimeInterval launchStartedAt;
|
|||
@property (nonatomic) BOOL didAppLaunchFail;
|
||||
@property (nonatomic) LKP2PServer *lokiP2PServer;
|
||||
@property (nonatomic) LKGroupChatPoller *lokiPublicChatPoller;
|
||||
@property (nonatomic) LKGroupChatPoller *lokiNewsPoller;
|
||||
@property (nonatomic) LKGroupChatPoller *lokiMessengerUpdatesPoller;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -1485,34 +1487,60 @@ static NSTimeInterval launchStartedAt;
|
|||
|
||||
#pragma mark - Loki
|
||||
|
||||
- (void)setUpPublicChatIfNeeded
|
||||
- (LKGroupChat *)lokiPublicChat
|
||||
{
|
||||
if (self.lokiPublicChatPoller != nil) { return; }
|
||||
self.lokiPublicChatPoller = [[LKGroupChatPoller alloc] initForGroup:(NSUInteger)LKGroupChatAPI.publicChatID onServer:LKGroupChatAPI.publicChatServer];
|
||||
BOOL isPublicChatSetUp = [NSUserDefaults.standardUserDefaults boolForKey:@"isPublicChatSetUp"];
|
||||
if (isPublicChatSetUp) { return; }
|
||||
NSString *title = NSLocalizedString(@"Loki Public Chat", @"");
|
||||
NSData *groupID = [[[LKGroupChatAPI.publicChatServer stringByAppendingString:@"."] stringByAppendingString:@(LKGroupChatAPI.publicChatID).stringValue] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
TSGroupModel *group = [[TSGroupModel alloc] initWithTitle:title memberIds:@[ OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey, LKGroupChatAPI.publicChatServer ] image:nil groupId:groupID];
|
||||
__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:@"isPublicChatSetUp"];
|
||||
return [[LKGroupChat alloc] initWithKindAsString:@"publicChat" id:@(LKGroupChatAPI.publicChatID).stringValue server:LKGroupChatAPI.publicChatServer displayName:NSLocalizedString(@"Loki Public Chat", @"") isDeletable:true];
|
||||
}
|
||||
|
||||
- (void)startPublicChatPollingIfNeeded
|
||||
- (LKGroupChat *)lokiNews
|
||||
{
|
||||
[self setUpPublicChatIfNeeded];
|
||||
return [[LKGroupChat alloc] initWithKindAsString:@"rss" id:@"loki.network.feed" server:@"https://loki.network/feed/" displayName:NSLocalizedString(@"Loki News", @"") isDeletable:true];
|
||||
}
|
||||
|
||||
- (LKGroupChat *)lokiMessengerUpdates
|
||||
{
|
||||
return [[LKGroupChat alloc] initWithKindAsString:@"rss" id:@"loki.network.messenger-update" server:@"https://loki.network/category/messenger-updates/feed/" displayName:NSLocalizedString(@"Loki Messenger Updates", @"") isDeletable:false];
|
||||
}
|
||||
|
||||
- (void)createGroupChatsIfNeeded
|
||||
{
|
||||
NSArray *allGroupChats = @[ self.lokiPublicChat, self.lokiNews, self.lokiMessengerUpdates ];
|
||||
NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey;
|
||||
for (LKGroupChat *chat in allGroupChats) {
|
||||
NSString *userDefaultsKey = [@"isSetUp." stringByAppendingString:chat.id];
|
||||
BOOL isChatSetUp = [NSUserDefaults.standardUserDefaults boolForKey:userDefaultsKey];
|
||||
if (!isChatSetUp || !chat.isDeletable) {
|
||||
TSGroupModel *group = [[TSGroupModel alloc] initWithTitle:chat.displayName memberIds:@[ userHexEncodedPublicKey, chat.server ] image:nil groupId:[chat.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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createGroupChatPollersIfNeeded
|
||||
{
|
||||
if (self.lokiPublicChatPoller == nil) { self.lokiPublicChatPoller = [[LKGroupChatPoller alloc] initForGroup:self.lokiPublicChat]; }
|
||||
if (self.lokiNewsPoller == nil) { self.lokiNewsPoller = [[LKGroupChatPoller alloc] initForGroup:self.lokiNews]; }
|
||||
if (self.lokiMessengerUpdatesPoller == nil) { self.lokiMessengerUpdatesPoller = [[LKGroupChatPoller alloc] initForGroup:self.lokiMessengerUpdates]; }
|
||||
}
|
||||
|
||||
- (void)startGroupChatPollersIfNeeded
|
||||
{
|
||||
[self createGroupChatPollersIfNeeded];
|
||||
[self.lokiPublicChatPoller startIfNeeded];
|
||||
[self.lokiNewsPoller startIfNeeded];
|
||||
[self.lokiMessengerUpdatesPoller startIfNeeded];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,26 +1,41 @@
|
|||
import FeedKit
|
||||
|
||||
// TODO: Move the RSS feed logic into its own file
|
||||
|
||||
@objc(LKGroupChatPoller)
|
||||
public final class LokiGroupChatPoller : NSObject {
|
||||
private let group: UInt
|
||||
private let server: String
|
||||
private let group: LokiGroupChat
|
||||
private var pollForNewMessagesTimer: Timer? = nil
|
||||
private var pollForDeletedMessagesTimer: Timer? = nil
|
||||
private var hasStarted = false
|
||||
|
||||
private let pollForNewMessagesInterval: TimeInterval = 4
|
||||
private let pollForDeletedMessagesInterval: TimeInterval = 120
|
||||
private lazy var pollForNewMessagesInterval: TimeInterval = {
|
||||
switch group.kind {
|
||||
case .publicChat(_): return 4
|
||||
case .rss(_): return 8 * 60
|
||||
}
|
||||
}()
|
||||
|
||||
@objc(initForGroup:onServer:)
|
||||
public init(for group: UInt, on server: String) {
|
||||
private lazy var pollForDeletedMessagesInterval: TimeInterval = {
|
||||
switch group.kind {
|
||||
case .publicChat(_): return 32 * 60
|
||||
case .rss(_): preconditionFailure()
|
||||
}
|
||||
}()
|
||||
|
||||
@objc(initForGroup:)
|
||||
public init(for group: LokiGroupChat) {
|
||||
self.group = group
|
||||
self.server = server
|
||||
super.init()
|
||||
}
|
||||
|
||||
@objc public func startIfNeeded() {
|
||||
if hasStarted { return }
|
||||
pollForNewMessagesTimer = Timer.scheduledTimer(withTimeInterval: pollForNewMessagesInterval, repeats: true) { [weak self] _ in self?.pollForNewMessages() }
|
||||
pollForDeletedMessagesTimer = Timer.scheduledTimer(withTimeInterval: pollForDeletedMessagesInterval, repeats: true) { [weak self] _ in self?.pollForDeletedMessages() }
|
||||
pollForNewMessages() // Perform initial update
|
||||
if group.isPublicChat {
|
||||
pollForDeletedMessagesTimer = Timer.scheduledTimer(withTimeInterval: pollForDeletedMessagesInterval, repeats: true) { [weak self] _ in self?.pollForDeletedMessages() }
|
||||
}
|
||||
hasStarted = true
|
||||
}
|
||||
|
||||
|
@ -32,28 +47,57 @@ public final class LokiGroupChatPoller : NSObject {
|
|||
|
||||
private func pollForNewMessages() {
|
||||
let group = self.group
|
||||
let server = self.server
|
||||
let _ = LokiGroupChatAPI.getMessages(for: group, on: server).map { messages in
|
||||
messages.reversed().map { message in
|
||||
let id = "\(server).\(group)".data(using: String.Encoding.utf8)!
|
||||
let x1 = SSKProtoGroupContext.builder(id: id, type: .deliver)
|
||||
x1.setName(NSLocalizedString("Loki Public Chat", comment: ""))
|
||||
let x2 = SSKProtoDataMessage.builder()
|
||||
x2.setTimestamp(message.timestamp)
|
||||
x2.setGroup(try! x1.build())
|
||||
x2.setBody(message.body)
|
||||
let x3 = SSKProtoContent.builder()
|
||||
x3.setDataMessage(try! x2.build())
|
||||
let x4 = SSKProtoEnvelope.builder(type: .ciphertext, timestamp: message.timestamp)
|
||||
let senderHexEncodedPublicKey = message.hexEncodedPublicKey
|
||||
let endIndex = senderHexEncodedPublicKey.endIndex
|
||||
let cutoffIndex = senderHexEncodedPublicKey.index(endIndex, offsetBy: -8)
|
||||
let senderDisplayName = "\(message.displayName) (...\(senderHexEncodedPublicKey[cutoffIndex..<endIndex]))"
|
||||
x4.setSource(senderDisplayName)
|
||||
x4.setSourceDevice(OWSDevicePrimaryDeviceId)
|
||||
x4.setContent(try! x3.build().serializedData())
|
||||
OWSPrimaryStorage.shared().dbReadWriteConnection.readWrite { transaction in
|
||||
SSKEnvironment.shared.messageManager.throws_processEnvelope(try! x4.build(), plaintextData: try! x3.build().serializedData(), wasReceivedByUD: false, transaction: transaction)
|
||||
func parseGroupMessage(body: String, timestamp: UInt64, senderDisplayName: String) {
|
||||
let id = group.id.data(using: String.Encoding.utf8)!
|
||||
let x1 = SSKProtoGroupContext.builder(id: id, type: .deliver)
|
||||
x1.setName(group.displayName)
|
||||
let x2 = SSKProtoDataMessage.builder()
|
||||
x2.setTimestamp(timestamp)
|
||||
x2.setGroup(try! x1.build())
|
||||
x2.setBody(body)
|
||||
let x3 = SSKProtoContent.builder()
|
||||
x3.setDataMessage(try! x2.build())
|
||||
let x4 = SSKProtoEnvelope.builder(type: .ciphertext, timestamp: timestamp)
|
||||
x4.setSource(senderDisplayName)
|
||||
x4.setSourceDevice(OWSDevicePrimaryDeviceId)
|
||||
x4.setContent(try! x3.build().serializedData())
|
||||
OWSPrimaryStorage.shared().dbReadWriteConnection.readWrite { transaction in
|
||||
SSKEnvironment.shared.messageManager.throws_processEnvelope(try! x4.build(), plaintextData: try! x3.build().serializedData(), wasReceivedByUD: false, transaction: transaction)
|
||||
}
|
||||
}
|
||||
switch group.kind {
|
||||
case .publicChat(let id):
|
||||
let _ = LokiGroupChatAPI.getMessages(for: id, on: group.server).done { messages in
|
||||
messages.reversed().forEach { message in
|
||||
let senderHexEncodedPublicKey = message.hexEncodedPublicKey
|
||||
let endIndex = senderHexEncodedPublicKey.endIndex
|
||||
let cutoffIndex = senderHexEncodedPublicKey.index(endIndex, offsetBy: -8)
|
||||
let senderDisplayName = "\(message.displayName) (...\(senderHexEncodedPublicKey[cutoffIndex..<endIndex]))"
|
||||
parseGroupMessage(body: message.body, timestamp: message.timestamp, senderDisplayName: senderDisplayName)
|
||||
}
|
||||
}
|
||||
case .rss(_):
|
||||
let url = URL(string: group.server)!
|
||||
FeedParser(URL: url).parseAsync { wrapper in
|
||||
guard case .rss(let feed) = wrapper, let items = feed.items else { return print("[Loki] Failed to parse RSS feed for: \(group.server)") }
|
||||
items.reversed().forEach { item in
|
||||
guard let title = item.title, let description = item.description, let date = item.pubDate else { return }
|
||||
let timestamp = UInt64(date.timeIntervalSince1970 * 1000)
|
||||
let regex = try! NSRegularExpression(pattern: "<a\\s+(?:[^>]*?\\s+)?href=\"([^\"]*)\".*?>(.*?)<.*?\\/a>")
|
||||
var bodyAsHTML = "<b>\(title)</b>\(description)"
|
||||
while true {
|
||||
guard let match = regex.firstMatch(in: bodyAsHTML, options: [], range: NSRange(location: 0, length: bodyAsHTML.utf16.count)) else { break }
|
||||
let matchRange = match.range(at: 0)
|
||||
let urlRange = match.range(at: 1)
|
||||
let descriptionRange = match.range(at: 2)
|
||||
let url = (bodyAsHTML as NSString).substring(with: urlRange)
|
||||
let description = (bodyAsHTML as NSString).substring(with: descriptionRange)
|
||||
bodyAsHTML = (bodyAsHTML as NSString).replacingCharacters(in: matchRange, with: "\(description) (\(url))") as String
|
||||
}
|
||||
guard let bodyAsData = bodyAsHTML.data(using: String.Encoding.unicode) else { return }
|
||||
let options = [ NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html ]
|
||||
guard let body = try? NSAttributedString(data: bodyAsData, options: options, documentAttributes: nil) else { return }
|
||||
parseGroupMessage(body: body.string, timestamp: timestamp, senderDisplayName: NSLocalizedString("Loki", comment: ""))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -692,7 +692,8 @@ typedef NS_ENUM(NSInteger, HomeViewControllerSection) {
|
|||
}
|
||||
if (OWSIdentityManager.sharedManager.identityKeyPair != nil) {
|
||||
AppDelegate *appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate;
|
||||
[appDelegate startPublicChatPollingIfNeeded];
|
||||
[appDelegate createGroupChatsIfNeeded];
|
||||
[appDelegate startGroupChatPollersIfNeeded];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2607,6 +2607,8 @@
|
|||
"Update Required" = "Update Required";
|
||||
"This version of Loki Messenger is no longer supported. Please press OK to reset your account and migrate to the latest version." = "This version of Loki Messenger is no longer supported. Please press OK to reset your account and migrate to the latest version.";
|
||||
"Loki Public Chat" = "Loki Public Chat";
|
||||
"Loki News" = "Loki News";
|
||||
"Loki Messenger Updates" = "Loki Messenger Updates";
|
||||
"Show QR Code" = "Show QR Code";
|
||||
"This is your personal QR code. Other people can scan it to start a secure conversation with you." = "This is your personal QR code. Other people can scan it to start a secure conversation with you.";
|
||||
"Scan a QR Code Instead" = "Scan a QR Code Instead";
|
||||
|
@ -2614,3 +2616,4 @@
|
|||
"You can enable camera access in your device settings." = "You can enable camera access in your device settings.";
|
||||
"Scan the QR code of the person you'd like to securely message. They can find their QR code by going into Loki Messenger's in-app settings and clicking \"Show QR Code\"." = "Scan the QR code of the person you'd like to securely message. They can find their QR code by going into Loki Messenger's in-app settings and clicking \"Show QR Code\".";
|
||||
"Scan QR Code" = "Scan QR Code";
|
||||
"Loki" = "Loki";
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
@objc(LKGroupChat)
|
||||
public final class LokiGroupChat : NSObject {
|
||||
public let kind: Kind
|
||||
@objc public let server: String
|
||||
@objc public let displayName: String
|
||||
@objc public let isDeletable: Bool
|
||||
|
||||
@objc public var id: String {
|
||||
switch kind {
|
||||
case .publicChat(let id): return "\(server).\(id)"
|
||||
case .rss(let customID): return "rss://\(customID)"
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Convenience
|
||||
@objc public var isPublicChat: Bool {
|
||||
if case .publicChat(_) = kind { return true } else { return false }
|
||||
}
|
||||
|
||||
@objc public var isRSS: Bool {
|
||||
if case .rss(_) = kind { return true } else { return false }
|
||||
}
|
||||
|
||||
// MARK: Kind
|
||||
public enum Kind { case publicChat(id: UInt), rss(customID: String) }
|
||||
|
||||
// MARK: Initialization
|
||||
public init(kind: Kind, server: String, displayName: String, isDeletable: Bool) {
|
||||
self.kind = kind
|
||||
self.server = server
|
||||
self.displayName = displayName
|
||||
self.isDeletable = isDeletable
|
||||
}
|
||||
|
||||
@objc public convenience init(kindAsString: String, id: String, server: String, displayName: String, isDeletable: Bool) {
|
||||
let kind: Kind
|
||||
switch kindAsString {
|
||||
case "publicChat": kind = .publicChat(id: UInt(id)!)
|
||||
case "rss": kind = .rss(customID: id)
|
||||
default: preconditionFailure()
|
||||
}
|
||||
self.init(kind: kind, server: server, displayName: displayName, isDeletable: isDeletable)
|
||||
}
|
||||
|
||||
// MARK: Description
|
||||
override public var description: String { return "\(id) (\(displayName))" }
|
||||
}
|
|
@ -169,7 +169,7 @@ public final class LokiGroupChatAPI : NSObject {
|
|||
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? UInt, 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 messages for group chat with ID: \(group) on server: \(server) from: \(rawResponse).")
|
||||
print("[Loki] Couldn't parse message for group chat with ID: \(group) on server: \(server) from: \(rawResponse).")
|
||||
throw Error.messageParsingFailed
|
||||
}
|
||||
let timestamp = UInt64(date.timeIntervalSince1970) * 1000
|
||||
|
@ -197,7 +197,7 @@ public final class LokiGroupChatAPI : NSObject {
|
|||
}
|
||||
return rawMessages.flatMap { message in
|
||||
guard let serverID = message["id"] as? UInt else {
|
||||
print("[Loki] Couldn't parse message for group chat with ID: \(group) on server: \(server) from: \(message).")
|
||||
print("[Loki] Couldn't parse deleted message for group chat with ID: \(group) on server: \(server) from: \(message).")
|
||||
return nil
|
||||
}
|
||||
let isDeleted = (message["is_deleted"] as? Bool ?? false)
|
||||
|
|
Loading…
Reference in New Issue