mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
11 KiB
Swift
288 lines
11 KiB
Swift
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
|
|
|
import Foundation
|
|
import GRDB
|
|
import SessionUtil
|
|
import SessionUtilitiesKit
|
|
|
|
// MARK: - LibSession
|
|
|
|
internal extension LibSession {
|
|
static let columnsRelatedToUserProfile: [Profile.Columns] = [
|
|
Profile.Columns.name,
|
|
Profile.Columns.profilePictureUrl,
|
|
Profile.Columns.profileEncryptionKey
|
|
]
|
|
|
|
static let syncedSettings: [String] = [
|
|
Setting.BoolKey.checkForCommunityMessageRequests.rawValue
|
|
]
|
|
}
|
|
|
|
// MARK: - LibSessionCacheType
|
|
|
|
public extension LibSessionCacheType {
|
|
var userProfileDisplayName: String {
|
|
guard
|
|
case .userProfile(let conf) = config(for: .userProfile, sessionId: userSessionId),
|
|
let profileNamePtr: UnsafePointer<CChar> = user_profile_get_name(conf)
|
|
else { return "" }
|
|
|
|
return String(cString: profileNamePtr)
|
|
}
|
|
}
|
|
|
|
// MARK: - Incoming Changes
|
|
|
|
internal extension LibSessionCacheType {
|
|
func handleUserProfileUpdate(
|
|
_ db: Database,
|
|
in config: LibSession.Config?,
|
|
serverTimestampMs: Int64
|
|
) throws {
|
|
guard configNeedsDump(config) else { return }
|
|
guard case .userProfile(let conf) = config else { throw LibSessionError.invalidConfigObject }
|
|
|
|
// A profile must have a name so if this is null then it's invalid and can be ignored
|
|
guard let profileNamePtr: UnsafePointer<CChar> = user_profile_get_name(conf) else { return }
|
|
|
|
let userSessionId: SessionId = dependencies[cache: .general].sessionId
|
|
let profileName: String = String(cString: profileNamePtr)
|
|
let profilePic: user_profile_pic = user_profile_get_pic(conf)
|
|
let profilePictureUrl: String? = profilePic.get(\.url, nullIfEmpty: true)
|
|
|
|
// Handle user profile changes
|
|
try Profile.updateIfNeeded(
|
|
db,
|
|
publicKey: userSessionId.hexString,
|
|
displayNameUpdate: .currentUserUpdate(profileName),
|
|
displayPictureUpdate: {
|
|
guard let profilePictureUrl: String = profilePictureUrl else { return .currentUserRemove }
|
|
|
|
return .currentUserUpdateTo(
|
|
url: profilePictureUrl,
|
|
key: profilePic.get(\.key),
|
|
fileName: nil
|
|
)
|
|
}(),
|
|
sentTimestamp: TimeInterval(Double(serverTimestampMs) / 1000),
|
|
using: dependencies
|
|
)
|
|
|
|
// Update the 'Note to Self' visibility and priority
|
|
let threadInfo: LibSession.PriorityVisibilityInfo? = try? SessionThread
|
|
.filter(id: userSessionId.hexString)
|
|
.select(.id, .variant, .pinnedPriority, .shouldBeVisible)
|
|
.asRequest(of: LibSession.PriorityVisibilityInfo.self)
|
|
.fetchOne(db)
|
|
let targetPriority: Int32 = user_profile_get_nts_priority(conf)
|
|
|
|
// Create the 'Note to Self' thread if it doesn't exist
|
|
if let threadInfo: LibSession.PriorityVisibilityInfo = threadInfo {
|
|
let threadChanges: [ConfigColumnAssignment] = [
|
|
((threadInfo.shouldBeVisible == LibSession.shouldBeVisible(priority: targetPriority)) ? nil :
|
|
SessionThread.Columns.shouldBeVisible.set(to: LibSession.shouldBeVisible(priority: targetPriority))
|
|
),
|
|
(threadInfo.pinnedPriority == targetPriority ? nil :
|
|
SessionThread.Columns.pinnedPriority.set(to: targetPriority)
|
|
)
|
|
].compactMap { $0 }
|
|
|
|
if !threadChanges.isEmpty {
|
|
try SessionThread
|
|
.filter(id: userSessionId.hexString)
|
|
.updateAllAndConfig(
|
|
db,
|
|
threadChanges,
|
|
using: dependencies
|
|
)
|
|
}
|
|
}
|
|
else {
|
|
// If the 'Note to Self' conversation is hidden then we should trigger the proper
|
|
// `deleteOrLeave` behaviour
|
|
if !LibSession.shouldBeVisible(priority: targetPriority) {
|
|
try SessionThread.deleteOrLeave(
|
|
db,
|
|
type: .hideContactConversation,
|
|
threadId: userSessionId.hexString,
|
|
threadVariant: .contact,
|
|
using: dependencies
|
|
)
|
|
}
|
|
else {
|
|
try SessionThread.upsert(
|
|
db,
|
|
id: userSessionId.hexString,
|
|
variant: .contact,
|
|
values: SessionThread.TargetValues(
|
|
shouldBeVisible: .setTo(LibSession.shouldBeVisible(priority: targetPriority)),
|
|
pinnedPriority: .setTo(targetPriority)
|
|
),
|
|
using: dependencies
|
|
)
|
|
}
|
|
}
|
|
|
|
// Update the 'Note to Self' disappearing messages configuration
|
|
let targetExpiry: Int32 = user_profile_get_nts_expiry(conf)
|
|
let targetIsEnable: Bool = targetExpiry > 0
|
|
let targetConfig: DisappearingMessagesConfiguration = DisappearingMessagesConfiguration(
|
|
threadId: userSessionId.hexString,
|
|
isEnabled: targetIsEnable,
|
|
durationSeconds: TimeInterval(targetExpiry),
|
|
type: targetIsEnable ? .disappearAfterSend : .unknown
|
|
)
|
|
let localConfig: DisappearingMessagesConfiguration = try DisappearingMessagesConfiguration
|
|
.fetchOne(db, id: userSessionId.hexString)
|
|
.defaulting(to: DisappearingMessagesConfiguration.defaultWith(userSessionId.hexString))
|
|
|
|
if targetConfig != localConfig {
|
|
try targetConfig
|
|
.saved(db)
|
|
.clearUnrelatedControlMessages(
|
|
db,
|
|
threadVariant: .contact,
|
|
using: dependencies
|
|
)
|
|
}
|
|
|
|
// Update settings if needed
|
|
let updatedAllowBlindedMessageRequests: Int32 = user_profile_get_blinded_msgreqs(conf)
|
|
let updatedAllowBlindedMessageRequestsBoolValue: Bool = (updatedAllowBlindedMessageRequests >= 1)
|
|
|
|
if
|
|
updatedAllowBlindedMessageRequests >= 0 &&
|
|
updatedAllowBlindedMessageRequestsBoolValue != db[.checkForCommunityMessageRequests]
|
|
{
|
|
db[.checkForCommunityMessageRequests] = updatedAllowBlindedMessageRequestsBoolValue
|
|
}
|
|
|
|
// Create a contact for the current user if needed (also force-approve the current user
|
|
// in case the account got into a weird state or restored directly from a migration)
|
|
let userContact: Contact = Contact.fetchOrCreate(db, id: userSessionId.hexString, using: dependencies)
|
|
|
|
if !userContact.isTrusted || !userContact.isApproved || !userContact.didApproveMe {
|
|
try userContact.upsert(db)
|
|
try Contact
|
|
.filter(id: userSessionId.hexString)
|
|
.updateAllAndConfig(
|
|
db,
|
|
Contact.Columns.isTrusted.set(to: true), // Always trust the current user
|
|
Contact.Columns.isApproved.set(to: true),
|
|
Contact.Columns.didApproveMe.set(to: true),
|
|
using: dependencies
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Outgoing Changes
|
|
|
|
internal extension LibSession {
|
|
static func update(
|
|
profile: Profile,
|
|
in config: Config?
|
|
) throws {
|
|
try update(
|
|
profileInfo: ProfileInfo(
|
|
name: profile.name,
|
|
profilePictureUrl: profile.profilePictureUrl,
|
|
profileEncryptionKey: profile.profileEncryptionKey
|
|
),
|
|
in: config
|
|
)
|
|
}
|
|
|
|
static func update(
|
|
profileInfo: ProfileInfo,
|
|
in config: Config?
|
|
) throws {
|
|
guard case .userProfile(let conf) = config else { throw LibSessionError.invalidConfigObject }
|
|
|
|
// Update the name
|
|
var cUpdatedName: [CChar] = try profileInfo.name.cString(using: .utf8) ?? { throw LibSessionError.invalidCConversion }()
|
|
user_profile_set_name(conf, &cUpdatedName)
|
|
try LibSessionError.throwIfNeeded(conf)
|
|
|
|
// Either assign the updated profile pic, or sent a blank profile pic (to remove the current one)
|
|
var profilePic: user_profile_pic = user_profile_pic()
|
|
profilePic.set(\.url, to: profileInfo.profilePictureUrl)
|
|
profilePic.set(\.key, to: profileInfo.profileEncryptionKey)
|
|
user_profile_set_pic(conf, profilePic)
|
|
try LibSessionError.throwIfNeeded(conf)
|
|
}
|
|
|
|
static func updateNoteToSelf(
|
|
priority: Int32? = nil,
|
|
disappearingMessagesConfig: DisappearingMessagesConfiguration? = nil,
|
|
in config: Config?
|
|
) throws {
|
|
guard case .userProfile(let conf) = config else { throw LibSessionError.invalidConfigObject }
|
|
|
|
if let priority: Int32 = priority {
|
|
user_profile_set_nts_priority(conf, priority)
|
|
}
|
|
|
|
if let config: DisappearingMessagesConfiguration = disappearingMessagesConfig {
|
|
user_profile_set_nts_expiry(conf, Int32(config.durationSeconds))
|
|
}
|
|
}
|
|
|
|
static func updateSettings(
|
|
checkForCommunityMessageRequests: Bool? = nil,
|
|
in config: Config?
|
|
) throws {
|
|
guard case .userProfile(let conf) = config else { throw LibSessionError.invalidConfigObject }
|
|
|
|
if let blindedMessageRequests: Bool = checkForCommunityMessageRequests {
|
|
user_profile_set_blinded_msgreqs(conf, (blindedMessageRequests ? 1 : 0))
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - External Outgoing Changes
|
|
|
|
public extension LibSession {
|
|
static func updateNoteToSelf(
|
|
_ db: Database,
|
|
priority: Int32? = nil,
|
|
disappearingMessagesConfig: DisappearingMessagesConfiguration? = nil,
|
|
using dependencies: Dependencies
|
|
) throws {
|
|
try dependencies.mutate(cache: .libSession) { cache in
|
|
try cache.performAndPushChange(db, for: .userProfile, sessionId: dependencies[cache: .general].sessionId) { config in
|
|
try LibSession.updateNoteToSelf(
|
|
priority: priority,
|
|
disappearingMessagesConfig: disappearingMessagesConfig,
|
|
in: config
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Direct Values
|
|
|
|
extension LibSession {
|
|
static func rawBlindedMessageRequestValue(in config: Config?) throws -> Int32 {
|
|
guard case .userProfile(let conf) = config else { throw LibSessionError.invalidConfigObject }
|
|
|
|
return user_profile_get_blinded_msgreqs(conf)
|
|
}
|
|
}
|
|
|
|
// MARK: - ProfileInfo
|
|
|
|
public extension LibSession {
|
|
struct ProfileInfo {
|
|
let name: String
|
|
let profilePictureUrl: String?
|
|
let profileEncryptionKey: Data?
|
|
}
|
|
}
|
|
|
|
// MARK: - C Conformance
|
|
|
|
extension user_profile_pic: CAccessible & CMutable {}
|