Merge pull request #44 from loki-project/refactor

Refactoring
This commit is contained in:
gmbnt 2019-08-30 16:05:18 +10:00 committed by GitHub
commit a19315cfeb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 397 additions and 211 deletions

2
Pods

@ -1 +1 @@
Subproject commit 960a820c76a95a64cfb1c6ad721c68f73a8f27b9
Subproject commit 8b30c2d91fe7f9743350dd30521b5ca74e78766c

View file

@ -8,6 +8,7 @@ extern NSString *const AppDelegateStoryboardMain;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
- (void)stopLongPollerIfNeeded;
- (void)createGroupChatsIfNeeded;
- (void)createRSSFeedsIfNeeded;
- (void)startGroupChatPollersIfNeeded;

View file

@ -63,7 +63,10 @@ static NSTimeInterval launchStartedAt;
@property (nonatomic) BOOL hasInitialRootViewController;
@property (nonatomic) BOOL areVersionMigrationsComplete;
@property (nonatomic) BOOL didAppLaunchFail;
// Loki
@property (nonatomic) LKP2PServer *lokiP2PServer;
@property (nonatomic) LKLongPoller *lokiLongPoller;
@property (nonatomic) LKGroupChatPoller *lokiPublicChatPoller;
@property (nonatomic) LKRSSFeedPoller *lokiNewsFeedPoller;
@property (nonatomic) LKRSSFeedPoller *lokiMessengerUpdatesFeedPoller;
@ -175,7 +178,7 @@ static NSTimeInterval launchStartedAt;
[DDLog flushLog];
[LKAPI stopLongPolling];
[self stopLongPollerIfNeeded];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
@ -194,7 +197,8 @@ static NSTimeInterval launchStartedAt;
[DDLog flushLog];
[LKAPI stopLongPolling];
[self stopLongPollerIfNeeded];
if (self.lokiP2PServer) { [self.lokiP2PServer stop]; }
}
@ -761,7 +765,7 @@ static NSTimeInterval launchStartedAt;
[Environment.shared.contactsManager fetchSystemContactsOnceIfAlreadyAuthorized];
// Loki: Start long polling
[LKAPI startLongPollingIfNeeded];
[self startLongPollerIfNeeded];
// Loki: Tell our friends that we are online
[LKP2PAPI broadcastOnlineStatus];
@ -1359,8 +1363,8 @@ static NSTimeInterval launchStartedAt;
// For non-legacy users, read receipts are on by default.
[self.readReceiptManager setAreReadReceiptsEnabled:YES];
// Start long polling
[LKAPI startLongPollingIfNeeded];
// Loki: Start long polling
[self startLongPollerIfNeeded];
}
}
@ -1406,23 +1410,6 @@ static NSTimeInterval launchStartedAt;
[UIViewController attemptRotationToDeviceOrientation];
}
#pragma mark - Long polling
- (void)handleNewMessagesReceived:(NSNotification *)notification
{
NSArray *messages = (NSArray *)notification.userInfo[@"messages"];
NSLog(@"[Loki] Received %lu messages through long polling.", messages.count);
for (SSKProtoEnvelope *envelope in messages) {
NSData *envelopeData = [envelope serializedDataAndReturnError:nil];
if (envelopeData != nil) {
[SSKEnvironment.shared.messageReceiver handleReceivedEnvelopeData:envelopeData];
} else {
OWSFailDebug(@"Failed to deserialize envelope.");
}
}
}
#pragma mark - status bar touches
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
@ -1487,6 +1474,34 @@ static NSTimeInterval launchStartedAt;
#pragma mark - Loki
- (void)setUpLongPollerIfNeeded
{
if (self.lokiLongPoller != nil) { return; }
NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey;
if (userHexEncodedPublicKey == nil) { return; }
self.lokiLongPoller = [[LKLongPoller alloc] initOnMessagesReceived:^(NSArray<SSKProtoEnvelope *> *messages) {
for (SSKProtoEnvelope *message in messages) {
NSData *data = [message serializedDataAndReturnError:nil];
if (data != nil) {
[SSKEnvironment.shared.messageReceiver handleReceivedEnvelopeData:data];
} else {
NSLog(@"[Loki] Failed to deserialize envelope.");
}
}
}];
}
- (void)startLongPollerIfNeeded
{
[self setUpLongPollerIfNeeded];
[self.lokiLongPoller startIfNeeded];
}
- (void)stopLongPollerIfNeeded
{
[self.lokiLongPoller stopIfNeeded];
}
- (LKGroupChat *)lokiPublicChat
{
return [[LKGroupChat alloc] initWithServerID:@(LKGroupChatAPI.publicChatServerID).unsignedIntegerValue server:LKGroupChatAPI.publicChatServer displayName:NSLocalizedString(@"Loki Public Chat", @"") isDeletable:true];

View file

@ -7,7 +7,7 @@ public final class LokiGroupChatPoller : NSObject {
private var hasStarted = false
private let pollForNewMessagesInterval: TimeInterval = 4
private let pollForDeletedMessagesInterval: TimeInterval = 32 * 60
private let pollForDeletedMessagesInterval: TimeInterval = 20
@objc(initForGroup:)
public init(for group: LokiGroupChat) {
@ -44,6 +44,10 @@ public final class LokiGroupChatPoller : NSObject {
x2.setTimestamp(message.timestamp)
x2.setGroup(try! x1.build())
x2.setBody(message.body)
let messageServerID = message.serverID!
let publicChatInfo = SSKProtoPublicChatInfo.builder()
publicChatInfo.setServerID(messageServerID)
x2.setPublicChatInfo(try! publicChatInfo.build())
let x3 = SSKProtoContent.builder()
x3.setDataMessage(try! x2.build())
let x4 = SSKProtoEnvelope.builder(type: .ciphertext, timestamp: message.timestamp)

View file

@ -2,6 +2,7 @@
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "AppDelegate.h"
#import "AppSettingsViewController.h"
#import "AboutTableViewController.h"
#import "AdvancedSettingsTableViewController.h"
@ -533,7 +534,8 @@
[ThreadUtil deleteAllContent];
[SSKEnvironment.shared.identityManager clearIdentityKey];
[LKAPI clearRandomSnodePool];
[LKAPI stopLongPolling];
AppDelegate *appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate;
[appDelegate stopLongPollerIfNeeded];
[SSKEnvironment.shared.tsAccountManager resetForReregistration];
UIViewController *rootViewController = [[OnboardingController new] initialViewController];
OWSNavigationController *navigationController = [[OWSNavigationController alloc] initWithRootViewController:rootViewController];

View file

@ -680,7 +680,8 @@ typedef NS_ENUM(NSInteger, HomeViewControllerSection) {
[ThreadUtil deleteAllContent];
[SSKEnvironment.shared.identityManager clearIdentityKey];
[LKAPI clearRandomSnodePool];
[LKAPI stopLongPolling];
AppDelegate *appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate;
[appDelegate stopLongPollerIfNeeded];
[SSKEnvironment.shared.tsAccountManager resetForReregistration];
UIViewController *rootViewController = [[OnboardingController new] initialViewController];
OWSNavigationController *navigationController = [[OWSNavigationController alloc] initWithRootViewController:rootViewController];

View file

@ -253,6 +253,7 @@ message DataMessage {
repeated Contact contact = 9;
repeated Preview preview = 10;
optional LokiProfile profile = 101; // Loki: The current user's profile
optional PublicChatInfo publicChatInfo = 999; // Loki: Internal public chat info
}
message NullMessage {
@ -421,3 +422,8 @@ message GroupDetails {
optional string color = 7;
optional bool blocked = 8;
}
// Internal - DO NOT SEND
message PublicChatInfo {
optional uint64 serverID = 1;
}

View file

@ -1,49 +0,0 @@
import PromiseKit
internal extension LokiAPI {
private static let receivedMessageHashValuesKey = "receivedMessageHashValuesKey"
private static let receivedMessageHashValuesCollection = "receivedMessageHashValuesCollection"
internal static func getLastMessageHashValue(for target: LokiAPITarget) -> String? {
var result: String? = nil
// Uses a read/write connection because getting the last message hash value also removes expired messages as needed
// TODO: This shouldn't be the case; a getter shouldn't have an unexpected side effect
storage.dbReadWriteConnection.readWrite { transaction in
result = storage.getLastMessageHash(forServiceNode: target.address, transaction: transaction)
}
return result
}
internal static func setLastMessageHashValue(for target: LokiAPITarget, hashValue: String, expirationDate: UInt64) {
storage.dbReadWriteConnection.readWrite { transaction in
storage.setLastMessageHash(forServiceNode: target.address, hash: hashValue, expiresAt: expirationDate, transaction: transaction)
}
}
internal static func getReceivedMessageHashValues() -> Set<String>? {
var result: Set<String>? = nil
storage.dbReadConnection.read { transaction in
result = transaction.object(forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection) as! Set<String>?
}
return result
}
internal static func setReceivedMessageHashValues(to receivedMessageHashValues: Set<String>) {
storage.dbReadWriteConnection.readWrite { transaction in
transaction.setObject(receivedMessageHashValues, forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection)
}
}
}
internal extension Promise {
internal func recoveringNetworkErrorsIfNeeded() -> Promise<T> {
return recover() { error -> Promise<T> in
switch error {
case NetworkManagerError.taskError(_, let underlyingError): throw underlyingError
default: throw error
}
}
}
}

View file

@ -1,116 +0,0 @@
import PromiseKit
private typealias Callback = () -> Void
public extension LokiAPI {
private static var isLongPolling = false
private static var shouldStopPolling = false
private static var usedSnodes = [LokiAPITarget]()
private static var cancels = [Callback]()
/// Start long polling.
/// This will send a notification if new messages were received
@objc public static func startLongPollingIfNeeded() {
guard !isLongPolling else { return }
isLongPolling = true
shouldStopPolling = false
print("[Loki] Started long polling.")
longPoll()
}
/// Stop long polling
@objc public static func stopLongPolling() {
shouldStopPolling = true
isLongPolling = false
usedSnodes.removeAll()
cancelAllPromises()
print("[Loki] Stopped long polling.")
}
/// The long polling loop
private static func longPoll() {
// This is here so we can stop the infinite loop
guard !shouldStopPolling else { return }
getSwarm(for: userHexEncodedPublicKey).then { _ -> Guarantee<[Result<Void>]> in
var promises = [Promise<Void>]()
let connections = 3
for i in 0..<connections {
let (promise, cancel) = openConnection()
promises.append(promise)
cancels.append(cancel)
}
return when(resolved: promises)
}.done { _ in
// Since all promises are complete, we can clear the cancels
cancelAllPromises()
// Keep long polling until it is stopped
longPoll()
}.retainUntilComplete()
}
private static func cancelAllPromises() {
cancels.forEach { cancel in cancel() }
cancels.removeAll()
}
private static func getUnusedSnodes() -> [LokiAPITarget] {
let snodes = LokiAPI.swarmCache[userHexEncodedPublicKey] ?? []
return snodes.filter { !usedSnodes.contains($0) }
}
/// Open a connection to an unused snode and get messages from it
private static func openConnection() -> (Promise<Void>, cancel: Callback) {
var isCancelled = false
let cancel = {
isCancelled = true
}
func connectToNextSnode() -> Promise<Void> {
guard let nextSnode = getUnusedSnodes().first else {
// We don't have anymore unused snodes
return Promise.value(())
}
// Add the snode to the used array
usedSnodes.append(nextSnode)
func getMessagesInfinitely(from target: LokiAPITarget) -> Promise<Void> {
// The only way to exit the infinite loop is to throw an error 3 times or cancel
return getRawMessages(from: target, usingLongPolling: true).then { rawResponse -> Promise<Void> in
// Check if we need to abort
guard !isCancelled else { throw PMKError.cancelled }
// Process the messages
let messages = parseRawMessagesResponse(rawResponse, from: target)
// Send our messages as a notification
NotificationCenter.default.post(name: .newMessagesReceived, object: nil, userInfo: ["messages": messages])
// Continue fetching if we haven't cancelled
return getMessagesInfinitely(from: target)
}.retryingIfNeeded(maxRetryCount: 3)
}
// Keep getting messages for this snode
// If we errored out then connect to the next snode
return getMessagesInfinitely(from: nextSnode).recover { _ -> Promise<Void> in
// Cancelled, so just return successfully
guard !isCancelled else { return Promise.value(()) }
// Connect to the next snode if we haven't cancelled
// We also need to remove the cached snode so we don't contact it again
dropIfNeeded(nextSnode, hexEncodedPublicKey: userHexEncodedPublicKey)
return connectToNextSnode()
}
}
// Keep connecting to snodes
return (connectToNextSnode(), cancel)
}
}

View file

@ -173,4 +173,51 @@ public final class LokiAPI : NSObject {
return envelope
}
}
// MARK: Caching
private static let receivedMessageHashValuesKey = "receivedMessageHashValuesKey"
private static let receivedMessageHashValuesCollection = "receivedMessageHashValuesCollection"
private static func getLastMessageHashValue(for target: LokiAPITarget) -> String? {
var result: String? = nil
// Uses a read/write connection because getting the last message hash value also removes expired messages as needed
// TODO: This shouldn't be the case; a getter shouldn't have an unexpected side effect
storage.dbReadWriteConnection.readWrite { transaction in
result = storage.getLastMessageHash(forServiceNode: target.address, transaction: transaction)
}
return result
}
private static func setLastMessageHashValue(for target: LokiAPITarget, hashValue: String, expirationDate: UInt64) {
storage.dbReadWriteConnection.readWrite { transaction in
storage.setLastMessageHash(forServiceNode: target.address, hash: hashValue, expiresAt: expirationDate, transaction: transaction)
}
}
private static func getReceivedMessageHashValues() -> Set<String>? {
var result: Set<String>? = nil
storage.dbReadConnection.read { transaction in
result = transaction.object(forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection) as! Set<String>?
}
return result
}
private static func setReceivedMessageHashValues(to receivedMessageHashValues: Set<String>) {
storage.dbReadWriteConnection.readWrite { transaction in
transaction.setObject(receivedMessageHashValues, forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection)
}
}
}
// MARK: Error Handling
private extension Promise {
fileprivate func recoveringNetworkErrorsIfNeeded() -> Promise<T> {
return recover() { error -> Promise<T> in
switch error {
case NetworkManagerError.taskError(_, let underlyingError): throw underlyingError
default: throw error
}
}
}
}

View file

@ -28,13 +28,13 @@ public final class LokiGroupChatAPI : NSObject {
// MARK: Error
public enum Error : Swift.Error {
case tokenParsingFailed, tokenDecryptionFailed, messageParsingFailed
case tokenParsingFailed, tokenDecryptionFailed, messageParsingFailed, deletionParsingFailed
}
// MARK: Database
private static let authTokenCollection = "LokiGroupChatAuthTokenCollection"
private static let lastMessageServerIDCollection = "LokiGroupChatLastMessageServerIDCollection"
private static let firstMessageServerIDCollection = "LokiGroupChatFirstMessageServerIDCollection"
private static let lastDeletionServerIDCollection = "LokiGroupChatLastDeletionServerIDCollection"
private static func getAuthTokenFromDatabase(for server: String) -> String? {
var result: String? = nil
@ -64,17 +64,17 @@ public final class LokiGroupChatAPI : NSObject {
}
}
private static func getFirstMessageServerID(for group: UInt64, on server: String) -> UInt? {
private static func getLastDeletionServerID(for group: UInt64, on server: String) -> UInt? {
var result: UInt? = nil
storage.dbReadConnection.read { transaction in
result = transaction.object(forKey: "\(server).\(group)", inCollection: firstMessageServerIDCollection) as! UInt?
result = transaction.object(forKey: "\(server).\(group)", inCollection: lastDeletionServerIDCollection) as! UInt?
}
return result
}
private static func setFirstMessageServerID(for group: UInt64, on server: String, to newValue: UInt64) {
private static func setLastDeletionServerID(for group: UInt64, on server: String, to newValue: UInt64) {
storage.dbReadWriteConnection.readWrite { transaction in
transaction.setObject(newValue, forKey: "\(server).\(group)", inCollection: firstMessageServerIDCollection)
transaction.setObject(newValue, forKey: "\(server).\(group)", inCollection: lastDeletionServerIDCollection)
}
}
@ -147,9 +147,7 @@ public final class LokiGroupChatAPI : NSObject {
}
guard hexEncodedPublicKey != userHexEncodedPublicKey else { return nil }
let lastMessageServerID = getLastMessageServerID(for: group, on: server)
let firstMessageServerID = getFirstMessageServerID(for: group, on: server)
if serverID > (lastMessageServerID ?? 0) { setLastMessageServerID(for: group, on: server, to: serverID) }
if serverID < (firstMessageServerID ?? UInt.max) { setFirstMessageServerID(for: group, on: server, to: serverID) }
return LokiGroupMessage(serverID: serverID, hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName, body: body, type: publicChatMessageType, timestamp: timestamp)
}
}
@ -186,22 +184,27 @@ public final class LokiGroupChatAPI : NSObject {
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).")
let firstMessageServerID = getFirstMessageServerID(for: group, on: server) ?? 0
let queryParameters = "is_deleted=true&since_id=\(firstMessageServerID)"
let url = URL(string: "\(server)/channels/\(group)/messages?\(queryParameters)")!
let queryParameters: String
if let lastDeletionServerID = getLastDeletionServerID(for: group, on: server) {
queryParameters = "since_id=\(lastDeletionServerID)"
} else {
queryParameters = "count=\(fallbackBatchCount)"
}
let url = URL(string: "\(server)/loki/v1/channel/\(group)/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 rawMessages = json["data"] as? [JSON] else {
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).")
throw Error.messageParsingFailed
throw Error.deletionParsingFailed
}
return rawMessages.flatMap { message in
guard let serverID = message["id"] as? UInt64 else {
print("[Loki] Couldn't parse deleted message for group chat with ID: \(group) on server: \(server) from: \(message).")
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).")
return nil
}
let isDeleted = (message["is_deleted"] as? Bool ?? false)
return isDeleted ? serverID : nil
let lastDeletionServerID = getLastDeletionServerID(for: group, on: server)
if serverID > (lastDeletionServerID ?? 0) { setLastDeletionServerID(for: group, on: server, to: serverID) }
return messageServerID
}
}
}

View file

@ -0,0 +1,90 @@
import PromiseKit
@objc(LKLongPoller)
public final class LokiLongPoller : NSObject {
private let onMessagesReceived: ([SSKProtoEnvelope]) -> Void
private let storage = OWSPrimaryStorage.shared()
private var hasStarted = false
private var hasStopped = false
private var connections = Set<Promise<Void>>()
private var usedSnodes = Set<LokiAPITarget>()
// MARK: Settings
private let connectionCount = 3
private let retryInterval: TimeInterval = 4
// MARK: Convenience
private var userHexEncodedPublicKey: String { return OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey }
// MARK: Initialization
@objc public init(onMessagesReceived: @escaping ([SSKProtoEnvelope]) -> Void) {
self.onMessagesReceived = onMessagesReceived
super.init()
}
// MARK: Public API
@objc public func startIfNeeded() {
guard !hasStarted else { return }
print("[Loki] Started long polling.")
hasStarted = true
hasStopped = false
openConnections()
}
@objc public func stopIfNeeded() {
guard !hasStopped else { return }
print("[Loki] Stopped long polling.")
hasStarted = false
hasStopped = true
usedSnodes.removeAll()
}
// MARK: Private API
private func openConnections() {
guard !hasStopped else { return }
LokiAPI.getSwarm(for: userHexEncodedPublicKey).then { [weak self] _ -> Guarantee<[Result<Void>]> in
guard let strongSelf = self else { return Guarantee.value([Result<Void>]()) }
strongSelf.usedSnodes.removeAll()
let connections: [Promise<Void>] = (0..<strongSelf.connectionCount).map { _ in
let (promise, seal) = Promise<Void>.pending()
strongSelf.openConnectionToNextSnode(seal: seal)
return promise
}
strongSelf.connections = Set(connections)
return when(resolved: connections)
}.ensure { [weak self] in
guard let strongSelf = self else { return }
Timer.scheduledTimer(withTimeInterval: strongSelf.retryInterval, repeats: false) { _ in
guard let strongSelf = self else { return }
strongSelf.openConnections()
}
}
}
private func openConnectionToNextSnode(seal: Resolver<Void>) {
let swarm = LokiAPI.swarmCache[userHexEncodedPublicKey] ?? []
let userHexEncodedPublicKey = self.userHexEncodedPublicKey
let unusedSnodes = Set(swarm).subtracting(usedSnodes)
if !unusedSnodes.isEmpty {
let nextSnode = unusedSnodes.randomElement()!
usedSnodes.insert(nextSnode)
print("[Loki] Opening long polling connection to \(nextSnode).")
longPoll(nextSnode, seal: seal).catch { [weak self] error in
print("[Loki] Long polling connection to \(nextSnode) failed; dropping it and switching to next snode.")
LokiAPI.dropIfNeeded(nextSnode, hexEncodedPublicKey: userHexEncodedPublicKey)
self?.openConnectionToNextSnode(seal: seal)
}
} else {
seal.fulfill(())
}
}
private func longPoll(_ target: LokiAPITarget, seal: Resolver<Void>) -> Promise<Void> {
return LokiAPI.getRawMessages(from: target, usingLongPolling: true).then { [weak self] rawResponse -> Promise<Void> in
guard let strongSelf = self, !strongSelf.hasStopped else { return Promise.value(()) }
let messages = LokiAPI.parseRawMessagesResponse(rawResponse, from: target)
strongSelf.onMessagesReceived(messages)
return strongSelf.longPoll(target, seal: seal)
}
}
}

View file

@ -1401,6 +1401,11 @@ NS_ASSUME_NONNULL_BEGIN
thread:oldGroupThread
envelope:envelope
transaction:transaction];
if (dataMessage.publicChatInfo != nil && dataMessage.publicChatInfo.hasServerID) {
[self.primaryStorage setIDForMessageWithServerID:dataMessage.publicChatInfo.serverID to:incomingMessage.uniqueId in:transaction];
}
return incomingMessage;
}
default: {

View file

@ -3308,6 +3308,9 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
if let _value = profile {
builder.setProfile(_value)
}
if let _value = publicChatInfo {
builder.setPublicChatInfo(_value)
}
return builder
}
@ -3379,6 +3382,10 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
proto.profile = valueParam.proto
}
@objc public func setPublicChatInfo(_ valueParam: SSKProtoPublicChatInfo) {
proto.publicChatInfo = valueParam.proto
}
@objc public func build() throws -> SSKProtoDataMessage {
return try SSKProtoDataMessage.parseProto(proto)
}
@ -3402,6 +3409,8 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
@objc public let profile: SSKProtoDataMessageLokiProfile?
@objc public let publicChatInfo: SSKProtoPublicChatInfo?
@objc public var body: String? {
guard proto.hasBody else {
return nil
@ -3449,7 +3458,8 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
quote: SSKProtoDataMessageQuote?,
contact: [SSKProtoDataMessageContact],
preview: [SSKProtoDataMessagePreview],
profile: SSKProtoDataMessageLokiProfile?) {
profile: SSKProtoDataMessageLokiProfile?,
publicChatInfo: SSKProtoPublicChatInfo?) {
self.proto = proto
self.attachments = attachments
self.group = group
@ -3457,6 +3467,7 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
self.contact = contact
self.preview = preview
self.profile = profile
self.publicChatInfo = publicChatInfo
}
@objc
@ -3494,6 +3505,11 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
profile = try SSKProtoDataMessageLokiProfile.parseProto(proto.profile)
}
var publicChatInfo: SSKProtoPublicChatInfo? = nil
if proto.hasPublicChatInfo {
publicChatInfo = try SSKProtoPublicChatInfo.parseProto(proto.publicChatInfo)
}
// MARK: - Begin Validation Logic for SSKProtoDataMessage -
// MARK: - End Validation Logic for SSKProtoDataMessage -
@ -3504,7 +3520,8 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder {
quote: quote,
contact: contact,
preview: preview,
profile: profile)
profile: profile,
publicChatInfo: publicChatInfo)
return result
}
@ -6215,3 +6232,94 @@ extension SSKProtoGroupDetails.SSKProtoGroupDetailsBuilder {
}
#endif
// MARK: - SSKProtoPublicChatInfo
@objc public class SSKProtoPublicChatInfo: NSObject {
// MARK: - SSKProtoPublicChatInfoBuilder
@objc public class func builder() -> SSKProtoPublicChatInfoBuilder {
return SSKProtoPublicChatInfoBuilder()
}
// asBuilder() constructs a builder that reflects the proto's contents.
@objc public func asBuilder() -> SSKProtoPublicChatInfoBuilder {
let builder = SSKProtoPublicChatInfoBuilder()
if hasServerID {
builder.setServerID(serverID)
}
return builder
}
@objc public class SSKProtoPublicChatInfoBuilder: NSObject {
private var proto = SignalServiceProtos_PublicChatInfo()
@objc fileprivate override init() {}
@objc public func setServerID(_ valueParam: UInt64) {
proto.serverID = valueParam
}
@objc public func build() throws -> SSKProtoPublicChatInfo {
return try SSKProtoPublicChatInfo.parseProto(proto)
}
@objc public func buildSerializedData() throws -> Data {
return try SSKProtoPublicChatInfo.parseProto(proto).serializedData()
}
}
fileprivate let proto: SignalServiceProtos_PublicChatInfo
@objc public var serverID: UInt64 {
return proto.serverID
}
@objc public var hasServerID: Bool {
return proto.hasServerID
}
private init(proto: SignalServiceProtos_PublicChatInfo) {
self.proto = proto
}
@objc
public func serializedData() throws -> Data {
return try self.proto.serializedData()
}
@objc public class func parseData(_ serializedData: Data) throws -> SSKProtoPublicChatInfo {
let proto = try SignalServiceProtos_PublicChatInfo(serializedData: serializedData)
return try parseProto(proto)
}
fileprivate class func parseProto(_ proto: SignalServiceProtos_PublicChatInfo) throws -> SSKProtoPublicChatInfo {
// MARK: - Begin Validation Logic for SSKProtoPublicChatInfo -
// MARK: - End Validation Logic for SSKProtoPublicChatInfo -
let result = SSKProtoPublicChatInfo(proto: proto)
return result
}
@objc public override var debugDescription: String {
return "\(proto)"
}
}
#if DEBUG
extension SSKProtoPublicChatInfo {
@objc public func serializedDataIgnoringErrors() -> Data? {
return try! self.serializedData()
}
}
extension SSKProtoPublicChatInfo.SSKProtoPublicChatInfoBuilder {
@objc public func buildIgnoringErrors() -> SSKProtoPublicChatInfo? {
return try! self.build()
}
}
#endif

