Merge pull request #354 from oxen-io/multi-device

Sync Profile Updates
pull/355/head
Niels Andriesse 4 years ago committed by GitHub
commit b388fcd1b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -218,4 +218,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 2fca3f32c171e1324c9e3809b96a32d4a929d05c
COCOAPODS: 1.10.0.rc.1
COCOAPODS: 1.10.1

@ -141,7 +141,6 @@
4CA485BB2232339F004B9E7D /* PhotoCaptureViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA485BA2232339F004B9E7D /* PhotoCaptureViewController.swift */; };
4CC1ECFB211A553000CC13BE /* AppUpdateNag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC1ECFA211A553000CC13BE /* AppUpdateNag.swift */; };
4CC613362227A00400E21A3A /* ConversationSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC613352227A00400E21A3A /* ConversationSearch.swift */; };
4CFE6B6C21F92BA700006701 /* LegacyNotificationsAdaptee.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFE6B6B21F92BA700006701 /* LegacyNotificationsAdaptee.swift */; };
5DF9AB212C6DB1E8BE70EFF6 /* Pods_SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB523C549815DE935E98151E /* Pods_SessionMessagingKit.framework */; };
70377AAB1918450100CAF501 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70377AAA1918450100CAF501 /* MobileCoreServices.framework */; };
75A5E31037A6F0E5677F3B5C /* Pods_SessionNotificationServiceExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62ED73E38E0EC8506A9131AD /* Pods_SessionNotificationServiceExtension.framework */; };
@ -1122,7 +1121,6 @@
4CA485BA2232339F004B9E7D /* PhotoCaptureViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoCaptureViewController.swift; sourceTree = "<group>"; };
4CC1ECFA211A553000CC13BE /* AppUpdateNag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateNag.swift; sourceTree = "<group>"; };
4CC613352227A00400E21A3A /* ConversationSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationSearch.swift; sourceTree = "<group>"; };
4CFE6B6B21F92BA700006701 /* LegacyNotificationsAdaptee.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyNotificationsAdaptee.swift; sourceTree = "<group>"; };
53D547348A367C8A14D37FC0 /* Pods_SignalUtilitiesKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalUtilitiesKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5F3070F3395081DD0EB4F933 /* Pods-SignalUtilitiesKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalUtilitiesKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalUtilitiesKit/Pods-SignalUtilitiesKit.debug.xcconfig"; sourceTree = "<group>"; };
62ED73E38E0EC8506A9131AD /* Pods_SessionNotificationServiceExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SessionNotificationServiceExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -2923,7 +2921,6 @@
45CD81EE1DC030E7004C9430 /* SyncPushTokensJob.swift */,
451A13B01E13DED2000A50FD /* AppNotifications.swift */,
450DF2081E0DD2C6003D14BE /* UserNotificationsAdaptee.swift */,
4CFE6B6B21F92BA700006701 /* LegacyNotificationsAdaptee.swift */,
);
path = Notifications;
sourceTree = "<group>";
@ -4957,7 +4954,6 @@
B85357C323A1BD1200AAF6CD /* SeedVC.swift in Sources */,
45B5360E206DD8BB00D61655 /* UIResponder+OWS.swift in Sources */,
B8D84ECF25E3108A005A043E /* ExpandingAttachmentsButton.swift in Sources */,
4CFE6B6C21F92BA700006701 /* LegacyNotificationsAdaptee.swift in Sources */,
B8CCF6432397711F0091D419 /* SettingsVC.swift in Sources */,
C354E75A23FE2A7600CE22E3 /* BaseVC.swift in Sources */,
C32C5D40256DD51E003C73A2 /* Storage+VolumeSamples.swift in Sources */,

