Profile info update change, RadioButton bugfix, unit test changes

• Update group member profile info on invite acceptance
• Fixed a bug with the default RadioButton enabled state
• Added a few more unit tests
pull/894/head
Morgan Pretty 6 months ago
parent a6e39d930f
commit 8bd2468fb6

@ -84,6 +84,7 @@ public enum MentionUtilities {
guard let targetString: String = {
guard !isCurrentUser else { return "you".localized() }
// FIXME: This does a database query and is happening when populating UI - should try to refactor it somehow (ideally resolve a set of mentioned profiles as part of the database query)
guard let displayName: String = Profile.displayNameNoFallback(id: sessionId, threadVariant: threadVariant, using: dependencies) else {
lastMatchEnd = (match.range.location + match.range.length)
return nil

@ -229,11 +229,13 @@ internal extension LibSession {
memberId: String,
role: GroupMember.Role,
status: GroupMember.RoleStatus,
profile: Profile?,
using dependencies: Dependencies
) throws {
try dependencies.mutate(cache: .libSession) { cache in
try cache.performAndPushChange(db, for: .groupMembers, sessionId: groupSessionId) { config in
try LibSession.updateMemberStatus(memberId: memberId, role: role, status: status, in: config)
try LibSession.updateMemberProfile(memberId: memberId, profile: profile, in: config)
}
}
}
@ -266,6 +268,46 @@ internal extension LibSession {
try LibSessionError.throwIfNeeded(conf)
}
static func updateMemberProfile(
_ db: Database,
groupSessionId: SessionId,
memberId: String,
profile: Profile?,
using dependencies: Dependencies
) throws {
try dependencies.mutate(cache: .libSession) { cache in
try cache.performAndPushChange(db, for: .groupMembers, sessionId: groupSessionId) { config in
try LibSession.updateMemberProfile(memberId: memberId, profile: profile, in: config)
}
}
}
static func updateMemberProfile(
memberId: String,
profile: Profile?,
in config: Config?
) throws {
guard let profile: Profile = profile else { return }
guard case .object(let conf) = config else { throw LibSessionError.invalidConfigObject }
// Only update members if they already exist in the group
var cMemberId: [CChar] = try memberId.cString(using: .utf8) ?? { throw LibSessionError.invalidCConversion }()
var groupMember: config_group_member = config_group_member()
// If the member doesn't exist then do nothing
guard groups_members_get(conf, &groupMember, &cMemberId) else { return }
groupMember.set(\.name, to: profile.name)
if profile.profilePictureUrl != nil && profile.profileEncryptionKey != nil {
groupMember.set(\.profile_pic.url, to: profile.profilePictureUrl)
groupMember.set(\.profile_pic.key, to: profile.profileEncryptionKey)
}
groups_members_set(conf, &groupMember)
try? LibSessionError.throwIfNeeded(conf)
}
static func flagMembersForRemoval(
_ db: Database,
groupSessionId: SessionId,

@ -567,6 +567,16 @@ extension MessageReceiver {
db,
senderSessionId: sender,
groupSessionIdHexString: groupSessionId.hexString,
profile: message.profile.map { profile in
profile.displayName.map {
Profile(
id: sender,
name: $0,
profilePictureUrl: profile.profilePictureUrl,
profileEncryptionKey: profile.profileKey
)
}
},
using: dependencies
)
}
@ -976,6 +986,7 @@ extension MessageReceiver {
_ db: Database,
senderSessionId: String,
groupSessionIdHexString: String?,
profile: Profile?,
using dependencies: Dependencies
) throws {
// Only group admins can update the member approval state
@ -1005,6 +1016,7 @@ extension MessageReceiver {
memberId: senderSessionId,
role: .standard,
status: .accepted,
profile: profile,
using: dependencies
)
@ -1020,6 +1032,13 @@ extension MessageReceiver {
calledFromConfig: nil,
using: dependencies
)
try LibSession.updateMemberProfile(
db,
groupSessionId: groupSessionId,
memberId: senderSessionId,
profile: profile,
using: dependencies
)
default: break // Invalid cases
}

@ -378,6 +378,7 @@ extension MessageReceiver {
db,
senderSessionId: sender,
groupSessionIdHexString: thread.id,
profile: nil, // Don't update the profile in this case
using: dependencies
)

@ -620,6 +620,7 @@ extension MessageSender {
memberId: memberId,
role: .standard,
status: .notSentYet,
profile: nil,
using: dependencies
)
@ -786,6 +787,7 @@ extension MessageSender {
memberId: memberId,
role: .admin,
status: .notSentYet,
profile: nil,
using: dependencies
)

@ -1031,8 +1031,9 @@ class MessageReceiverGroupsSpec: QuickSpec {
// MARK: ---- updates the GROUP_KEYS state correctly
it("updates the GROUP_KEYS state correctly") {
// TODO: Should return a value????
mockCrypto.when { $0.generate(.ed25519KeyPair(seed: .any)) }.thenReturn(nil)
mockCrypto
.when { $0.generate(.ed25519KeyPair(seed: .any)) }
.thenReturn(KeyPair(publicKey: [1, 2, 3], secretKey: [4, 5, 6]))
mockStorage.write { db in
try MessageReceiver.handleGroupUpdateMessage(
@ -1986,8 +1987,8 @@ class MessageReceiverGroupsSpec: QuickSpec {
}
}
// MARK: ---- updates the profile information if provided
it("updates the profile information if provided") {
// MARK: ---- updates the profile information in the database if provided
it("updates the profile information in the database if provided") {
mockStorage.write { db in
try MessageReceiver.handleGroupUpdateMessage(
db,
@ -2129,6 +2130,28 @@ class MessageReceiverGroupsSpec: QuickSpec {
expect(groups_members_get(groupMembersConf, &groupMember, &cMemberId)).to(beTrue())
expect(groupMember.invited).to(equal(0))
}
// MARK: ---- updates the config member entry with profile information if provided
it("updates the config member entry with profile information if provided") {
mockStorage.write { db in
_ = try GroupMember.deleteAll(db)
}
mockStorage.write { db in
try MessageReceiver.handleGroupUpdateMessage(
db,
threadId: groupId.hexString,
threadVariant: .group,
message: inviteResponseMessage,
using: dependencies
)
}
var cMemberId: [CChar] = "051111111111111111111111111111111111111111111111111111111111111112".cString(using: .utf8)!
var groupMember: config_group_member = config_group_member()
expect(groups_members_get(groupMembersConf, &groupMember, &cMemberId)).to(beTrue())
expect(groupMember.get(\.name)).to(equal("TestOtherMember"))
}
}
}

@ -347,7 +347,7 @@ class ThreadSettingsViewModelSpec: QuickSpec {
let modal2: ConfirmationModal? = (screenTransitions.last?.destination as? ConfirmationModal)
expect(modal2?.info.title).to(equal("theError".localized()))
expect(modal2?.info.body).to(equal(.text("displayNameErrorDescriptionShorter".localized())))
expect(modal2?.info.body).to(equal(.text("nicknameErrorShorter".localized())))
expect(modal2?.info.confirmTitle).to(beNil())
expect(modal2?.info.cancelTitle).to(equal("okay".localized()))
}

@ -63,6 +63,46 @@ class DatabaseSpec: QuickSpec {
// MARK: - a Database
describe("a Database") {
// MARK: -- triggers the migration requirements in the expected order
it("triggers the migration requirements in the expected order") {
var migrationRequirementsSet: [MigrationRequirement] = []
mockStorage.perform(
migrationTargets: [
TestMigratableTarget.self
],
async: false,
onProgressUpdate: nil,
onMigrationRequirement: { [dependencies = dependencies!] db, requirement in
migrationRequirementsSet.append(requirement)
MigrationTest.handleRequirements(db, requirement: requirement, using: dependencies)
},
onComplete: { _, _ in }
)
expect(migrationRequirementsSet).to(equal([.sessionIdCached, .libSessionStateLoaded]))
}
// MARK: -- triggers all prior migration requirements if only a latter one is called
it("triggers all prior migration requirements if only a latter one is called") {
var migrationRequirementsSet: [MigrationRequirement] = []
mockStorage.perform(
migrationTargets: [
SNMessagingKit.self
],
async: false,
onProgressUpdate: nil,
onMigrationRequirement: { [dependencies = dependencies!] db, requirement in
migrationRequirementsSet.append(requirement)
MigrationTest.handleRequirements(db, requirement: requirement, using: dependencies)
},
onComplete: { _, _ in }
)
expect(migrationRequirementsSet).to(equal([.sessionIdCached, .libSessionStateLoaded]))
}
// MARK: -- can be created from an empty state
it("can be created from an empty state") {
mockStorage.perform(
@ -404,3 +444,29 @@ private class MigrationTest {
}
}
}
enum TestMigratableTarget: MigratableTarget { // Just to make the external API nice
public static func migrations() -> TargetMigrations {
return TargetMigrations(
identifier: .session,
migrations: [
[
TestRequiresLibSessionStateMigration.self
]
]
)
}
}
enum TestRequiresLibSessionStateMigration: Migration {
static let target: TargetMigrations.Identifier = .session
static let identifier: String = "test" // stringlint:disable
static let needsConfigSync: Bool = false
static let minExpectedRunDuration: TimeInterval = 0.1
static var requirements: [MigrationRequirement] = [.libSessionStateLoaded]
static let fetchedTables: [(TableRecord & FetchableRecord).Type] = []
static let createdOrAlteredTables: [(TableRecord & FetchableRecord).Type] = []
static let droppedTables: [(TableRecord & FetchableRecord).Type] = []
static func migrate(_ db: Database, using dependencies: Dependencies) throws {}
}

@ -36,7 +36,7 @@ public class RadioButton: UIView {
set { titleLabel.text = newValue }
}
public private(set) var isEnabled: Bool = false
public private(set) var isEnabled: Bool = true
public private(set) var isSelected: Bool = false
private let onSelected: ((RadioButton) -> ())?

@ -389,7 +389,7 @@ open class Storage {
// Remove any processed requirements from the list (don't want to process them multiple times)
unprocessedMigrationRequirements.mutate {
$0 = Array($0.asSet().subtracting(migration.requirements.asSet()))
$0 = Array($0.asSet().subtracting(unprocessedRequirements.asSet()))
}
}

Loading…
Cancel
Save