Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consent syncing #423

Merged
merged 23 commits into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ede8122
update package
nplasterer Sep 13, 2024
70c3a41
Merge branch 'main' of https://github.com/xmtp/xmtp-ios
nplasterer Sep 13, 2024
c72c33c
Merge branch 'main' of https://github.com/xmtp/xmtp-ios
nplasterer Sep 20, 2024
bc9fb62
Merge branch 'main' of https://github.com/xmtp/xmtp-ios
nplasterer Sep 22, 2024
3e6c8e2
Merge branch 'main' of https://github.com/xmtp/xmtp-ios
nplasterer Sep 26, 2024
558729b
Merge branch 'main' of https://github.com/xmtp/xmtp-ios
nplasterer Sep 26, 2024
709cd72
Merge branch 'main' of https://github.com/xmtp/xmtp-ios
nplasterer Sep 26, 2024
6af3b08
Merge branch 'main' of https://github.com/xmtp/xmtp-ios
nplasterer Oct 1, 2024
e499905
Merge branch 'main' of https://github.com/xmtp/xmtp-ios
nplasterer Oct 9, 2024
c18f087
Merge branch 'main' of https://github.com/xmtp/xmtp-ios
nplasterer Oct 24, 2024
380f36c
Merge branch 'main' of https://github.com/xmtp/xmtp-ios
nplasterer Oct 25, 2024
d6250fc
Merge branch 'main' of https://github.com/xmtp/xmtp-ios into np/conse…
nplasterer Nov 8, 2024
6cade8c
get on the latest version of the sdk
nplasterer Nov 8, 2024
6726f78
get it compiling
nplasterer Nov 8, 2024
a4c1a50
update inbox create to take just the env
nplasterer Nov 10, 2024
6e057cd
update to handle ns
nplasterer Nov 10, 2024
c98d24c
add publish messages to the common interface
nplasterer Nov 10, 2024
f21fde5
update the listing functions
nplasterer Nov 10, 2024
ce1a6a8
switch on conversation type
nplasterer Nov 10, 2024
cba9c4e
add tests for everything
nplasterer Nov 10, 2024
d64a54c
Merge branch 'main' into np/consent-sync
nplasterer Nov 10, 2024
4ebfa8b
fix up lint issue
nplasterer Nov 10, 2024
b3aac07
Merge branch 'np/consent-sync' of https://github.com/xmtp/xmtp-ios in…
nplasterer Nov 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ let package = Package(
.package(url: "https://github.com/1024jp/GzipSwift", from: "5.2.0"),
.package(url: "https://github.com/bufbuild/connect-swift", exact: "0.12.0"),
.package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.0.0"),
.package(url: "https://github.com/xmtp/libxmtp-swift.git", exact: "0.6.0"),
.package(url: "https://github.com/xmtp/libxmtp-swift.git", exact: "3.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
18 changes: 11 additions & 7 deletions Sources/XMTPiOS/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public final class Client {
{
let accountAddress = account.address.lowercased()
let inboxId = try await getOrCreateInboxId(
options: options, address: accountAddress)
api: options.api, address: accountAddress)

return try await initializeClient(
accountAddress: accountAddress,
Expand All @@ -155,7 +155,7 @@ public final class Client {
{
let accountAddress = address.lowercased()
let inboxId = try await getOrCreateInboxId(
options: options, address: accountAddress)
api: options.api, address: accountAddress)

return try await initializeClient(
accountAddress: accountAddress,
Expand Down Expand Up @@ -256,19 +256,19 @@ public final class Client {
}

public static func getOrCreateInboxId(
options: ClientOptions, address: String
api: ClientOptions.Api, address: String
) async throws -> String {
var inboxId: String
do {
inboxId =
try await getInboxIdForAddress(
logger: XMTPLogger(),
host: options.api.env.url,
isSecure: options.api.env.isSecure == true,
host: api.env.url,
isSecure: api.env.isSecure == true,
accountAddress: address
) ?? generateInboxId(accountAddress: address, nonce: 0)
} catch {
inboxId = generateInboxId(accountAddress: address, nonce: 0)
inboxId = try generateInboxId(accountAddress: address, nonce: 0)
}
return inboxId
}
Expand Down Expand Up @@ -387,7 +387,11 @@ public final class Client {
}

public func requestMessageHistorySync() async throws {
try await ffiClient.requestHistorySync()
try await ffiClient.sendSyncRequest(kind: .messages)
}

public func syncConsent() async throws {
try await ffiClient.sendSyncRequest(kind: .consent)
}

public func revokeAllOtherInstallations(signingKey: SigningKey) async throws
Expand Down
21 changes: 16 additions & 5 deletions Sources/XMTPiOS/Conversation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import LibXMTP
public enum Conversation: Identifiable, Equatable, Hashable {
case group(Group)
case dm(Dm)

public static func == (lhs: Conversation, rhs: Conversation) -> Bool {
lhs.topic == rhs.topic
}
Expand Down Expand Up @@ -44,7 +44,7 @@ public enum Conversation: Identifiable, Equatable, Hashable {
}
}

public func consentState() async throws -> ConsentState {
public func consentState() throws -> ConsentState {
switch self {
case let .group(group):
return try group.consentState()
Expand Down Expand Up @@ -92,6 +92,15 @@ public enum Conversation: Identifiable, Equatable, Hashable {
content: content, options: options)
}
}

public func publishMessages() async throws {
switch self {
case let .group(group):
return try await group.publishMessages()
case let .dm(dm):
return try await dm.publishMessages()
}
}

public var type: ConversationType {
switch self {
Expand Down Expand Up @@ -168,19 +177,21 @@ public enum Conversation: Identifiable, Equatable, Hashable {
}

public func messages(
limit: Int? = nil, before: Date? = nil, after: Date? = nil,
limit: Int? = nil,
beforeNs: Int64? = nil,
afterNs: Int64? = nil,
direction: SortDirection? = .descending,
deliveryStatus: MessageDeliveryStatus = .all
) async throws -> [DecodedMessage] {
switch self {
case let .group(group):
return try await group.messages(
before: before, after: after, limit: limit,
beforeNs: beforeNs, afterNs: afterNs, limit: limit,
direction: direction, deliveryStatus: deliveryStatus
)
case let .dm(dm):
return try await dm.messages(
before: before, after: after, limit: limit,
beforeNs: beforeNs, afterNs: afterNs, limit: limit,
direction: direction, deliveryStatus: deliveryStatus
)
}
Expand Down
163 changes: 103 additions & 60 deletions Sources/XMTPiOS/Conversations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ public enum ConversationOrder {
case createdAt, lastMessage
}

public enum ConversationType {
case all, groups, dms
}

final class ConversationStreamCallback: FfiConversationCallback {
func onError(error: LibXMTP.FfiSubscribeError) {
print("Error ConversationStreamCallback \(error)")
Expand Down Expand Up @@ -80,11 +84,13 @@ public actor Conversations {
}

public func listGroups(
createdAfter: Date? = nil, createdBefore: Date? = nil, limit: Int? = nil
createdAfter: Date? = nil, createdBefore: Date? = nil,
limit: Int? = nil, order: ConversationOrder = .createdAt,
consentState: ConsentState? = nil
) async throws -> [Group] {
var options = FfiListConversationsOptions(
createdAfterNs: nil, createdBeforeNs: nil, limit: nil,
consentState: nil)
consentState: consentState?.toFFI)
if let createdAfter {
options.createdAfterNs = Int64(createdAfter.millisecondsSinceEpoch)
}
Expand All @@ -95,17 +101,25 @@ public actor Conversations {
if let limit {
options.limit = Int64(limit)
}
return try await ffiConversations.listGroups(opts: options).map {
let conversations = try await ffiConversations.listGroups(
opts: options)

let sortedConversations = try sortConversations(
conversations, order: order)

return sortedConversations.map {
$0.groupFromFFI(client: client)
}
}

public func listDms(
createdAfter: Date? = nil, createdBefore: Date? = nil, limit: Int? = nil
createdAfter: Date? = nil, createdBefore: Date? = nil,
limit: Int? = nil, order: ConversationOrder = .createdAt,
consentState: ConsentState? = nil
) async throws -> [Dm] {
var options = FfiListConversationsOptions(
createdAfterNs: nil, createdBeforeNs: nil, limit: nil,
consentState: nil)
consentState: consentState?.toFFI)
if let createdAfter {
options.createdAfterNs = Int64(createdAfter.millisecondsSinceEpoch)
}
Expand All @@ -116,7 +130,13 @@ public actor Conversations {
if let limit {
options.limit = Int64(limit)
}
return try await ffiConversations.listDms(opts: options).map {
let conversations = try await ffiConversations.listDms(
opts: options)

let sortedConversations = try sortConversations(
conversations, order: order)

return sortedConversations.map {
$0.dmFromFFI(client: client)
}
}
Expand Down Expand Up @@ -179,40 +199,49 @@ public actor Conversations {
}
}

public func stream() -> AsyncThrowingStream<
public func stream(type: ConversationType = .all) -> AsyncThrowingStream<
Conversation, Error
> {
AsyncThrowingStream { continuation in
let ffiStreamActor = FfiStreamActor()
let conversationCallback = ConversationStreamCallback {
conversation in
guard !Task.isCancelled else {
continuation.finish()
return
}
do {
let conversationType = try conversation.groupMetadata()
.conversationType()
if conversationType == "dm" {
continuation.yield(
Conversation.dm(
conversation.dmFromFFI(client: self.client))
)
} else if conversationType == "group" {
continuation.yield(
Conversation.group(
conversation.groupFromFFI(client: self.client))
)
}
} catch {
// Do nothing if the conversation type is neither a group or dm
}
}

let task = Task {
let stream = await ffiConversations.stream(
callback: ConversationStreamCallback { conversation in
guard !Task.isCancelled else {
continuation.finish()
return
}
do {
let conversationType =
try conversation.groupMetadata()
.conversationType()
if conversationType == "dm" {
continuation.yield(
Conversation.dm(
conversation.dmFromFFI(
client: self.client))
)
} else if conversationType == "group" {
continuation.yield(
Conversation.group(
conversation.groupFromFFI(
client: self.client))
)
}
} catch {
// Do nothing if the conversation type is neither a group or dm
}
let stream: FfiStreamCloser
switch type {
case .groups:
stream = await ffiConversations.streamGroups(
callback: conversationCallback)
case .all:
stream = await ffiConversations.stream(
callback: conversationCallback)
case .dms:
stream = await ffiConversations.streamDms(
callback: conversationCallback)
}
)
await ffiStreamActor.setFfiStream(stream)
continuation.onTermination = { @Sendable reason in
Task {
Expand Down Expand Up @@ -306,7 +335,8 @@ public actor Conversations {
throw ConversationError.memberCannotBeSelf
}
let addressMap = try await self.client.canMessage(addresses: addresses)
let unregisteredAddresses = addressMap
let unregisteredAddresses =
addressMap
.filter { !$0.value }
.map { $0.key }

Expand All @@ -328,34 +358,47 @@ public actor Conversations {
return group
}

public func streamAllMessages() -> AsyncThrowingStream<
DecodedMessage, Error
> {
public func streamAllMessages(type: ConversationType = .all)
-> AsyncThrowingStream<DecodedMessage, Error>
{
AsyncThrowingStream { continuation in
let ffiStreamActor = FfiStreamActor()
let task = Task {
let stream =
await ffiConversations
.streamAllMessages(
messageCallback: MessageCallback(client: self.client) {
message in
guard !Task.isCancelled else {
continuation.finish()
Task {
await ffiStreamActor.endStream() // End the stream upon cancellation
}
return
}
do {
continuation.yield(
try Message(
client: self.client, ffiMessage: message
).decode())
} catch {
print("Error onMessage \(error)")
}
}

let messageCallback = MessageCallback(client: self.client) {
message in
guard !Task.isCancelled else {
continuation.finish()
Task {
await ffiStreamActor.endStream()
}
return
}
do {
continuation.yield(
try Message(client: self.client, ffiMessage: message)
.decode()
)
} catch {
print("Error onMessage \(error)")
}
}

let task = Task {
let stream: FfiStreamCloser
switch type {
case .groups:
stream = await ffiConversations.streamAllGroupMessages(
messageCallback: messageCallback
)
case .dms:
stream = await ffiConversations.streamAllDmMessages(
messageCallback: messageCallback
)
case .all:
stream = await ffiConversations.streamAllMessages(
messageCallback: messageCallback
)
}
await ffiStreamActor.setFfiStream(stream)
}

Expand Down
Loading
Loading