diff --git a/Package.swift b/Package.swift index 6c0bc8e03..9a61e0040 100644 --- a/Package.swift +++ b/Package.swift @@ -64,7 +64,7 @@ let package = Package( path: "Sources/WalletConnectSign"), .target( name: "WalletConnectChat", - dependencies: ["WalletConnectIdentity", "WalletConnectSync"], + dependencies: ["WalletConnectIdentity", "WalletConnectSync", "WalletConnectHistory"], path: "Sources/Chat"), .target( name: "Auth", diff --git a/Sources/Chat/ChatClient.swift b/Sources/Chat/ChatClient.swift index a0862fcc2..eb85954c8 100644 --- a/Sources/Chat/ChatClient.swift +++ b/Sources/Chat/ChatClient.swift @@ -4,6 +4,7 @@ import Combine public class ChatClient { private var publishers = [AnyCancellable]() private let identityClient: IdentityClient + private let historyClient: HistoryClient private let messagingService: MessagingService private let resubscriptionService: ResubscriptionService private let invitationHandlingService: InvitationHandlingService @@ -58,6 +59,7 @@ public class ChatClient { // MARK: - Initialization init(identityClient: IdentityClient, + historyClient: HistoryClient, messagingService: MessagingService, resubscriptionService: ResubscriptionService, invitationHandlingService: InvitationHandlingService, @@ -69,6 +71,7 @@ public class ChatClient { socketConnectionStatusPublisher: AnyPublisher ) { self.identityClient = identityClient + self.historyClient = historyClient self.messagingService = messagingService self.resubscriptionService = resubscriptionService self.invitationHandlingService = invitationHandlingService @@ -93,6 +96,10 @@ public class ChatClient { ) async throws -> String { let publicKey = try await identityClient.register(account: account, onSign: onSign) + + let payload = RegisterPayload(tags: ["2002"], relayUrl: "wss://relay.walletconnect.com") + try await historyClient.registerTags(payload: payload, historyUrl: "https://history.walletconnect.com") + try await syncRegisterService.register(account: account, onSign: onSign) guard !isPrivate else { diff --git a/Sources/Chat/ChatClientFactory.swift b/Sources/Chat/ChatClientFactory.swift index c341beec8..1fb8d073c 100644 --- a/Sources/Chat/ChatClientFactory.swift +++ b/Sources/Chat/ChatClientFactory.swift @@ -25,14 +25,17 @@ public struct ChatClientFactory { storage: KeyValueStorage, syncClient: SyncClient ) -> ChatClient { + let clientIdStorage = ClientIdStorage(keychain: keychain) + let historyClient = HistoryClient(clientIdStorage: clientIdStorage) let kms = KeyManagementService(keychain: keychain) + let serializer = Serializer(kms: kms) let messageStore = KeyedDatabase<[Message]>(storage: storage, identifier: ChatStorageIdentifiers.messages.rawValue) let receivedInviteStore = KeyedDatabase<[ReceivedInvite]>(storage: storage, identifier: ChatStorageIdentifiers.receivedInvites.rawValue) let threadStore: SyncStore = SyncStoreFactory.create(name: ChatStorageIdentifiers.thread.rawValue, syncClient: syncClient, storage: storage) let identityClient = IdentityClientFactory.create(keyserver: keyserverURL, keychain: keychain, logger: logger) let inviteKeyDelegate = InviteKeyDelegate(networkingInteractor: networkingInteractor, kms: kms, identityClient: identityClient) let sentInviteDelegate = SentInviteStoreDelegate(networkingInteractor: networkingInteractor, kms: kms) - let threadDelegate = ThreadStoreDelegate(networkingInteractor: networkingInteractor, kms: kms) + let threadDelegate = ThreadStoreDelegate(networkingInteractor: networkingInteractor, kms: kms, historyClient: historyClient, serializer: serializer) let sentInviteStore: SyncStore = SyncStoreFactory.create(name: ChatStorageIdentifiers.sentInvite.rawValue, syncClient: syncClient, storage: storage) let inviteKeyStore: SyncStore = SyncStoreFactory.create(name: ChatStorageIdentifiers.inviteKey.rawValue, syncClient: syncClient, storage: storage) let receivedInviteStatusStore: SyncStore = SyncStoreFactory.create(name: ChatStorageIdentifiers.receivedInviteStatus.rawValue, syncClient: syncClient, storage: storage) @@ -47,6 +50,7 @@ public struct ChatClientFactory { let client = ChatClient( identityClient: identityClient, + historyClient: historyClient, messagingService: messagingService, resubscriptionService: resubscriptionService, invitationHandlingService: invitationHandlingService, diff --git a/Sources/Chat/ChatImports.swift b/Sources/Chat/ChatImports.swift index 24dfe29a5..447ee4a25 100644 --- a/Sources/Chat/ChatImports.swift +++ b/Sources/Chat/ChatImports.swift @@ -2,4 +2,5 @@ @_exported import WalletConnectSigner @_exported import WalletConnectIdentity @_exported import WalletConnectSync +@_exported import WalletConnectHistory #endif diff --git a/Sources/Chat/Storage/ChatStorage.swift b/Sources/Chat/Storage/ChatStorage.swift index b90a5f0be..2787f84b1 100644 --- a/Sources/Chat/Storage/ChatStorage.swift +++ b/Sources/Chat/Storage/ChatStorage.swift @@ -107,7 +107,7 @@ final class ChatStorage { func initializeDelegates() async throws { try await sentInviteStoreDelegate.onInitialization(sentInviteStore.getAll()) - try await threadStoreDelegate.onInitialization(threadStore.getAll()) + try await threadStoreDelegate.onInitialization(storage: self) try await inviteKeyDelegate.onInitialization(inviteKeyStore.getAll()) try await receiviedInviteStatusDelegate.onInitialization() } diff --git a/Sources/Chat/Storage/ThreadStoreDelegate.swift b/Sources/Chat/Storage/ThreadStoreDelegate.swift index d03a2b672..2715fc02b 100644 --- a/Sources/Chat/Storage/ThreadStoreDelegate.swift +++ b/Sources/Chat/Storage/ThreadStoreDelegate.swift @@ -4,15 +4,23 @@ final class ThreadStoreDelegate { private let networkingInteractor: NetworkInteracting private let kms: KeyManagementServiceProtocol + private let historyClient: HistoryClient + private let serializer: Serializing - init(networkingInteractor: NetworkInteracting, kms: KeyManagementServiceProtocol) { + init(networkingInteractor: NetworkInteracting, kms: KeyManagementServiceProtocol, historyClient: HistoryClient, serializer: Serializing) { self.networkingInteractor = networkingInteractor self.kms = kms + self.serializer = serializer + self.historyClient = historyClient } - func onInitialization(_ threads: [Thread]) async throws { - let topics = threads.map { $0.topic } - try await networkingInteractor.batchSubscribe(topics: topics) + func onInitialization(storage: ChatStorage) async throws { + let threads = storage.getAllThreads() + try await networkingInteractor.batchSubscribe(topics: threads.map { $0.topic }) + + for thread in threads { + try await fetchMessageHistory(thread: thread, storage: storage) + } } func onUpdate(_ thread: Thread, storage: ChatStorage) { @@ -24,6 +32,8 @@ final class ThreadStoreDelegate { let symmetricKey = try SymmetricKey(hex: thread.symKey) try kms.setSymmetricKey(symmetricKey, for: thread.topic) try await networkingInteractor.subscribe(topic: thread.topic) + + // Relay Client injection! } } @@ -31,3 +41,34 @@ final class ThreadStoreDelegate { } } + +private extension ThreadStoreDelegate { + + func fetchMessageHistory(thread: Thread, storage: ChatStorage) async throws { + let historyPayload = GetMessagesPayload(topic: thread.topic, originId: nil, messageCount: 200, direction: .forward) + let response = try await historyClient.getMessages(payload: historyPayload, historyUrl: "https://history.walletconnect.com") + + for messagePayload in response.messages { + let (request, _, _): (RPCRequest, _, _) = try serializer.deserialize( + topic: thread.topic, + encodedEnvelope: messagePayload + ) + + let wrapper = try request.params!.get(MessagePayload.Wrapper.self) + + let (messagePayload, messageClaims) = try MessagePayload.decodeAndVerify(from: wrapper) + + let authorAccount = messagePayload.recipientAccount == thread.selfAccount + ? thread.peerAccount + : thread.selfAccount + + let message = Message( + topic: thread.topic, + message: messagePayload.message, + authorAccount: authorAccount, + timestamp: messageClaims.iat) + + storage.set(message: message, account: thread.selfAccount) + } + } +} diff --git a/WalletConnectSwiftV2.podspec b/WalletConnectSwiftV2.podspec index ed2856704..6b3b759a6 100644 --- a/WalletConnectSwiftV2.podspec +++ b/WalletConnectSwiftV2.podspec @@ -101,10 +101,17 @@ Pod::Spec.new do |spec| ss.dependency 'WalletConnectSwiftV2/WalletConnectNetworking' end + spec.subspec 'WalletConnectHistory' do |ss| + ss.source_files = 'Sources/WalletConnectHistory/**/*.{h,m,swift}' + ss.dependency 'WalletConnectSwiftV2/WalletConnectRelay' + ss.dependency 'WalletConnectSwiftV2/HTTPClient' + end + spec.subspec 'WalletConnectChat' do |ss| ss.source_files = 'Sources/Chat/**/*.{h,m,swift}' ss.dependency 'WalletConnectSwiftV2/WalletConnectSync' ss.dependency 'WalletConnectSwiftV2/WalletConnectIdentity' + ss.dependency 'WalletConnectSwiftV2/WalletConnectHistory' end spec.subspec 'WalletConnectSync' do |ss|