Skip to content

Commit

Permalink
feat: Consent Caching
Browse files Browse the repository at this point in the history
Added Caching for Consent List
Updated allow/deny calls to only call network once
  • Loading branch information
Alex Risch authored and Alex Risch committed Apr 9, 2024
1 parent bf1219f commit 3b3fb49
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 86 deletions.
145 changes: 72 additions & 73 deletions Sources/XMTPiOS/Contacts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class ConsentList {
var publicKey: Data
var privateKey: Data
var identifier: String?

var lastFetched: Date?
var client: Client

init(client: Client) {
Expand All @@ -55,17 +55,18 @@ public class ConsentList {
identifier = try? LibXMTP.generatePrivatePreferencesTopicIdentifier(privateKey: privateKey)
}

func load(afterDate: Date? = nil) async throws -> ConsentList {
func load() async throws -> [ConsentListEntry] {
guard let identifier = identifier else {
throw ContactError.invalidIdentifier
}
let newDate = Date()

let pagination = Pagination(
after: afterDate,
direction: .ascending
)
after: lastFetched,
direction: .ascending
)
let envelopes = try await client.apiClient.envelopes(topic: Topic.preferenceList(identifier).description, pagination: pagination)
let consentList = ConsentList(client: client)
lastFetched = newDate

var preferences: [PrivatePreferencesAction] = []

Expand All @@ -74,75 +75,78 @@ public class ConsentList {

try preferences.append(PrivatePreferencesAction(serializedData: Data(payload)))
}

for preference in preferences {
for address in preference.allowAddress.walletAddresses {
_ = consentList.allow(address: address)
_ = allow(address: address)
}

for address in preference.denyAddress.walletAddresses {
_ = consentList.deny(address: address)
_ = deny(address: address)
}

for groupId in preference.allowGroup.groupIds {
_ = consentList.allowGroup(groupId: groupId)
_ = allowGroup(groupId: groupId)
}

for groupId in preference.denyGroup.groupIds {
_ = consentList.denyGroup(groupId: groupId)
_ = denyGroup(groupId: groupId)
}
}

return consentList
return Array(entries.values)
}

func publish(entry: ConsentListEntry) async throws {
guard let identifier = identifier else {
throw ContactError.invalidIdentifier
}

var payload = PrivatePreferencesAction()
switch entry.entryType {

case .address:
switch entry.consentType {
case .allowed:
payload.allowAddress.walletAddresses = [entry.value]
case .denied:
payload.denyAddress.walletAddresses = [entry.value]
case .unknown:
payload.messageType = nil
}

case .groupId:
switch entry.consentType {
case .allowed:
if let valueData = entry.value.data(using: .utf8) {
payload.allowGroup.groupIds = [valueData]
}
case .denied:
if let valueData = entry.value.data(using: .utf8) {
payload.denyGroup.groupIds = [valueData]
}
case .unknown:
payload.messageType = nil
}
}

let message = try LibXMTP.userPreferencesEncrypt(
publicKey: publicKey,
privateKey: privateKey,
message: payload.serializedData()
)
func publish(entries: [ConsentListEntry]) async throws {
guard let identifier = identifier else {
throw ContactError.invalidIdentifier
}
var payload = PrivatePreferencesAction()

for entry in entries {
switch entry.entryType {

case .address:
switch entry.consentType {
case .allowed:
payload.allowAddress.walletAddresses.append(entry.value)
case .denied:
payload.denyAddress.walletAddresses.append(entry.value)
case .unknown:
payload.messageType = nil
}

case .groupId:
switch entry.consentType {
case .allowed:
if let valueData = entry.value.data(using: .utf8) {
payload.allowGroup.groupIds.append(valueData)
}
case .denied:
if let valueData = entry.value.data(using: .utf8) {
payload.denyGroup.groupIds.append(valueData)
}
case .unknown:
payload.messageType = nil
}
}

}


let message = try LibXMTP.userPreferencesEncrypt(
publicKey: publicKey,
privateKey: privateKey,
message: payload.serializedData()
)

let envelope = Envelope(
topic: Topic.preferenceList(identifier),
timestamp: Date(),
message: Data(message)
)
let envelope = Envelope(
topic: Topic.preferenceList(identifier),
timestamp: Date(),
message: Data(message)
)

try await client.publish(envelopes: [envelope])
}
try await client.publish(envelopes: [envelope])
}

func allow(address: String) -> ConsentListEntry {
let entry = ConsentListEntry.address(address, type: ConsentState.allowed)
Expand Down Expand Up @@ -200,6 +204,7 @@ public actor Contacts {

// Whether or not we have sent invite/intro to this contact
var hasIntroduced: [String: Bool] = [:]
var latestUpdate: Date?

public var consentList: ConsentList

Expand All @@ -208,8 +213,8 @@ public actor Contacts {
consentList = ConsentList(client: client)
}

public func refreshConsentList(afterDate: Date? = nil) async throws -> ConsentList {
consentList = try await ConsentList(client: client).load(afterDate: afterDate)
public func refreshConsentList() async throws -> ConsentList {
_ = try await consentList.load()
return consentList
}

Expand All @@ -230,29 +235,23 @@ public actor Contacts {
}

public func allow(addresses: [String]) async throws {
for address in addresses {
try await ConsentList(client: client).publish(entry: consentList.allow(address: address))
}
var entries = addresses.map { consentList.allow(address: $0) }
try await consentList.publish(entries: entries)
}

public func deny(addresses: [String]) async throws {
for address in addresses {
try await ConsentList(client: client).publish(entry: consentList.deny(address: address))
}
var entries = addresses.map { consentList.deny(address: $0) }
try await consentList.publish(entries: entries)
}

public func allowGroup(groupIds: [Data]) async throws {
for groupId in groupIds {
let entry = consentList.allowGroup(groupId: groupId)
try await ConsentList(client: client).publish(entry: entry)
}
var entries = groupIds.map { consentList.allowGroup(groupId: $0) }
try await consentList.publish(entries: entries)
}

public func denyGroup(groupIds: [Data]) async throws {
for groupId in groupIds {
let entry = consentList.denyGroup(groupId: groupId)
try await ConsentList(client: client).publish(entry: entry)
}
var entries = groupIds.map { consentList.denyGroup(groupId: $0) }
try await consentList.publish(entries: entries)
}

func markIntroduced(_ peerAddress: String, _ isIntroduced: Bool) {
Expand Down
13 changes: 0 additions & 13 deletions Tests/XMTPTests/ContactsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,4 @@ class ContactsTests: XCTestCase {
result = await contacts.isDenied(fixtures.alice.address)
XCTAssertTrue(result)
}

func testRefreshConsentListPagination() async throws {
let fixtures = await fixtures()
let contacts = fixtures.bobClient.contacts
let aliceAddress = fixtures.alice.address
try await contacts.deny(addresses: [aliceAddress])
let date = Date()

let result = try await contacts.consentList.load(afterDate: date)
XCTAssertNil(result.entries[ConsentListEntry.address(aliceAddress).key]?.consentType)
let allResult = try await contacts.consentList.load()
XCTAssertNotNil(allResult.entries[ConsentListEntry.address(aliceAddress).key]?.consentType)
}
}

0 comments on commit 3b3fb49

Please sign in to comment.