From 6529d3192b3801fc82460f31cff73808ec4344b0 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Thu, 30 Jul 2020 15:19:19 +1000 Subject: [PATCH 1/3] Implement multi device removal sheet --- Signal.xcodeproj/project.pbxproj | 8 +- Signal/src/Loki/View Controllers/HomeVC.swift | 16 ++-- .../src/Loki/View Controllers/LandingVC.swift | 10 +- .../MultiDeviceRemovalSheet.swift | 93 +++++++++++++++++++ .../Loki/View Controllers/PNModeSheet.swift | 89 ------------------ .../src/Loki/View Controllers/PNModeVC.swift | 2 +- .../Loki/View Controllers/SettingsVC.swift | 4 +- .../src/Loki/Utilities/LKUserDefaults.swift | 2 +- 8 files changed, 116 insertions(+), 108 deletions(-) create mode 100644 Signal/src/Loki/View Controllers/MultiDeviceRemovalSheet.swift delete mode 100644 Signal/src/Loki/View Controllers/PNModeSheet.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 317fe4580..267c7ba11 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -574,7 +574,6 @@ C31A6C5A247F214E001123EF /* UIView+Glow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31A6C59247F214E001123EF /* UIView+Glow.swift */; }; C31A6C5C247F2CF3001123EF /* CGRect+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31A6C5B247F2CF3001123EF /* CGRect+Utilities.swift */; }; C34C8F7423A7830B00D82669 /* SpaceMono-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C34C8F7323A7830A00D82669 /* SpaceMono-Bold.ttf */; }; - C353F8F7244808E90011121A /* PNModeSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C353F8F6244808E90011121A /* PNModeSheet.swift */; }; C353F8F9244809150011121A /* PNOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C353F8F8244809150011121A /* PNOptionView.swift */; }; C3548F0624456447009433A8 /* PNModeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3548F0524456447009433A8 /* PNModeVC.swift */; }; C3548F0824456AB6009433A8 /* UIView+Wrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3548F0724456AB6009433A8 /* UIView+Wrapping.swift */; }; @@ -584,6 +583,7 @@ C35E8AA92485C85800ACB629 /* GeoLite2-Country-Blocks-IPv4.csv in Resources */ = {isa = PBXBuildFile; fileRef = C35E8AA62485C85600ACB629 /* GeoLite2-Country-Blocks-IPv4.csv */; }; C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */ = {isa = PBXBuildFile; fileRef = C35E8AAD2485E51D00ACB629 /* IP2Country.swift */; }; C3638C0524C7F0B500AF29BC /* LK002RemoveFriendRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3638C0424C7F0B500AF29BC /* LK002RemoveFriendRequests.swift */; }; + C369549D24D27A3500CEB4E3 /* MultiDeviceRemovalSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C369549C24D27A3500CEB4E3 /* MultiDeviceRemovalSheet.swift */; }; C36B8707243C50C60049991D /* SignalMessaging.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 453518921FC63DBF00210559 /* SignalMessaging.framework */; }; C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; }; C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */; }; @@ -1362,7 +1362,6 @@ C31A6C59247F214E001123EF /* UIView+Glow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Glow.swift"; sourceTree = ""; }; C31A6C5B247F2CF3001123EF /* CGRect+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGRect+Utilities.swift"; sourceTree = ""; }; C34C8F7323A7830A00D82669 /* SpaceMono-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SpaceMono-Bold.ttf"; sourceTree = ""; }; - C353F8F6244808E90011121A /* PNModeSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNModeSheet.swift; sourceTree = ""; }; C353F8F8244809150011121A /* PNOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNOptionView.swift; sourceTree = ""; }; C3548F0524456447009433A8 /* PNModeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNModeVC.swift; sourceTree = ""; }; C3548F0724456AB6009433A8 /* UIView+Wrapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Wrapping.swift"; sourceTree = ""; }; @@ -1372,6 +1371,7 @@ C35E8AA62485C85600ACB629 /* GeoLite2-Country-Blocks-IPv4.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "GeoLite2-Country-Blocks-IPv4.csv"; sourceTree = ""; }; C35E8AAD2485E51D00ACB629 /* IP2Country.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IP2Country.swift; sourceTree = ""; }; C3638C0424C7F0B500AF29BC /* LK002RemoveFriendRequests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LK002RemoveFriendRequests.swift; sourceTree = ""; }; + C369549C24D27A3500CEB4E3 /* MultiDeviceRemovalSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiDeviceRemovalSheet.swift; sourceTree = ""; }; C3AA6BB824CE8F1B002358B6 /* Migrating Translations from Android.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "Migrating Translations from Android.md"; sourceTree = ""; }; C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCopyableLabel.swift; sourceTree = ""; }; C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sheet.swift; sourceTree = ""; }; @@ -2710,12 +2710,12 @@ B85357C423A1F13800AAF6CD /* LinkDeviceVC.swift */, B85357C623A1FB5100AAF6CD /* LinkDeviceVCDelegate.swift */, B86BD08323399ACF000F5AE3 /* Modal.swift */, + C369549C24D27A3500CEB4E3 /* MultiDeviceRemovalSheet.swift */, B80A579E23DFF1F300876683 /* NewClosedGroupVC.swift */, B8CCF63623961D6D0091D419 /* NewPrivateChatVC.swift */, B894D0742339EDCF00B4D94D /* NukeDataModal.swift */, C3DFFAC723E970080058DAF8 /* OpenGroupSuggestionSheet.swift */, B879D448247E1BE300DB3608 /* PathVC.swift */, - C353F8F6244808E90011121A /* PNModeSheet.swift */, C3548F0524456447009433A8 /* PNModeVC.swift */, B886B4A62398B23E00211ABE /* QRCodeVC.swift */, B82B408B239A068800A248E7 /* RegisterVC.swift */, @@ -3801,7 +3801,6 @@ 4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */, 4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */, 3496955E219B605E00DCFE74 /* PhotoLibrary.swift in Sources */, - C353F8F7244808E90011121A /* PNModeSheet.swift in Sources */, 45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */, 340FC8A9204DAC8D007AEB0F /* NotificationSettingsOptionsViewController.m in Sources */, C3548F0624456447009433A8 /* PNModeVC.swift in Sources */, @@ -3848,6 +3847,7 @@ 4C21D5D8223AC60F00EF8A77 /* PhotoCapture.swift in Sources */, 4C13C9F620E57BA30089A98B /* ColorPickerViewController.swift in Sources */, 4CC1ECFB211A553000CC13BE /* AppUpdateNag.swift in Sources */, + C369549D24D27A3500CEB4E3 /* MultiDeviceRemovalSheet.swift in Sources */, 34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */, B8CCF639239721E20091D419 /* TabBar.swift in Sources */, 458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */, diff --git a/Signal/src/Loki/View Controllers/HomeVC.swift b/Signal/src/Loki/View Controllers/HomeVC.swift index 39f913d70..eec8fefc7 100644 --- a/Signal/src/Loki/View Controllers/HomeVC.swift +++ b/Signal/src/Loki/View Controllers/HomeVC.swift @@ -177,12 +177,16 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) isViewVisible = true - let hasSeenPNModeSheet = UserDefaults.standard[.hasSeenPNModeSheet] - if !hasSeenPNModeSheet { - let pnModeSheet = PNModeSheet() - pnModeSheet.modalPresentationStyle = .overFullScreen - pnModeSheet.modalTransitionStyle = .crossDissolve - present(pnModeSheet, animated: true, completion: nil) + let hasSeenMultiDeviceRemovalSheet = UserDefaults.standard[.hasSeenMultiDeviceRemovalSheet] + if !hasSeenMultiDeviceRemovalSheet { + let _ = FileServerAPI.getDeviceLinks(associatedWith: getUserHexEncodedPublicKey()).done(on: DispatchQueue.main) { [weak self] deviceLinks in + guard !deviceLinks.isEmpty else { return } + let multiDeviceRemovalSheet = MultiDeviceRemovalSheet() + multiDeviceRemovalSheet.modalPresentationStyle = .overFullScreen + multiDeviceRemovalSheet.modalTransitionStyle = .crossDissolve + self?.present(multiDeviceRemovalSheet, animated: true, completion: nil) + } + UserDefaults.standard[.hasSeenMultiDeviceRemovalSheet] = true } UserDefaults.standard[.hasLaunchedOnce] = true } diff --git a/Signal/src/Loki/View Controllers/LandingVC.swift b/Signal/src/Loki/View Controllers/LandingVC.swift index 777ef0f98..51acccbac 100644 --- a/Signal/src/Loki/View Controllers/LandingVC.swift +++ b/Signal/src/Loki/View Controllers/LandingVC.swift @@ -59,11 +59,11 @@ final class LandingVC : BaseVC, LinkDeviceVCDelegate, DeviceLinkingModalDelegate // Set up link button container let linkButtonContainer = UIView() linkButtonContainer.set(.height, to: Values.onboardingButtonBottomOffset) - linkButtonContainer.addSubview(linkButton) - linkButton.pin(.leading, to: .leading, of: linkButtonContainer, withInset: Values.massiveSpacing) - linkButton.pin(.top, to: .top, of: linkButtonContainer) - linkButtonContainer.pin(.trailing, to: .trailing, of: linkButton, withInset: Values.massiveSpacing) - linkButtonContainer.pin(.bottom, to: .bottom, of: linkButton, withInset: isIPhone5OrSmaller ? 6 : 10) +// linkButtonContainer.addSubview(linkButton) +// linkButton.pin(.leading, to: .leading, of: linkButtonContainer, withInset: Values.massiveSpacing) +// linkButton.pin(.top, to: .top, of: linkButtonContainer) +// linkButtonContainer.pin(.trailing, to: .trailing, of: linkButton, withInset: Values.massiveSpacing) +// linkButtonContainer.pin(.bottom, to: .bottom, of: linkButton, withInset: isIPhone5OrSmaller ? 6 : 10) // Set up button stack view let buttonStackView = UIStackView(arrangedSubviews: [ registerButton, restoreButton ]) buttonStackView.axis = .vertical diff --git a/Signal/src/Loki/View Controllers/MultiDeviceRemovalSheet.swift b/Signal/src/Loki/View Controllers/MultiDeviceRemovalSheet.swift new file mode 100644 index 000000000..1391eb3ae --- /dev/null +++ b/Signal/src/Loki/View Controllers/MultiDeviceRemovalSheet.swift @@ -0,0 +1,93 @@ +import PromiseKit + +final class MultiDeviceRemovalSheet : Sheet { + + private lazy var removalDate: Date = { + let calendar = Calendar(identifier: .gregorian) + let timezone = TimeZone(identifier: "Australia/Melbourne") + let components = DateComponents(calendar: calendar, timeZone: timezone, year: 2020, month: 8, day: 6, hour: 17) + return calendar.date(from: components)! + }() + + private lazy var removalDateDescription: String = { + let formatter = DateFormatter() + formatter.dateFormat = "MMMM d" + return formatter.string(from: removalDate) + }() + + private lazy var explanation: String = { + if UserDefaults.standard[.masterHexEncodedPublicKey] != nil { + let format = """ + You’re seeing this because this is a secondary device in a multi-device setup. To improve reliability and stability, we’ve decided to temporarily disable Session’s multi-device functionality on %@. Device linking has been disabled, and the next update will erase existing secondary devices. + + To read more about this change, visit the Session FAQ at getsession.org/faq. + """ + return String(format: format, removalDateDescription) + } else { + let format = """ + You’re seeing this because you have a secondary device linked to your Session ID. To improve reliability and stability, we’ve decided to temporarily disable Session’s multi-device functionality on %@. Device linking has been disabled, and the next update will erase existing secondary devices. + + To read more about this change, visit the Session FAQ at getsession.org/faq + """ + return String(format: format, removalDateDescription) + } + }() + + private lazy var attributedExplanation: NSAttributedString = { + let result = NSMutableAttributedString(string: explanation) + result.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.smallFontSize), range: (explanation as NSString).range(of: removalDateDescription)) + result.addAttribute(.foregroundColor, value: Colors.accent, range: (explanation as NSString).range(of: removalDateDescription)) + result.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.smallFontSize), range: (explanation as NSString).range(of: "getsession.org/faq")) + result.addAttribute(.foregroundColor, value: Colors.accent, range: (explanation as NSString).range(of: "getsession.org/faq")) + return result + }() + + private lazy var explanationLabel: UILabel = { + let result = UILabel() + result.textColor = Colors.text + result.font = .systemFont(ofSize: Values.smallFontSize) + result.attributedText = attributedExplanation + result.numberOfLines = 0 + result.lineBreakMode = .byWordWrapping + return result + }() + + override func populateContentView() { + // Set up title label + let titleLabel = UILabel() + titleLabel.textColor = Colors.text + titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize) + titleLabel.text = "Changes to Multi-Device" + titleLabel.numberOfLines = 0 + titleLabel.lineBreakMode = .byWordWrapping + // Set up explanation label + explanationLabel.isUserInteractionEnabled = true + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleExplanationLabelTapped)) + explanationLabel.addGestureRecognizer(tapGestureRecognizer) + // Set up OK button + let okButton = Button(style: .prominentOutline, size: .large) + okButton.set(.width, to: 240) + okButton.setTitle(NSLocalizedString("OK", comment: ""), for: UIControl.State.normal) + okButton.addTarget(self, action: #selector(close), for: UIControl.Event.touchUpInside) + // Set up main stack view + let stackView = UIStackView(arrangedSubviews: [ titleLabel, explanationLabel, okButton ]) + stackView.axis = .vertical + stackView.spacing = Values.veryLargeSpacing + stackView.alignment = .center + // Set up constraints + contentView.addSubview(stackView) + stackView.pin(.leading, to: .leading, of: contentView, withInset: Values.veryLargeSpacing) + stackView.pin(.top, to: .top, of: contentView, withInset: Values.largeSpacing) + contentView.pin(.trailing, to: .trailing, of: stackView, withInset: Values.veryLargeSpacing) + contentView.pin(.bottom, to: .bottom, of: stackView, withInset: Values.veryLargeSpacing + overshoot) + } + + @objc private func handleExplanationLabelTapped(_ tapGestureRecognizer: UITapGestureRecognizer) { + let range = (explanationLabel.text! as NSString).range(of: "getsession.org/faq") + let touchInExplanationLabelCoordinates = tapGestureRecognizer.location(in: explanationLabel) + let characterIndex = explanationLabel.characterIndex(for: touchInExplanationLabelCoordinates) + guard range.contains(characterIndex) else { return } + let url = URL(string: "https://getsession.org/faq")! + UIApplication.shared.open(url) + } +} diff --git a/Signal/src/Loki/View Controllers/PNModeSheet.swift b/Signal/src/Loki/View Controllers/PNModeSheet.swift deleted file mode 100644 index 1ddf9b6c9..000000000 --- a/Signal/src/Loki/View Controllers/PNModeSheet.swift +++ /dev/null @@ -1,89 +0,0 @@ -import PromiseKit - -final class PNModeSheet : Sheet, OptionViewDelegate { - - private var optionViews: [OptionView] { - [ apnsOptionView, backgroundPollingOptionView ] - } - - private var selectedOptionView: OptionView? { - return optionViews.first { $0.isSelected } - } - - // MARK: Components - private lazy var apnsOptionView = OptionView(title: NSLocalizedString("Apple Push Notification Service", comment: ""), explanation: NSLocalizedString("Session will use the Apple Push Notification service to receive push notifications. You'll be notified of new messages reliably and immediately. Using APNs means that your IP address and device token will be exposed to Apple. If you use push notifications for other apps, this will already be the case. Your IP address and device token will also be exposed to Loki, but your messages will still be onion-routed and end-to-end encrypted, so the contents of your messages will remain completely private.", comment: ""), delegate: self, isRecommended: true) - private lazy var backgroundPollingOptionView = OptionView(title: NSLocalizedString("Background Polling", comment: ""), explanation: NSLocalizedString("Session will occasionally check for new messages in the background. This guarantees full metadata protection, but message notifications may be significantly delayed.", comment: ""), delegate: self) - - // MARK: Lifecycle - override func populateContentView() { - // Set up title label - let titleLabel = UILabel() - titleLabel.textColor = Colors.text - titleLabel.font = .boldSystemFont(ofSize: isIPhone5OrSmaller ? Values.largeFontSize : Values.veryLargeFontSize) - titleLabel.text = NSLocalizedString("Push Notifications", comment: "") - titleLabel.numberOfLines = 0 - titleLabel.lineBreakMode = .byWordWrapping - // Set up explanation label - let explanationLabel = UILabel() - explanationLabel.textColor = Colors.text - explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) - explanationLabel.text = NSLocalizedString("Session now features two ways to handle push notifications. Make sure to read the descriptions carefully before you choose.", comment: "") - explanationLabel.numberOfLines = 0 - explanationLabel.lineBreakMode = .byWordWrapping - // Set up options stack view - let optionsStackView = UIStackView(arrangedSubviews: optionViews) - optionsStackView.axis = .vertical - optionsStackView.spacing = Values.smallSpacing - optionsStackView.alignment = .fill - // Set up confirm button - let confirmButton = Button(style: .prominentOutline, size: .medium) - confirmButton.set(.width, to: 240) - confirmButton.setTitle(NSLocalizedString("Confirm", comment: ""), for: UIControl.State.normal) - confirmButton.addTarget(self, action: #selector(confirm), for: UIControl.Event.touchUpInside) - // Set up dismiss button - let skipButton = Button(style: .regular, size: .medium) - skipButton.set(.width, to: 240) - skipButton.setTitle(NSLocalizedString("Skip", comment: ""), for: UIControl.State.normal) - skipButton.addTarget(self, action: #selector(close), for: UIControl.Event.touchUpInside) - // Set up button stack view - let bottomStackView = UIStackView(arrangedSubviews: [ confirmButton, skipButton ]) - bottomStackView.axis = .vertical - bottomStackView.spacing = isIPhone5OrSmaller ? Values.smallSpacing : Values.mediumSpacing - bottomStackView.alignment = .fill - // Set up main stack view - let stackView = UIStackView(arrangedSubviews: [ titleLabel, explanationLabel, optionsStackView, bottomStackView ]) - stackView.axis = .vertical - stackView.spacing = isIPhone5OrSmaller ? 12 : Values.largeSpacing - stackView.alignment = .center - // Set up constraints - contentView.addSubview(stackView) - stackView.pin(.leading, to: .leading, of: contentView, withInset: isIPhone5OrSmaller ? Values.mediumSpacing : Values.largeSpacing) - stackView.pin(.top, to: .top, of: contentView, withInset: isIPhone5OrSmaller ? Values.mediumSpacing : Values.largeSpacing) - contentView.pin(.trailing, to: .trailing, of: stackView, withInset: isIPhone5OrSmaller ? Values.mediumSpacing : Values.largeSpacing) - contentView.pin(.bottom, to: .bottom, of: stackView, withInset: (isIPhone5OrSmaller ? Values.mediumSpacing : Values.veryLargeSpacing) + overshoot) - } - - // MARK: Interaction - func optionViewDidActivate(_ optionView: OptionView) { - optionViews.filter { $0 != optionView }.forEach { $0.isSelected = false } - } - - @objc private func confirm() { - guard selectedOptionView != nil else { - let title = NSLocalizedString("Please Pick an Option", comment: "") - let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) - return present(alert, animated: true, completion: nil) - } - UserDefaults.standard[.isUsingFullAPNs] = (selectedOptionView == apnsOptionView) - let syncTokensJob = SyncPushTokensJob(accountManager: AppEnvironment.shared.accountManager, preferences: Environment.shared.preferences) - syncTokensJob.uploadOnlyIfStale = false - let _: Promise = syncTokensJob.run() - close() - } - - override func close() { - UserDefaults.standard[.hasSeenPNModeSheet] = true - super.close() - } -} diff --git a/Signal/src/Loki/View Controllers/PNModeVC.swift b/Signal/src/Loki/View Controllers/PNModeVC.swift index ab16bfee2..f2293b421 100644 --- a/Signal/src/Loki/View Controllers/PNModeVC.swift +++ b/Signal/src/Loki/View Controllers/PNModeVC.swift @@ -89,7 +89,7 @@ final class PNModeVC : BaseVC, OptionViewDelegate { return present(alert, animated: true, completion: nil) } UserDefaults.standard[.isUsingFullAPNs] = (selectedOptionView == apnsOptionView) - UserDefaults.standard[.hasSeenPNModeSheet] = true // Shouldn't be shown to users who've done the new onboarding + UserDefaults.standard[.hasSeenMultiDeviceRemovalSheet] = true TSAccountManager.sharedInstance().didRegister() let homeVC = HomeVC() navigationController!.setViewControllers([ homeVC ], animated: true) diff --git a/Signal/src/Loki/View Controllers/SettingsVC.swift b/Signal/src/Loki/View Controllers/SettingsVC.swift index 8e2976a25..518a67001 100644 --- a/Signal/src/Loki/View Controllers/SettingsVC.swift +++ b/Signal/src/Loki/View Controllers/SettingsVC.swift @@ -167,8 +167,8 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate { ] let isMasterDevice = UserDefaults.standard.isMasterDevice if isMasterDevice { - result.append(getSeparator()) - result.append(getSettingButton(withTitle: NSLocalizedString("vc_settings_devices_button_title", comment: ""), color: Colors.text, action: #selector(showLinkedDevices))) +// result.append(getSeparator()) +// result.append(getSettingButton(withTitle: NSLocalizedString("vc_settings_devices_button_title", comment: ""), color: Colors.text, action: #selector(showLinkedDevices))) result.append(getSeparator()) result.append(getSettingButton(withTitle: NSLocalizedString("vc_settings_recovery_phrase_button_title", comment: ""), color: Colors.text, action: #selector(showSeed))) } diff --git a/SignalServiceKit/src/Loki/Utilities/LKUserDefaults.swift b/SignalServiceKit/src/Loki/Utilities/LKUserDefaults.swift index ca4039e08..21ab9158d 100644 --- a/SignalServiceKit/src/Loki/Utilities/LKUserDefaults.swift +++ b/SignalServiceKit/src/Loki/Utilities/LKUserDefaults.swift @@ -5,8 +5,8 @@ public enum LKUserDefaults { public enum Bool : Swift.String { case hasLaunchedOnce case hasSeenGIFMetadataWarning + case hasSeenMultiDeviceRemovalSheet case hasSeenOpenGroupSuggestionSheet - case hasSeenPNModeSheet case hasViewedSeed /// Whether the device was unlinked as a slave device (used to notify the user on the landing screen). case wasUnlinked From 7f4fd06fc239d5c52da49e176abbcf494fbdce5c Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Thu, 30 Jul 2020 15:27:21 +1000 Subject: [PATCH 2/3] Update copy --- .../src/Loki/View Controllers/MultiDeviceRemovalSheet.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Signal/src/Loki/View Controllers/MultiDeviceRemovalSheet.swift b/Signal/src/Loki/View Controllers/MultiDeviceRemovalSheet.swift index 1391eb3ae..e78543eb3 100644 --- a/Signal/src/Loki/View Controllers/MultiDeviceRemovalSheet.swift +++ b/Signal/src/Loki/View Controllers/MultiDeviceRemovalSheet.swift @@ -18,14 +18,14 @@ final class MultiDeviceRemovalSheet : Sheet { private lazy var explanation: String = { if UserDefaults.standard[.masterHexEncodedPublicKey] != nil { let format = """ - You’re seeing this because this is a secondary device in a multi-device setup. To improve reliability and stability, we’ve decided to temporarily disable Session’s multi-device functionality on %@. Device linking has been disabled, and the next update will erase existing secondary devices. + You’re seeing this because this is a secondary device in a multi-device setup. To improve reliability and stability, we’ve decided to temporarily disable Session’s multi-device functionality. Device linking has been disabled, and existing secondary clients will be erased on %@. To read more about this change, visit the Session FAQ at getsession.org/faq. """ return String(format: format, removalDateDescription) } else { let format = """ - You’re seeing this because you have a secondary device linked to your Session ID. To improve reliability and stability, we’ve decided to temporarily disable Session’s multi-device functionality on %@. Device linking has been disabled, and the next update will erase existing secondary devices. + You’re seeing this because you have a secondary device linked to your Session ID. To improve reliability and stability, we’ve decided to temporarily disable Session’s multi-device functionality. Device linking has been disabled, and existing secondary clients will be erased on %@. To read more about this change, visit the Session FAQ at getsession.org/faq """ From f8dd62ca438309cce4f933bca6b5346fd28d68d8 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Fri, 31 Jul 2020 09:45:16 +1000 Subject: [PATCH 3/3] Clean --- Signal/src/Loki/View Controllers/HomeVC.swift | 29 +++++++------------ Signal/src/util/MainAppContext.m | 1 - 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Signal/src/Loki/View Controllers/HomeVC.swift b/Signal/src/Loki/View Controllers/HomeVC.swift index eec8fefc7..dfa9c5553 100644 --- a/Signal/src/Loki/View Controllers/HomeVC.swift +++ b/Signal/src/Loki/View Controllers/HomeVC.swift @@ -216,21 +216,17 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol isObservingDatabase = isViewVisible && CurrentAppContext().isAppForegroundAndActive() } - private func updateYDBThreadMapping() { + private func reload() { + AssertIsOnMainThread() uiDatabaseConnection.beginLongLivedReadTransaction() uiDatabaseConnection.read { transaction in self.threads.update(with: transaction) } - } - - private func reload() { - AssertIsOnMainThread() - updateYDBThreadMapping() threadViewModelCache.removeAll() tableView.reloadData() emptyStateView.isHidden = (threadCount != 0) } - + @objc private func handleYapDatabaseModifiedNotification(_ notification: Notification) { AssertIsOnMainThread() guard isObservingDatabase else { return } @@ -243,17 +239,13 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol } return } - // Guard for changes made in Notification Service Extension - // Crashes may happen if there is some modification in NSE - // The YapDBModificationNotification cannot cross process - // So the thread mapping won't update itself if DB modification happened in other process like NSE - // With these code we can sync the mapping before asking for changes from YapDB - if (notifications.count > 0) { - if let firstChangeset = notifications[0].userInfo { - let firstSnapshot = firstChangeset[YapDatabaseSnapshotKey] as! UInt64 - if (self.threads.snapshotOfLastUpdate != firstSnapshot - 1) { - reload() - return + // If changes were made in a different process (e.g. the Notification Service Extension) the thread mapping can be out of date + // at this point, causing the app to crash. The code below prevents that by force syncing the database before proceeding. + if notifications.count > 0 { + if let firstChangeSet = notifications[0].userInfo { + let firstSnapshot = firstChangeSet[YapDatabaseSnapshotKey] as! UInt64 + if threads.snapshotOfLastUpdate != firstSnapshot - 1 { + return reload() } } } @@ -283,7 +275,6 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol @objc private func handleApplicationDidBecomeActiveNotification(_ notification: Notification) { updateIsObservingDatabase() - updateYDBThreadMapping() } @objc private func handleApplicationWillResignActiveNotification(_ notification: Notification) { diff --git a/Signal/src/util/MainAppContext.m b/Signal/src/util/MainAppContext.m index e367efa31..9790ae493 100644 --- a/Signal/src/util/MainAppContext.m +++ b/Signal/src/util/MainAppContext.m @@ -157,7 +157,6 @@ NSString *const ReportedApplicationStateDidChangeNotification = @"ReportedApplic - (BOOL)isMainAppAndActive { - if (![NSThread isMainThread]) { return NO; } return [UIApplication sharedApplication].applicationState == UIApplicationStateActive; }