Merge branch 'dev' of https://github.com/loki-project/session-ios into PN-with-preview
This commit is contained in:
commit
0e604c1291
2
Pods
2
Pods
|
@ -1 +1 @@
|
|||
Subproject commit f636079cbdf8e7a0b959f25917b1bcc0b64b0f0c
|
||||
Subproject commit 7d2822b86d1ff3bf09eb6cab2f01a03e7e214081
|
|
@ -8,8 +8,8 @@ extern NSString *const AppDelegateStoryboardMain;
|
|||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
- (void)startLongPollerIfNeeded;
|
||||
- (void)stopLongPollerIfNeeded;
|
||||
- (void)startPollerIfNeeded;
|
||||
- (void)stopPollerIfNeeded;
|
||||
- (void)setUpDefaultPublicChatsIfNeeded;
|
||||
- (void)startOpenGroupPollersIfNeeded;
|
||||
- (void)stopOpenGroupPollersIfNeeded;
|
||||
|
|
|
@ -69,7 +69,7 @@ static BOOL isInternalTestVersion = NO;
|
|||
|
||||
// Loki
|
||||
@property (nonatomic) LKP2PServer *lokiP2PServer;
|
||||
@property (nonatomic) LKLongPoller *lokiLongPoller;
|
||||
@property (nonatomic) LKPoller *lokiPoller;
|
||||
@property (nonatomic) LKRSSFeedPoller *lokiNewsFeedPoller;
|
||||
@property (nonatomic) LKRSSFeedPoller *lokiMessengerUpdatesFeedPoller;
|
||||
|
||||
|
@ -181,7 +181,7 @@ static BOOL isInternalTestVersion = NO;
|
|||
[DDLog flushLog];
|
||||
|
||||
// Loki: Stop pollers
|
||||
[self stopLongPollerIfNeeded];
|
||||
[self stopPollerIfNeeded];
|
||||
[self stopOpenGroupPollersIfNeeded];
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ static BOOL isInternalTestVersion = NO;
|
|||
[DDLog flushLog];
|
||||
|
||||
// Loki: Stop pollers
|
||||
[self stopLongPollerIfNeeded];
|
||||
[self stopPollerIfNeeded];
|
||||
[self stopOpenGroupPollersIfNeeded];
|
||||
|
||||
if (self.lokiP2PServer) { [self.lokiP2PServer stop]; }
|
||||
|
@ -784,7 +784,7 @@ static BOOL isInternalTestVersion = NO;
|
|||
[LKP2PAPI broadcastOnlineStatus];
|
||||
|
||||
// Loki: Start pollers
|
||||
[self startLongPollerIfNeeded];
|
||||
[self startPollerIfNeeded];
|
||||
[self startOpenGroupPollersIfNeeded];
|
||||
|
||||
// Loki: Get device links
|
||||
|
@ -1477,7 +1477,7 @@ static BOOL isInternalTestVersion = NO;
|
|||
[self.lokiFriendRequestExpirationJob startIfNecessary];
|
||||
|
||||
// Loki: Start pollers
|
||||
[self startLongPollerIfNeeded];
|
||||
[self startPollerIfNeeded];
|
||||
[self startOpenGroupPollersIfNeeded];
|
||||
|
||||
// Loki: Get device links
|
||||
|
@ -1591,12 +1591,12 @@ static BOOL isInternalTestVersion = NO;
|
|||
|
||||
#pragma mark - Loki
|
||||
|
||||
- (void)setUpLongPollerIfNeeded
|
||||
- (void)setUpPollerIfNeeded
|
||||
{
|
||||
if (self.lokiLongPoller != nil) { return; }
|
||||
if (self.lokiPoller != nil) { return; }
|
||||
NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey;
|
||||
if (userHexEncodedPublicKey == nil) { return; }
|
||||
self.lokiLongPoller = [[LKLongPoller alloc] initOnMessagesReceived:^(NSArray<SSKProtoEnvelope *> *messages) {
|
||||
self.lokiPoller = [[LKPoller alloc] initOnMessagesReceived:^(NSArray<SSKProtoEnvelope *> *messages) {
|
||||
for (SSKProtoEnvelope *message in messages) {
|
||||
NSData *data = [message serializedDataAndReturnError:nil];
|
||||
if (data != nil) {
|
||||
|
@ -1608,15 +1608,15 @@ static BOOL isInternalTestVersion = NO;
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)startLongPollerIfNeeded
|
||||
- (void)startPollerIfNeeded
|
||||
{
|
||||
[self setUpLongPollerIfNeeded];
|
||||
[self.lokiLongPoller startIfNeeded];
|
||||
[self setUpPollerIfNeeded];
|
||||
[self.lokiPoller startIfNeeded];
|
||||
}
|
||||
|
||||
- (void)stopLongPollerIfNeeded
|
||||
- (void)stopPollerIfNeeded
|
||||
{
|
||||
[self.lokiLongPoller stopIfNeeded];
|
||||
[self.lokiPoller stopIfNeeded];
|
||||
}
|
||||
|
||||
- (void)setUpDefaultPublicChatsIfNeeded
|
||||
|
@ -1713,7 +1713,7 @@ static BOOL isInternalTestVersion = NO;
|
|||
[SSKEnvironment.shared.messageSenderJobQueue clearAllJobs];
|
||||
[SSKEnvironment.shared.identityManager clearIdentityKey];
|
||||
[LKAPI clearRandomSnodePool];
|
||||
[self stopLongPollerIfNeeded];
|
||||
[self stopPollerIfNeeded];
|
||||
[self stopOpenGroupPollersIfNeeded];
|
||||
[self.lokiNewsFeedPoller stop];
|
||||
[self.lokiMessengerUpdatesFeedPoller stop];
|
||||
|
|
|
@ -5,5 +5,19 @@ class BaseVC : UIViewController {
|
|||
|
||||
override func viewDidLoad() {
|
||||
setNeedsStatusBarAppearanceUpdate()
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleUnexpectedDeviceLinkRequestReceivedNotification), name: .unexpectedDeviceLinkRequestReceived, object: nil)
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
@objc private func handleUnexpectedDeviceLinkRequestReceivedNotification() {
|
||||
guard DeviceLinkingUtilities.shouldShowUnexpectedDeviceLinkRequestReceivedAlert else { return }
|
||||
DispatchQueue.main.async {
|
||||
let alert = UIAlertController(title: "Device Link Request Received", message: "Open the device link screen by going to \"Settings\"> \"Devices\" > \"Link a Device\" to link your devices.", preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
|
|||
|
||||
private lazy var subtitleLabel: UILabel = {
|
||||
let result = UILabel()
|
||||
result.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
|
||||
result.textColor = Colors.text
|
||||
result.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
result.numberOfLines = 0
|
||||
result.lineBreakMode = .byWordWrapping
|
||||
|
@ -131,7 +131,7 @@ final class DeviceLinkingModal : Modal, DeviceLinkingSessionDelegate {
|
|||
}()
|
||||
subtitleLabel.text = {
|
||||
switch mode {
|
||||
case .master: return NSLocalizedString("Open Session on your secondary device and tap \"Link to an existing account\"", comment: "")
|
||||
case .master: return NSLocalizedString("Download Session on your other device and tap \"Link to an existing account\" at the bottom of the landing screen. If you have an existing account on your other device already you will have to delete that account first.", comment: "")
|
||||
case .slave: return NSLocalizedString("Please check that the words below match those shown on your other device", comment: "")
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -155,7 +155,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
|
|||
TSAccountManager.sharedInstance().phoneNumberAwaitingVerification = keyPair.hexEncodedPublicKey
|
||||
TSAccountManager.sharedInstance().didRegister()
|
||||
let appDelegate = UIApplication.shared.delegate as! AppDelegate
|
||||
appDelegate.startLongPollerIfNeeded()
|
||||
appDelegate.startPollerIfNeeded()
|
||||
let deviceLinkingModal = DeviceLinkingModal(mode: .slave, delegate: self)
|
||||
deviceLinkingModal.modalPresentationStyle = .overFullScreen
|
||||
deviceLinkingModal.modalTransitionStyle = .crossDissolve
|
||||
|
@ -176,7 +176,7 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate
|
|||
|
||||
func handleDeviceLinkingModalDismissed() {
|
||||
let appDelegate = UIApplication.shared.delegate as! AppDelegate
|
||||
appDelegate.stopLongPollerIfNeeded()
|
||||
appDelegate.stopPollerIfNeeded()
|
||||
TSAccountManager.sharedInstance().resetForReregistration()
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon
|
|||
}()
|
||||
|
||||
private lazy var scanQRCodeWrapperVC: ScanQRCodeWrapperVC = {
|
||||
let message = NSLocalizedString("Link to your existing account by going into your in-app settings and clicking \"Devices\".", comment: "")
|
||||
let message = NSLocalizedString("Navigate to \"Settings\" > \"Devices\" > \"Link a Device\" on your other device and then scan the QR code that comes up to start the linking process.", comment: "")
|
||||
let result = ScanQRCodeWrapperVC(message: message)
|
||||
result.delegate = self
|
||||
return result
|
||||
|
@ -167,7 +167,7 @@ private final class EnterPublicKeyVC : UIViewController {
|
|||
let explanationLabel = UILabel()
|
||||
explanationLabel.textColor = Colors.text
|
||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
explanationLabel.text = "Enter your Session ID to start the linking process."
|
||||
explanationLabel.text = "Navigate to \"Settings\" > \"Devices\" > \"Link a Device\" on your other device and then enter your Session ID here to start the linking process."
|
||||
explanationLabel.numberOfLines = 0
|
||||
explanationLabel.lineBreakMode = .byWordWrapping
|
||||
// Link button
|
||||
|
|
|
@ -444,6 +444,11 @@ typedef enum : NSUInteger {
|
|||
selector:@selector(handleMessageFailedNotification:)
|
||||
name:NSNotification.messageFailed
|
||||
object:nil];
|
||||
// Device linking
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleUnexpectedDeviceLinkRequestReceivedNotification)
|
||||
name:NSNotification.unexpectedDeviceLinkRequestReceived
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (BOOL)isGroupConversation
|
||||
|
@ -5479,6 +5484,16 @@ typedef enum : NSUInteger {
|
|||
});
|
||||
}
|
||||
|
||||
- (void)handleUnexpectedDeviceLinkRequestReceivedNotification
|
||||
{
|
||||
if (!LKDeviceLinkingUtilities.shouldShowUnexpectedDeviceLinkRequestReceivedAlert) { return; }
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Device Link Request Received" message:@"Open the device link screen by going to \"Settings\"> \"Devices\" > \"Link a Device\" to link your devices." preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -681,7 +681,7 @@ typedef NS_ENUM(NSInteger, HomeViewControllerSection) {
|
|||
[SSKEnvironment.shared.identityManager clearIdentityKey];
|
||||
[LKAPI clearRandomSnodePool];
|
||||
AppDelegate *appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate;
|
||||
[appDelegate stopLongPollerIfNeeded];
|
||||
[appDelegate stopPollerIfNeeded];
|
||||
[appDelegate stopOpenGroupPollersIfNeeded];
|
||||
[SSKEnvironment.shared.tsAccountManager resetForReregistration];
|
||||
UIViewController *rootViewController = [[OnboardingController new] initialViewController];
|
||||
|
|
|
@ -2768,7 +2768,7 @@
|
|||
"Link to an existing account" = "Link to an existing account";
|
||||
"Enter your public key" = "Enter your public key";
|
||||
"Link to your existing account by going into your in-app settings and clicking \"Devices\"." = "Link to your existing account by going into your in-app settings and clicking \"Devices\".";
|
||||
"Open Session on your secondary device and tap \"Link to an existing account\"" = "Open Session on your secondary device and tap \"Link to an existing account\"";
|
||||
"Download Session on your other device and tap \"Link to an existing account\" at the bottom of the landing screen. If you have an existing account on your other device already you will have to delete that account first." = "Download Session on your other device and tap \"Link to an existing account\" at the bottom of the landing screen. If you have an existing account on your other device already you will have to delete that account first.";
|
||||
"Group Settings" = "Group Settings";
|
||||
"Your Session ID is the unique address people can use to contact you on Session. With no connection to your real identity, your Session ID is totally anonymous and private by design." = "Your Session ID is the unique address people can use to contact you on Session. With no connection to your real identity, your Session ID is totally anonymous and private by design.";
|
||||
"Enter the recovery phrase that was given to you when you signed up to restore your account." = "Enter the recovery phrase that was given to you when you signed up to restore your account.";
|
||||
|
|
|
@ -1,20 +1,16 @@
|
|||
import PromiseKit
|
||||
|
||||
@objc(LKLongPoller)
|
||||
public final class LokiLongPoller : NSObject {
|
||||
@objc(LKPoller)
|
||||
public final class LokiPoller : 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 getUserHexEncodedPublicKey() }
|
||||
private static let pollInterval: TimeInterval = 1
|
||||
private static let retryInterval: TimeInterval = 4
|
||||
|
||||
// MARK: Initialization
|
||||
@objc public init(onMessagesReceived: @escaping ([SSKProtoEnvelope]) -> Void) {
|
||||
|
@ -25,7 +21,7 @@ public final class LokiLongPoller : NSObject {
|
|||
// MARK: Public API
|
||||
@objc public func startIfNeeded() {
|
||||
guard !hasStarted else { return }
|
||||
print("[Loki] Started long polling.")
|
||||
print("[Loki] Started polling.")
|
||||
hasStarted = true
|
||||
hasStopped = false
|
||||
openConnections()
|
||||
|
@ -33,7 +29,7 @@ public final class LokiLongPoller : NSObject {
|
|||
|
||||
@objc public func stopIfNeeded() {
|
||||
guard !hasStopped else { return }
|
||||
print("[Loki] Stopped long polling.")
|
||||
print("[Loki] Stopped polling.")
|
||||
hasStarted = false
|
||||
hasStopped = true
|
||||
usedSnodes.removeAll()
|
||||
|
@ -42,49 +38,46 @@ public final class LokiLongPoller : NSObject {
|
|||
// 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>]()) }
|
||||
LokiAPI.getSwarm(for: getUserHexEncodedPublicKey()).then { [weak self] _ -> Promise<Void> in
|
||||
guard let strongSelf = self else { return Promise { $0.fulfill(()) } }
|
||||
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)
|
||||
let (promise, seal) = Promise<Void>.pending()
|
||||
strongSelf.pollNextSnode(seal: seal)
|
||||
return promise
|
||||
}.ensure { [weak self] in
|
||||
guard let strongSelf = self else { return }
|
||||
Timer.scheduledTimer(withTimeInterval: strongSelf.retryInterval, repeats: false) { _ in
|
||||
Timer.scheduledTimer(withTimeInterval: LokiPoller.retryInterval, repeats: false) { _ in
|
||||
guard let strongSelf = self else { return }
|
||||
strongSelf.openConnections()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func openConnectionToNextSnode(seal: Resolver<Void>) {
|
||||
private func pollNextSnode(seal: Resolver<Void>) {
|
||||
let userHexEncodedPublicKey = getUserHexEncodedPublicKey()
|
||||
let swarm = LokiAPI.swarmCache[userHexEncodedPublicKey] ?? []
|
||||
let userHexEncodedPublicKey = self.userHexEncodedPublicKey
|
||||
let unusedSnodes = Set(swarm).subtracting(usedSnodes)
|
||||
if !unusedSnodes.isEmpty {
|
||||
// randomElement() uses the system's default random generator, which is cryptographically secure
|
||||
let nextSnode = unusedSnodes.randomElement()!
|
||||
usedSnodes.insert(nextSnode)
|
||||
print("[Loki] Opening long polling connection to \(nextSnode).")
|
||||
longPoll(nextSnode, seal: seal).catch(on: LokiAPI.errorHandlingQueue) { [weak self] error in
|
||||
print("[Loki] Long polling connection to \(nextSnode) failed; dropping it and switching to next snode.")
|
||||
print("[Loki] Polling \(nextSnode).")
|
||||
poll(nextSnode, seal: seal).catch(on: LokiAPI.errorHandlingQueue) { [weak self] error in
|
||||
print("[Loki] Polling \(nextSnode) failed; dropping it and switching to next snode.")
|
||||
LokiAPI.dropIfNeeded(nextSnode, hexEncodedPublicKey: userHexEncodedPublicKey)
|
||||
self?.openConnectionToNextSnode(seal: seal)
|
||||
self?.pollNextSnode(seal: seal)
|
||||
}
|
||||
} else {
|
||||
seal.fulfill(())
|
||||
}
|
||||
}
|
||||
|
||||
private func longPoll(_ target: LokiAPITarget, seal: Resolver<Void>) -> Promise<Void> {
|
||||
return LokiAPI.getRawMessages(from: target, usingLongPolling: true).then(on: DispatchQueue.global()) { [weak self] rawResponse -> Promise<Void> in
|
||||
private func poll(_ target: LokiAPITarget, seal: Resolver<Void>) -> Promise<Void> {
|
||||
return LokiAPI.getRawMessages(from: target, usingLongPolling: false).then(on: DispatchQueue.global()) { [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)
|
||||
return strongSelf.poll(target, seal: seal)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,19 @@
|
|||
|
||||
public enum DeviceLinkingUtilities {
|
||||
|
||||
@objc(LKDeviceLinkingUtilities)
|
||||
public final class DeviceLinkingUtilities : NSObject {
|
||||
private static var lastUnexpectedDeviceLinkRequestDate: Date? = nil
|
||||
|
||||
private override init() { }
|
||||
|
||||
@objc public static var shouldShowUnexpectedDeviceLinkRequestReceivedAlert: Bool {
|
||||
let now = Date()
|
||||
if let lastUnexpectedDeviceLinkRequestDate = lastUnexpectedDeviceLinkRequestDate {
|
||||
if now.timeIntervalSince(lastUnexpectedDeviceLinkRequestDate) < 30 { return false }
|
||||
}
|
||||
lastUnexpectedDeviceLinkRequestDate = now
|
||||
return true
|
||||
}
|
||||
|
||||
// When requesting a device link, the slave device signs the master device's public key. When authorizing
|
||||
// a device link, the master device signs the slave device's public key.
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ public extension Notification.Name {
|
|||
public static let seedViewed = Notification.Name("seedViewed")
|
||||
// Interaction
|
||||
public static let dataNukeRequested = Notification.Name("dataNukeRequested")
|
||||
// Device linking
|
||||
public static let unexpectedDeviceLinkRequestReceived = Notification.Name("unexpectedDeviceLinkRequestReceived")
|
||||
}
|
||||
|
||||
@objc public extension NSNotification {
|
||||
|
@ -37,4 +39,6 @@ public extension Notification.Name {
|
|||
@objc public static let seedViewed = Notification.Name.seedViewed.rawValue as NSString
|
||||
// Interaction
|
||||
@objc public static let dataNukeRequested = Notification.Name.dataNukeRequested.rawValue as NSString
|
||||
// Device linking
|
||||
@objc public static let unexpectedDeviceLinkRequestReceived = Notification.Name.unexpectedDeviceLinkRequestReceived.rawValue as NSString
|
||||
}
|
||||
|
|
|
@ -494,6 +494,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
} else if (slaveSignature != nil) { // Request
|
||||
OWSLogInfo(@"[Loki] Received a device linking request from: %@", envelope.source); // Not slaveHexEncodedPublicKey
|
||||
if (LKDeviceLinkingSession.current == nil) {
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:NSNotification.unexpectedDeviceLinkRequestReceived object:nil];
|
||||
}
|
||||
[LKDeviceLinkingSession.current processLinkingRequestFrom:slaveHexEncodedPublicKey to:masterHexEncodedPublicKey with:slaveSignature];
|
||||
}
|
||||
} else if (contentProto.syncMessage) {
|
||||
|
|
Loading…
Reference in New Issue