@ -143,6 +143,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
appDelegate.startPollerIfNeeded()
appDelegate.startClosedGroupPollerIfNeeded()
appDelegate.startOpenGroupPollersIfNeeded()
appDelegate.syncConfigurationIfNeeded()
}
// Onion request path countries cache
DispatchQueue.global(qos: .utility).async {

@ -118,11 +118,6 @@ static NSTimeInterval launchStartedAt;
return AppEnvironment.shared.userNotificationActionHandler;
}
- (OWSLegacyNotificationActionHandler *)legacyNotificationActionHandler
{
return AppEnvironment.shared.legacyNotificationActionHandler;
}
#pragma mark - Lifecycle
- (void)applicationDidEnterBackground:(UIApplication *)application
@ -641,19 +636,6 @@ static NSTimeInterval launchStartedAt;
#endif
}
- (void)application:(UIApplication *)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
return;
}
[self.notificationPresenter didRegisterLegacyNotificationSettings];
}
- (void)clearAllNotificationsAndRestoreBadgeCount
{
OWSAssertIsOnMainThread();

@ -37,26 +37,11 @@ import SignalUtilitiesKit
@objc
public var backup: OWSBackup
private var _legacyNotificationActionHandler: LegacyNotificationActionHandler
@objc
public var legacyNotificationActionHandler: LegacyNotificationActionHandler {
get {
if #available(iOS 10, *) {
owsFailDebug("shouldn't user legacyNotificationActionHandler on modern iOS")
}
return _legacyNotificationActionHandler
}
set {
_legacyNotificationActionHandler = newValue
}
}
// Stored properties cannot be marked as `@available`, only classes and functions.
// Instead, store a private `Any` and wrap it with a public `@available` getter
private var _userNotificationActionHandler: Any?
@objc
@available(iOS 10.0, *)
public var userNotificationActionHandler: UserNotificationActionHandler {
return _userNotificationActionHandler as! UserNotificationActionHandler
}
@ -70,10 +55,7 @@ import SignalUtilitiesKit
self.pushRegistrationManager = PushRegistrationManager()
self.backup = OWSBackup()
self.backupLazyRestore = BackupLazyRestore()
if #available(iOS 10.0, *) {
self._userNotificationActionHandler = UserNotificationActionHandler()
}
self._legacyNotificationActionHandler = LegacyNotificationActionHandler()
self._userNotificationActionHandler = UserNotificationActionHandler()
super.init()

