From de44af877b6faedf01d5049266523f375d6f3c1f Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Thu, 7 Nov 2024 11:43:59 -0300 Subject: [PATCH] wip discontinuities TODO test TODO test figure out internal interface TODO see if there are any spec points to do TODO if Umair merges reactions before this, we need to incorporate here. Resolves #47. --- Sources/AblyChat/DefaultMessages.swift | 15 ++++++++++----- .../DefaultRoomLifecycleContributor.swift | 16 +++++++++++++--- Sources/AblyChat/RoomFeature.swift | 16 ++++++++++++++++ .../AblyChatTests/Mocks/MockFeatureChannel.swift | 14 ++++++++++++++ 4 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 Tests/AblyChatTests/Mocks/MockFeatureChannel.swift diff --git a/Sources/AblyChat/DefaultMessages.swift b/Sources/AblyChat/DefaultMessages.swift index d11b283d..9981c96c 100644 --- a/Sources/AblyChat/DefaultMessages.swift +++ b/Sources/AblyChat/DefaultMessages.swift @@ -13,7 +13,7 @@ private struct MessageSubscriptionWrapper { @MainActor internal final class DefaultMessages: Messages, EmitsDiscontinuities { private let roomID: String - public nonisolated let channel: RealtimeChannelProtocol + public nonisolated let featureChannel: FeatureChannel private let chatAPI: ChatAPI private let clientID: String @@ -21,8 +21,8 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities { // UUID acts as a unique identifier for each listener/subscription. MessageSubscriptionWrapper houses the subscription and the timeserial of when it was attached or resumed. private var subscriptionPoints: [UUID: MessageSubscriptionWrapper] = [:] - internal nonisolated init(channel: RealtimeChannelProtocol, chatAPI: ChatAPI, roomID: String, clientID: String) async { - self.channel = channel + internal nonisolated init(featureChannel: FeatureChannel, chatAPI: ChatAPI, roomID: String, clientID: String) async { + self.featureChannel = featureChannel self.chatAPI = chatAPI self.roomID = roomID self.clientID = clientID @@ -32,6 +32,10 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities { await handleChannelEvents(roomId: roomID) } + internal nonisolated var channel: any RealtimeChannelProtocol { + featureChannel.channel + } + // (CHA-M4) Messages can be received via a subscription in realtime. internal func subscribe(bufferingPolicy: BufferingPolicy) async throws -> MessageSubscription { let uuid = UUID() @@ -100,8 +104,9 @@ internal final class DefaultMessages: Messages, EmitsDiscontinuities { } // TODO: (CHA-M7) Users may subscribe to discontinuity events to know when there’s been a break in messages that they need to resolve. Their listener will be called when a discontinuity event is triggered from the room lifecycle. - https://github.com/ably-labs/ably-chat-swift/issues/47 - internal nonisolated func subscribeToDiscontinuities() -> Subscription { - fatalError("not implemented") + internal func subscribeToDiscontinuities() async -> Subscription { + // TODO: test + await featureChannel.subscribeToDiscontinuities() } private func getBeforeSubscriptionStart(_ uuid: UUID, params: QueryOptions) async throws -> any PaginatedResult { diff --git a/Sources/AblyChat/DefaultRoomLifecycleContributor.swift b/Sources/AblyChat/DefaultRoomLifecycleContributor.swift index a236061b..601850f7 100644 --- a/Sources/AblyChat/DefaultRoomLifecycleContributor.swift +++ b/Sources/AblyChat/DefaultRoomLifecycleContributor.swift @@ -1,8 +1,9 @@ import Ably -internal actor DefaultRoomLifecycleContributor: RoomLifecycleContributor { +internal actor DefaultRoomLifecycleContributor: RoomLifecycleContributor, EmitsDiscontinuities { internal let channel: DefaultRoomLifecycleContributorChannel internal let feature: RoomFeature + private var discontinuitySubscriptions: [Subscription] = [] internal init(channel: DefaultRoomLifecycleContributorChannel, feature: RoomFeature) { self.channel = channel @@ -11,8 +12,17 @@ internal actor DefaultRoomLifecycleContributor: RoomLifecycleContributor { // MARK: - Discontinuities - internal func emitDiscontinuity(_: ARTErrorInfo) { - // TODO: https://github.com/ably-labs/ably-chat-swift/issues/47 + internal func emitDiscontinuity(_ error: ARTErrorInfo) { + for subscription in discontinuitySubscriptions { + subscription.emit(error) + } + } + + internal func subscribeToDiscontinuities() -> Subscription { + let subscription = Subscription(bufferingPolicy: .unbounded) + // TODO: clean up old subscriptions (https://github.com/ably-labs/ably-chat-swift/issues/36) + discontinuitySubscriptions.append(subscription) + return subscription } } diff --git a/Sources/AblyChat/RoomFeature.swift b/Sources/AblyChat/RoomFeature.swift index e2fb70fc..c31a1c1a 100644 --- a/Sources/AblyChat/RoomFeature.swift +++ b/Sources/AblyChat/RoomFeature.swift @@ -1,3 +1,5 @@ +import Ably + /// The features offered by a chat room. internal enum RoomFeature { case messages @@ -21,3 +23,17 @@ internal enum RoomFeature { } } } + +// TODO: what's this? +internal protocol FeatureChannel: Sendable, EmitsDiscontinuities { + var channel: RealtimeChannelProtocol { get } +} + +internal struct DefaultFeatureChannel: FeatureChannel { + internal var channel: RealtimeChannelProtocol + internal var contributor: DefaultRoomLifecycleContributor + + internal func subscribeToDiscontinuities() async -> Subscription { + await contributor.subscribeToDiscontinuities() + } +} diff --git a/Tests/AblyChatTests/Mocks/MockFeatureChannel.swift b/Tests/AblyChatTests/Mocks/MockFeatureChannel.swift new file mode 100644 index 00000000..fd091366 --- /dev/null +++ b/Tests/AblyChatTests/Mocks/MockFeatureChannel.swift @@ -0,0 +1,14 @@ +import Ably +@testable import AblyChat + +final class MockFeatureChannel: FeatureChannel { + let channel: RealtimeChannelProtocol + + init(channel: RealtimeChannelProtocol) { + self.channel = channel + } + + func subscribeToDiscontinuities() async -> Subscription { + fatalError("TODO") + } +}