View file

@ -802,6 +802,16 @@ struct SignalServiceProtos_DataMessage {
/// Clears the value of `profile`. Subsequent reads from it will return its default value.
mutating func clearProfile() {_uniqueStorage()._profile = nil}
/// Loki: Internal public chat info
var publicChatInfo: SignalServiceProtos_PublicChatInfo {
get {return _storage._publicChatInfo ?? SignalServiceProtos_PublicChatInfo()}
set {_uniqueStorage()._publicChatInfo = newValue}
}
/// Returns true if `publicChatInfo` has been explicitly set.
var hasPublicChatInfo: Bool {return _storage._publicChatInfo != nil}
/// Clears the value of `publicChatInfo`. Subsequent reads from it will return its default value.
mutating func clearPublicChatInfo() {_uniqueStorage()._publicChatInfo = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
enum Flags: SwiftProtobuf.Enum {
@ -2492,6 +2502,28 @@ struct SignalServiceProtos_GroupDetails {
fileprivate var _storage = _StorageClass.defaultInstance
}
/// Internal - DO NOT SEND
struct SignalServiceProtos_PublicChatInfo {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var serverID: UInt64 {
get {return _serverID ?? 0}
set {_serverID = newValue}
}
/// Returns true if `serverID` has been explicitly set.
var hasServerID: Bool {return self._serverID != nil}
/// Clears the value of `serverID`. Subsequent reads from it will return its default value.
mutating func clearServerID() {self._serverID = nil}
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _serverID: UInt64? = nil
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "SignalServiceProtos"
@ -3146,6 +3178,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf.
9: .same(proto: "contact"),
10: .same(proto: "preview"),
101: .same(proto: "profile"),
999: .same(proto: "publicChatInfo"),
]
fileprivate class _StorageClass {
@ -3160,6 +3193,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf.
var _contact: [SignalServiceProtos_DataMessage.Contact] = []
var _preview: [SignalServiceProtos_DataMessage.Preview] = []
var _profile: SignalServiceProtos_DataMessage.LokiProfile? = nil
var _publicChatInfo: SignalServiceProtos_PublicChatInfo? = nil
static let defaultInstance = _StorageClass()
@ -3177,6 +3211,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf.
_contact = source._contact
_preview = source._preview
_profile = source._profile
_publicChatInfo = source._publicChatInfo
}
}
@ -3203,6 +3238,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf.
case 9: try decoder.decodeRepeatedMessageField(value: &_storage._contact)
case 10: try decoder.decodeRepeatedMessageField(value: &_storage._preview)
case 101: try decoder.decodeSingularMessageField(value: &_storage._profile)
case 999: try decoder.decodeSingularMessageField(value: &_storage._publicChatInfo)
default: break
}
}
@ -3244,6 +3280,9 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf.
if let v = _storage._profile {
try visitor.visitSingularMessageField(value: v, fieldNumber: 101)
}
if let v = _storage._publicChatInfo {
try visitor.visitSingularMessageField(value: v, fieldNumber: 999)
}
}
try unknownFields.traverse(visitor: &visitor)
}
@ -3264,6 +3303,7 @@ extension SignalServiceProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf.
if _storage._contact != rhs_storage._contact {return false}
if _storage._preview != rhs_storage._preview {return false}
if _storage._profile != rhs_storage._profile {return false}
if _storage._publicChatInfo != rhs_storage._publicChatInfo {return false}
return true
}
if !storagesAreEqual {return false}
@ -5113,3 +5153,32 @@ extension SignalServiceProtos_GroupDetails.Avatar: SwiftProtobuf.Message, SwiftP
return true
}
}
extension SignalServiceProtos_PublicChatInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".PublicChatInfo"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "serverID"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
switch fieldNumber {
case 1: try decoder.decodeSingularUInt64Field(value: &self._serverID)
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if let v = self._serverID {
try visitor.visitSingularUInt64Field(value: v, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: SignalServiceProtos_PublicChatInfo, rhs: SignalServiceProtos_PublicChatInfo) -> Bool {
if lhs._serverID != rhs._serverID {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}