@ -105,11 +105,7 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
@objc
public override init() {
if #available(iOS 10, *) {
self.adaptee = UserNotificationPresenterAdaptee()
} else {
self.adaptee = LegacyNotificationPresenterAdaptee()
}
self.adaptee = UserNotificationPresenterAdaptee()
super.init()
@ -135,25 +131,6 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
// MARK: -
// It is not safe to assume push token requests will be acknowledged until the user has
// registered their notification settings.
//
// e.g. in the case that Background Fetch is disabled, token requests will be ignored until
// we register user notification settings.
//
// For modern UNUserNotificationSettings, the registration takes a callback, so "waiting" for
// notification settings registration is straight forward, however for legacy UIUserNotification
// settings, the settings request is confirmed in the AppDelegate, where we call this method
// to inform the adaptee it's safe to proceed.
@objc
public func didRegisterLegacyNotificationSettings() {
guard let legacyAdaptee = adaptee as? LegacyNotificationPresenterAdaptee else {
owsFailDebug("unexpected notifications adaptee: \(adaptee)")
return
}
legacyAdaptee.didRegisterUserNotificationSettings()
}
@objc
func handleMessageRead(notification: Notification) {
AssertIsOnMainThread()

@ -1,280 +0,0 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
import PromiseKit
struct LegacyNotificationConfig {
static var allNotificationCategories: Set<UIUserNotificationCategory> {
let categories = AppNotificationCategory.allCases.map { notificationCategory($0) }
return Set(categories)
}
static func notificationActions(for category: AppNotificationCategory) -> [UIUserNotificationAction] {
return category.actions.map { notificationAction($0) }
}
static func notificationAction(_ action: AppNotificationAction) -> UIUserNotificationAction {
switch action {
case .markAsRead:
let mutableAction = UIMutableUserNotificationAction()
mutableAction.identifier = action.identifier
mutableAction.title = MessageStrings.markAsReadNotificationAction
mutableAction.activationMode = .background
mutableAction.isDestructive = false
mutableAction.isAuthenticationRequired = false
return mutableAction
case .reply:
let mutableAction = UIMutableUserNotificationAction()
mutableAction.identifier = action.identifier
mutableAction.title = MessageStrings.replyNotificationAction
mutableAction.activationMode = .background
mutableAction.isDestructive = false
mutableAction.isAuthenticationRequired = false
mutableAction.behavior = .textInput
return mutableAction
case .showThread:
let mutableAction = UIMutableUserNotificationAction()
mutableAction.identifier = action.identifier
mutableAction.title = CallStrings.showThreadButtonTitle
mutableAction.activationMode = .foreground
mutableAction.isDestructive = false
mutableAction.isAuthenticationRequired = true
return mutableAction
}
}
static func action(identifier: String) -> AppNotificationAction? {
return AppNotificationAction.allCases.first { notificationAction($0).identifier == identifier }
}
static func notificationActions(category: AppNotificationCategory) -> [UIUserNotificationAction] {
return category.actions.map { notificationAction($0) }
}
static func notificationCategory(_ category: AppNotificationCategory) -> UIUserNotificationCategory {
let notificationCategory = UIMutableUserNotificationCategory()
notificationCategory.identifier = category.identifier
let actions = notificationActions(category: category)
notificationCategory.setActions(actions, for: .minimal)
notificationCategory.setActions(actions, for: .default)
return notificationCategory
}
}
class LegacyNotificationPresenterAdaptee {
private var notifications: [String: UILocalNotification] = [:]
private var userNotificationSettingsPromise: Promise<Void>?
private var userNotificationSettingsResolver: Resolver<Void>?
// Notification registration is confirmed via AppDelegate
// Before this occurs, it is not safe to assume push token requests will be acknowledged.
//
// e.g. in the case that Background Fetch is disabled, token requests will be ignored until
// we register user notification settings.
@objc
public func didRegisterUserNotificationSettings() {
AssertIsOnMainThread()
guard let userNotificationSettingsResolver = self.userNotificationSettingsResolver else {
owsFailDebug("promise completion in \(#function) unexpectedly nil")
return
}
userNotificationSettingsResolver.fulfill(())
}
}
extension LegacyNotificationPresenterAdaptee: NotificationPresenterAdaptee {
func registerNotificationSettings() -> Promise<Void> {
AssertIsOnMainThread()
Logger.debug("")
guard self.userNotificationSettingsPromise == nil else {
let promise = self.userNotificationSettingsPromise!
Logger.info("already registered user notification settings")
return promise
}
let (promise, resolver) = Promise<Void>.pending()
self.userNotificationSettingsPromise = promise
self.userNotificationSettingsResolver = resolver
let settings = UIUserNotificationSettings(types: [.alert, .sound, .badge],
categories: LegacyNotificationConfig.allNotificationCategories)
UIApplication.shared.registerUserNotificationSettings(settings)
return promise
}
func notify(category: AppNotificationCategory, title: String?, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?) {
AssertIsOnMainThread()
notify(category: category, title: title, body: body, userInfo: userInfo, sound: sound, replacingIdentifier: nil)
}
func notify(category: AppNotificationCategory, title: String?, body: String, userInfo: [AnyHashable: Any], sound: OWSSound?, replacingIdentifier: String?) {
AssertIsOnMainThread()
guard UIApplication.shared.applicationState != .active else {
if let sound = sound {
let soundId = OWSSounds.systemSoundID(for: sound, quiet: true)
// Vibrate, respect silent switch, respect "Alert" volume, not media volume.
AudioServicesPlayAlertSound(soundId)
}
return
}
let alertBody: String
if let title = title {
// TODO - Make this a format string for better l10n
alertBody = title.rtlSafeAppend(":").rtlSafeAppend(" ").rtlSafeAppend(body)
} else {
alertBody = body
}
let notification = UILocalNotification()
notification.category = category.identifier
notification.alertBody = alertBody.filterForDisplay
notification.userInfo = userInfo
notification.soundName = sound?.filename
var notificationIdentifier: String = UUID().uuidString
if let replacingIdentifier = replacingIdentifier {
notificationIdentifier = replacingIdentifier
Logger.debug("replacing notification with identifier: \(notificationIdentifier)")
cancelNotification(identifier: notificationIdentifier)
}
let checkForCancel = category == .incomingMessage
if checkForCancel {
assert(userInfo[AppNotificationUserInfoKey.threadId] != nil)
notification.fireDate = Date(timeIntervalSinceNow: kNotificationDelayForRemoteRead)
notification.timeZone = NSTimeZone.local
}
Logger.debug("presenting notification with identifier: \(notificationIdentifier)")
UIApplication.shared.scheduleLocalNotification(notification)
notifications[notificationIdentifier] = notification
}
func cancelNotification(_ notification: UILocalNotification) {
AssertIsOnMainThread()
UIApplication.shared.cancelLocalNotification(notification)
}
func cancelNotification(identifier: String) {
AssertIsOnMainThread()
guard let notification = notifications.removeValue(forKey: identifier) else {
Logger.debug("no notification to cancel with identifier: \(identifier)")
return
}
cancelNotification(notification)
}
func cancelNotifications(threadId: String) {
AssertIsOnMainThread()
for notification in notifications.values {
guard let notificationThreadId = notification.userInfo?[AppNotificationUserInfoKey.threadId] as? String else {
continue
}
guard notificationThreadId == threadId else {
continue
}
cancelNotification(notification)
}
}
func clearAllNotifications() {
AssertIsOnMainThread()
for (_, notification) in notifications {
cancelNotification(notification)
}
type(of: self).clearExistingNotifications()
}
public class func clearExistingNotifications() {
// This will cancel all "scheduled" local notifications that haven't
// been presented yet.
UIApplication.shared.cancelAllLocalNotifications()
// To clear all already presented local notifications, we need to
// set the app badge number to zero after setting it to a non-zero value.
UIApplication.shared.applicationIconBadgeNumber = 1
UIApplication.shared.applicationIconBadgeNumber = 0
}
}
@objc(OWSLegacyNotificationActionHandler)
public class LegacyNotificationActionHandler: NSObject {
@objc
public static let kDefaultActionIdentifier = "LegacyNotificationActionHandler.kDefaultActionIdentifier"
var actionHandler: NotificationActionHandler {
return NotificationActionHandler.shared
}
@objc
func handleNotificationResponse(actionIdentifier: String,
notification: UILocalNotification,
responseInfo: [AnyHashable: Any],
completionHandler: @escaping () -> Void) {
firstly {
try handleNotificationResponse(actionIdentifier: actionIdentifier, notification: notification, responseInfo: responseInfo)
}.done {
completionHandler()
}.catch { error in
completionHandler()
owsFailDebug("error: \(error)")
Logger.error("error: \(error)")
}.retainUntilComplete()
}
func handleNotificationResponse(actionIdentifier: String,
notification: UILocalNotification,
responseInfo: [AnyHashable: Any]) throws -> Promise<Void> {
assert(AppReadiness.isAppReady())
let userInfo = notification.userInfo ?? [:]
switch actionIdentifier {
case type(of: self).kDefaultActionIdentifier:
Logger.debug("default action")
return try actionHandler.showThread(userInfo: userInfo)
default:
// proceed
break
}
guard let action = LegacyNotificationConfig.action(identifier: actionIdentifier) else {
throw NotificationError.failDebug("unable to find action for actionIdentifier: \(actionIdentifier)")
}
switch action {
case .markAsRead:
return try actionHandler.markAsRead(userInfo: userInfo)
case .reply:
guard let replyText = responseInfo[UIUserNotificationActionResponseTypedTextKey] as? String else {
throw NotificationError.failDebug("replyText was unexpectedly nil")
}
return try actionHandler.reply(userInfo: userInfo, replyText: replyText)
case .showThread:
return try actionHandler.showThread(userInfo: userInfo)
}
}
}
extension OWSSound {
var filename: String? {
return OWSSounds.filename(for: self, quiet: false)
}
}

@ -6,7 +6,6 @@ import Foundation
import UserNotifications
import PromiseKit
@available(iOS 10.0, *)
class UserNotificationConfig {
class var allNotificationCategories: Set<UNNotificationCategory> {
@ -50,7 +49,6 @@ class UserNotificationConfig {
}
@available(iOS 10.0, *)
class UserNotificationPresenterAdaptee: NSObject, UNUserNotificationCenterDelegate {
private let notificationCenter: UNUserNotificationCenter
@ -64,7 +62,6 @@ class UserNotificationPresenterAdaptee: NSObject, UNUserNotificationCenterDelega
}
}
@available(iOS 10.0, *)
extension UserNotificationPresenterAdaptee: NotificationPresenterAdaptee {
func registerNotificationSettings() -> Promise<Void> {
@ -170,7 +167,6 @@ extension UserNotificationPresenterAdaptee: NotificationPresenterAdaptee {
AssertIsOnMainThread()
notificationCenter.removeAllPendingNotificationRequests()
notificationCenter.removeAllDeliveredNotifications()
LegacyNotificationPresenterAdaptee.clearExistingNotifications()
}
func shouldPresentNotification(category: AppNotificationCategory, userInfo: [AnyHashable: Any]) -> Bool {
@ -198,7 +194,6 @@ extension UserNotificationPresenterAdaptee: NotificationPresenterAdaptee {
}
@objc(OWSUserNotificationActionHandler)
@available(iOS 10.0, *)
public class UserNotificationActionHandler: NSObject {
var actionHandler: NotificationActionHandler {
@ -258,7 +253,7 @@ public class UserNotificationActionHandler: NSObject {
}
extension OWSSound {
@available(iOS 10.0, *)
func notificationSound(isQuiet: Bool) -> UNNotificationSound {
guard let filename = OWSSounds.filename(for: self, quiet: isQuiet) else {
owsFailDebug("filename was unexpectedly nil")

@ -59,10 +59,10 @@ final class LandingVC : BaseVC {
// Link button container
let linkButtonContainer = UIView()
linkButtonContainer.set(.height, to: Values.onboardingButtonBottomOffset)
// linkButtonContainer.addSubview(linkButton)
// linkButton.center(.horizontal, in: linkButtonContainer)
// let isIPhoneX = (UIApplication.shared.keyWindow!.safeAreaInsets.bottom > 0)
// linkButton.centerYAnchor.constraint(equalTo: linkButtonContainer.centerYAnchor, constant: isIPhoneX ? -4 : 0).isActive = true
linkButtonContainer.addSubview(linkButton)
linkButton.center(.horizontal, in: linkButtonContainer)
let isIPhoneX = (UIApplication.shared.keyWindow!.safeAreaInsets.bottom > 0)
linkButton.centerYAnchor.constraint(equalTo: linkButtonContainer.centerYAnchor, constant: isIPhoneX ? -4 : 0).isActive = true
// Button stack view
let buttonStackView = UIStackView(arrangedSubviews: [ registerButton, restoreButton ])
buttonStackView.axis = .vertical

@ -125,35 +125,20 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon
let (ed25519KeyPair, x25519KeyPair) = KeyPairUtilities.generate(from: seed)
Onboarding.Flow.link.preregister(with: seed, ed25519KeyPair: ed25519KeyPair, x25519KeyPair: x25519KeyPair)
TSAccountManager.sharedInstance().didRegister()
NotificationCenter.default.addObserver(self, selector: #selector(handleConfigurationMessageReceived), name: .configurationMessageReceived, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleInitialConfigurationMessageReceived), name: .initialConfigurationMessageReceived, object: nil)
ModalActivityIndicatorViewController.present(fromViewController: navigationController!) { [weak self] modal in
self?.activityIndicatorModal = modal
}
}
@objc private func handleConfigurationMessageReceived(_ notification: Notification) {
guard let profile = notification.object as? [String:Any?], let displayName = profile["displayName"] as? String else { return }
let profilePictureURL = profile["profilePictureURL"] as? String
let profileKeyAsData = profile["profileKey"] as? NSData
let profileKey = given(profileKeyAsData) { OWSAES256Key(data: $0 as Data)! }
@objc private func handleInitialConfigurationMessageReceived(_ notification: Notification) {
TSAccountManager.sharedInstance().phoneNumberAwaitingVerification = OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey
let profileManager = OWSProfileManager.shared()
var userProfile: OWSUserProfile!
Storage.write(with: { transaction in
userProfile = profileManager.getLocalUserProfile(with: transaction)
userProfile.profileName = displayName
userProfile.avatarUrlPath = profilePictureURL
userProfile.profileKey = profileKey
userProfile.save(with: transaction)
}, completion: {
profileManager.downloadAvatar(for: userProfile)
DispatchQueue.main.async {
self.navigationController!.dismiss(animated: true) {
let pnModeVC = PNModeVC()
self.navigationController!.setViewControllers([ pnModeVC ], animated: true)
}
DispatchQueue.main.async {
self.navigationController!.dismiss(animated: true) {
let pnModeVC = PNModeVC()
self.navigationController!.setViewControllers([ pnModeVC ], animated: true)
}
})
}
}
}

@ -14,11 +14,17 @@ enum Onboarding {
case .register:
userDefaults[.hasViewedSeed] = false
restorationTime = 0
userDefaults[.hasSyncedConfiguration] = true
userDefaults[.hasSyncedInitialConfiguration] = true
case .recover, .link:
userDefaults[.hasViewedSeed] = true
restorationTime = Date().timeIntervalSince1970
userDefaults[.hasSyncedConfiguration] = false
userDefaults[.hasSyncedInitialConfiguration] = false
}
switch self {
case .register, .recover:
userDefaults[.lastDisplayNameUpdate] = Date()
userDefaults[.lastProfilePictureUpdate] = Date()
case .link: break
}
OWSPrimaryStorage.shared().setRestorationTime(restorationTime)
}

@ -296,10 +296,19 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
}
private func updateProfile(isUpdatingDisplayName: Bool, isUpdatingProfilePicture: Bool) {
let userDefaults = UserDefaults.standard
let displayName = displayNameToBeUploaded ?? OWSProfileManager.shared().profileNameForRecipient(withID: getUserHexEncodedPublicKey())
let profilePicture = profilePictureToBeUploaded ?? OWSProfileManager.shared().profileAvatar(forRecipientId: getUserHexEncodedPublicKey())
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] modalActivityIndicator in
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self, displayNameToBeUploaded, profilePictureToBeUploaded] modalActivityIndicator in
OWSProfileManager.shared().updateLocalProfileName(displayName, avatarImage: profilePicture, success: {
if displayNameToBeUploaded != nil {
userDefaults[.lastDisplayNameUpdate] = Date()
}
if profilePictureToBeUploaded != nil {
userDefaults[.lastProfilePictureUpdate] = Date()
}
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete()
DispatchQueue.main.async {
modalActivityIndicator.dismiss {
guard let self = self else { return }

@ -51,7 +51,6 @@ enum NotificationHapticFeedbackType {
case error, success, warning
}
@available(iOS 10.0, *)
extension NotificationHapticFeedbackType {
var uiNotificationFeedbackType: UINotificationFeedbackGenerator.FeedbackType {
switch self {
@ -71,11 +70,7 @@ class NotificationHapticFeedback: NotificationHapticFeedbackAdapter {
let adapter: NotificationHapticFeedbackAdapter
init() {
if #available(iOS 10, *) {
adapter = ModernNotificationHapticFeedbackAdapter()
} else {
adapter = LegacyNotificationHapticFeedbackAdapter()
}
adapter = ModernNotificationHapticFeedbackAdapter()
}
func notificationOccurred(_ notificationType: NotificationHapticFeedbackType) {
@ -83,7 +78,6 @@ class NotificationHapticFeedback: NotificationHapticFeedbackAdapter {
}
}
@available(iOS 10.0, *)
class ModernNotificationHapticFeedbackAdapter: NotificationHapticFeedbackAdapter {
let feedbackGenerator = UINotificationFeedbackGenerator()
@ -96,13 +90,3 @@ class ModernNotificationHapticFeedbackAdapter: NotificationHapticFeedbackAdapter
feedbackGenerator.prepare()
}
}
class LegacyNotificationHapticFeedbackAdapter: NotificationHapticFeedbackAdapter {
func notificationOccurred(_ notificationType: NotificationHapticFeedbackType) {
vibrate()
}
private func vibrate() {
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
}
}

@ -143,24 +143,48 @@ extension MessageReceiver {
}
private static func handleConfigurationMessage(_ message: ConfigurationMessage, using transaction: Any) {
guard message.sender == getUserHexEncodedPublicKey(), !UserDefaults.standard[.hasSyncedConfiguration] else { return }
guard message.sender == getUserHexEncodedPublicKey() else { return }
let storage = SNMessagingKitConfiguration.shared.storage
// Notification
UserDefaults.standard[.hasSyncedConfiguration] = true
let profile: [String:Any?] = [ "displayName" : message.displayName, "profilePictureURL" : message.profilePictureURL, "profileKey" : message.profileKey ]
NotificationCenter.default.post(name: .configurationMessageReceived, object: profile)
// Closed groups
let allClosedGroupPublicKeys = storage.getUserClosedGroupPublicKeys()
for closedGroup in message.closedGroups {
guard !allClosedGroupPublicKeys.contains(closedGroup.publicKey) else { continue }
handleNewClosedGroup(groupPublicKey: closedGroup.publicKey, name: closedGroup.name, encryptionKeyPair: closedGroup.encryptionKeyPair,
members: [String](closedGroup.members), admins: [String](closedGroup.admins), messageSentTimestamp: message.sentTimestamp!, using: transaction)
}
// Open groups
let allOpenGroups = Set(storage.getAllUserOpenGroups().keys)
for openGroupURL in message.openGroups {
guard !allOpenGroups.contains(openGroupURL) else { continue }
OpenGroupManager.shared.add(with: openGroupURL, using: transaction).retainUntilComplete()
let transaction = transaction as! YapDatabaseReadWriteTransaction
let userDefaults = UserDefaults.standard
// Profile
let userProfile = storage.getUserProfile(using: transaction)
if let displayName = message.displayName {
let shouldUpdate = given(userDefaults[.lastDisplayNameUpdate]) { message.sentTimestamp! > UInt64($0.timeIntervalSince1970 * 1000) } ?? true
if shouldUpdate {
userProfile.profileName = displayName
userDefaults[.lastDisplayNameUpdate] = Date(timeIntervalSince1970: TimeInterval(message.sentTimestamp! / 1000))
}
}
if let profilePictureURL = message.profilePictureURL, let profileKeyAsData = message.profileKey {
let shouldUpdate = given(userDefaults[.lastProfilePictureUpdate]) { message.sentTimestamp! > UInt64($0.timeIntervalSince1970 * 1000) } ?? true
if shouldUpdate {
userProfile.avatarUrlPath = profilePictureURL
userProfile.profileKey = OWSAES256Key(data: profileKeyAsData)
userDefaults[.lastProfilePictureUpdate] = Date(timeIntervalSince1970: TimeInterval(message.sentTimestamp! / 1000))
}
}
userProfile.save(with: transaction)
transaction.addCompletionQueue(DispatchQueue.main) {
SSKEnvironment.shared.profileManager.downloadAvatar(for: userProfile)
}
// Initial configuration sync
if !UserDefaults.standard[.hasSyncedInitialConfiguration] {
UserDefaults.standard[.hasSyncedInitialConfiguration] = true
NotificationCenter.default.post(name: .initialConfigurationMessageReceived, object: nil)
// Closed groups
let allClosedGroupPublicKeys = storage.getUserClosedGroupPublicKeys()
for closedGroup in message.closedGroups {
guard !allClosedGroupPublicKeys.contains(closedGroup.publicKey) else { continue }
handleNewClosedGroup(groupPublicKey: closedGroup.publicKey, name: closedGroup.name, encryptionKeyPair: closedGroup.encryptionKeyPair,
members: [String](closedGroup.members), admins: [String](closedGroup.admins), messageSentTimestamp: message.sentTimestamp!, using: transaction)
}
// Open groups
let allOpenGroups = Set(storage.getAllUserOpenGroups().keys)
for openGroupURL in message.openGroups {
guard !allOpenGroups.contains(openGroupURL) else { continue }
OpenGroupManager.shared.add(with: openGroupURL, using: transaction).retainUntilComplete()
}
}
}

@ -1,10 +1,10 @@
public extension Notification.Name {
static let configurationMessageReceived = Notification.Name("configurationMessageReceived")
static let initialConfigurationMessageReceived = Notification.Name("initialConfigurationMessageReceived")
}
@objc public extension NSNotification {
@objc static let configurationMessageReceived = Notification.Name.configurationMessageReceived.rawValue as NSString
@objc static let initialConfigurationMessageReceived = Notification.Name.initialConfigurationMessageReceived.rawValue as NSString
}

@ -19,6 +19,7 @@ public protocol SessionMessagingKitStorageProtocol {
func getUserDisplayName() -> String?
func getUserProfileKey() -> Data?
func getUserProfilePictureURL() -> String?
func getUserProfile(using transaction: Any) -> OWSUserProfile
// MARK: - Closed Groups

@ -29,6 +29,8 @@ NS_ASSUME_NONNULL_BEGIN
- (void)ensureLocalProfileCached;
- (void)ensureProfileCachedForContactWithID:(NSString *)contactID with:(YapDatabaseReadWriteTransaction *)transaction;
- (void)downloadAvatarForUserProfile:(OWSUserProfile *)userProfile;
@end
NS_ASSUME_NONNULL_END

@ -3,7 +3,7 @@ import Foundation
public enum SNUserDefaults {
public enum Bool : Swift.String {
case hasSyncedConfiguration
case hasSyncedInitialConfiguration = "hasSyncedConfiguration"
case hasViewedSeed
case hasSeenLinkPreviewSuggestion
case isUsingFullAPNs
@ -13,6 +13,8 @@ public enum SNUserDefaults {
public enum Date : Swift.String {
case lastProfilePictureUpload
case lastConfigurationSync
case lastDisplayNameUpdate
case lastProfilePictureUpdate
}
public enum Double : Swift.String {

@ -5,4 +5,8 @@ extension Storage : SessionMessagingKitStorageProtocol, SessionSnodeKitStoragePr
let transaction = transaction as! YapDatabaseReadWriteTransaction
OWSPrimaryStorage.shared().updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, in: transaction)
}
public func getUserProfile(using transaction: Any) -> OWSUserProfile {
return OWSProfileManager.shared().getLocalUserProfile(with: transaction as! YapDatabaseReadWriteTransaction)
}
}

Loading…
Cancel
Save