Skip to content

Commit 8c06949

Browse files
committed
[ECO-4899] feat: add public API for Chat SDK
Based on Ably Chat JS SDK repo at 0b4b0f8. This is just a first attempt and all the decisions can be revisited. Notable TypeScript-to-Kotlin decisions: - Functions that return Promises become suspend functions. - I decided to leave listeners as they are and not use `Flow` in the public interface. These listeners will be invoked synchronously with the events they are listening to. This approach makes it easy to provide extension functions for an idiomatic `Flow` interface on one hand, and on the other hand, it gives maximum flexibility for SDK users to utilize other reactive libraries like Reactor or RxJava. - The presence data type is `Any` for now.
1 parent 17d0c76 commit 8c06949

21 files changed

+1361
-2
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.ably.chat
2+
3+
import io.ably.lib.realtime.AblyRealtime
4+
import io.ably.lib.types.ClientOptions
5+
6+
typealias RealtimeClient = AblyRealtime
7+
8+
/**
9+
* This is the core client for Ably chat. It provides access to chat rooms.
10+
*/
11+
interface ChatClient {
12+
/**
13+
* The rooms object, which provides access to chat rooms.
14+
*/
15+
val room: Room
16+
17+
/**
18+
* The underlying connection to Ably, which can be used to monitor the clients
19+
* connection to Ably servers.
20+
*/
21+
val connection: Connection
22+
23+
/**
24+
* The clientId of the current client.
25+
*/
26+
val clientId: String
27+
28+
/**
29+
* The underlying Ably Realtime client.
30+
*/
31+
val realtime: RealtimeClient
32+
33+
/**
34+
* The resolved client options for the client, including any defaults that have been set.
35+
*/
36+
val clientOptions: ClientOptions
37+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.ably.chat
2+
3+
import io.ably.lib.util.Log.LogHandler
4+
5+
/**
6+
* Configuration options for the chat client.
7+
*/
8+
data class ClientOptions(
9+
/**
10+
* A custom log handler that will be used to log messages from the client.
11+
* @defaultValue The client will log messages to the console.
12+
*/
13+
val logHandler: LogHandler? = null,
14+
15+
/**
16+
* The minimum log level at which messages will be logged.
17+
* @defaultValue LogLevel.Error
18+
*/
19+
val logLevel: LogLevel = LogLevel.Error,
20+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.ably.chat
2+
3+
/**
4+
* Represents a connection to Ably.
5+
*/
6+
interface Connection {
7+
/**
8+
* The current status of the connection.
9+
*/
10+
val status: ConnectionStatus
11+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package com.ably.chat
2+
3+
import io.ably.lib.types.ErrorInfo
4+
5+
/**
6+
* Default timeout for transient states before we attempt handle them as a state change.
7+
*/
8+
const val TRANSIENT_TIMEOUT = 5000
9+
10+
/**
11+
* Represents a connection to Ably.
12+
*/
13+
interface ConnectionStatus {
14+
/**
15+
* The current status of the connection.
16+
*/
17+
val current: ConnectionLifecycle
18+
19+
/**
20+
* The current error, if any, that caused the connection to enter the current status.
21+
*/
22+
val error: ErrorInfo?
23+
24+
/**
25+
* Registers a listener that will be called whenever the connection status changes.
26+
* @param listener The function to call when the status changes.
27+
*/
28+
fun on(listener: Listener)
29+
30+
/**
31+
* Unregisters a listener
32+
* @param listener The function to call when the status changes.
33+
*/
34+
fun off(listener: Listener)
35+
36+
/**
37+
* An interface for listening to changes for the connection status
38+
*/
39+
fun interface Listener {
40+
/**
41+
* A function that can be called when the connection status changes.
42+
* @param change The change in status.
43+
*/
44+
fun connectionStatusChanged(change: ConnectionStatusChange)
45+
}
46+
}
47+
48+
/**
49+
* The different states that the connection can be in through its lifecycle.
50+
*/
51+
enum class ConnectionLifecycle(val stateName: String) {
52+
/**
53+
* A temporary state for when the library is first initialized.
54+
*/
55+
Initialized("initialized"),
56+
57+
/**
58+
* The library is currently connecting to Ably.
59+
*/
60+
Connecting("connecting"),
61+
62+
/**
63+
* The library is currently connected to Ably.
64+
*/
65+
Connected("connected"),
66+
67+
/**
68+
* The library is currently disconnected from Ably, but will attempt to reconnect.
69+
*/
70+
Disconnected("disconnected"),
71+
72+
/**
73+
* The library is in an extended state of disconnection, but will attempt to reconnect.
74+
*/
75+
Suspended("suspended"),
76+
77+
/**
78+
* The library is currently disconnected from Ably and will not attempt to reconnect.
79+
*/
80+
Failed("failed"),
81+
}
82+
83+
/**
84+
* Represents a change in the status of the connection.
85+
*/
86+
data class ConnectionStatusChange(
87+
/**
88+
* The new status of the connection.
89+
*/
90+
val current: ConnectionLifecycle,
91+
92+
/**
93+
* The previous status of the connection.
94+
*/
95+
val previous: ConnectionLifecycle,
96+
97+
/**
98+
* An error that provides a reason why the connection has
99+
* entered the new status, if applicable.
100+
*/
101+
val error: ErrorInfo?,
102+
103+
/**
104+
* The time in milliseconds that the client will wait before attempting to reconnect.
105+
*/
106+
val retryIn: Long?,
107+
)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.ably.chat
2+
3+
import io.ably.lib.types.ErrorInfo
4+
5+
/**
6+
* An interface to be implemented by objects that can emit discontinuities to listeners.
7+
*/
8+
interface EmitsDiscontinuities {
9+
/**
10+
* Register a listener to be called when a discontinuity is detected.
11+
* @param listener The listener to be called when a discontinuity is detected.
12+
*/
13+
fun onDiscontinuity(listener: Listener)
14+
15+
/**
16+
* Unregister a listener to be called when a discontinuity is detected.
17+
* @param listener The listener
18+
*/
19+
fun offDiscontinuity(listener: Listener)
20+
21+
/**
22+
* An interface for listening when discontinuity happens
23+
*/
24+
fun interface Listener {
25+
/**
26+
* A function that can be called when discontinuity happens.
27+
* @param reason reason for discontinuity
28+
*/
29+
fun discontinuityEmitted(reason: ErrorInfo?)
30+
}
31+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.ably.chat
2+
3+
/**
4+
* Error codes for the Chat SDK.
5+
*/
6+
object ErrorCodes {
7+
/**
8+
* The messages feature failed to attach.
9+
*/
10+
const val MessagesAttachmentFailed = 102_001
11+
12+
/**
13+
* The presence feature failed to attach.
14+
*/
15+
const val PresenceAttachmentFailed = 102_002
16+
17+
/**
18+
* The reactions feature failed to attach.
19+
*/
20+
const val ReactionsAttachmentFailed = 102_003
21+
22+
/**
23+
* The occupancy feature failed to attach.
24+
*/
25+
const val OccupancyAttachmentFailed = 102_004
26+
27+
/**
28+
* The typing feature failed to attach.
29+
*/
30+
const val TypingAttachmentFailed = 102_005
31+
// 102006 - 102049 reserved for future use for attachment errors
32+
33+
/**
34+
* The messages feature failed to detach.
35+
*/
36+
const val MessagesDetachmentFailed = 102_050
37+
38+
/**
39+
* The presence feature failed to detach.
40+
*/
41+
const val PresenceDetachmentFailed = 102_051
42+
43+
/**
44+
* The reactions feature failed to detach.
45+
*/
46+
const val ReactionsDetachmentFailed = 102_052
47+
48+
/**
49+
* The occupancy feature failed to detach.
50+
*/
51+
const val OccupancyDetachmentFailed = 102_053
52+
53+
/**
54+
* The typing feature failed to detach.
55+
*/
56+
const val TypingDetachmentFailed = 102_054
57+
// 102055 - 102099 reserved for future use for detachment errors
58+
59+
/**
60+
* The room has experienced a discontinuity.
61+
*/
62+
const val RoomDiscontinuity = 102_100
63+
64+
// Unable to perform operation;
65+
66+
/**
67+
* Cannot perform operation because the room is in a failed state.
68+
*/
69+
const val RoomInFailedState = 102_101
70+
71+
/**
72+
* Cannot perform operation because the room is in a releasing state.
73+
*/
74+
const val RoomIsReleasing = 102_102
75+
76+
/**
77+
* Cannot perform operation because the room is in a released state.
78+
*/
79+
const val RoomIsReleased = 102_103
80+
81+
/**
82+
* Cannot perform operation because the previous operation failed.
83+
*/
84+
const val PreviousOperationFailed = 102_104
85+
86+
/**
87+
* An unknown error has happened in the room lifecycle.
88+
*/
89+
const val RoomLifecycleError = 102_105
90+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.ably.chat
2+
3+
/**
4+
* All chat message events.
5+
*/
6+
enum class MessageEventType(val eventName: String) {
7+
/** Fires when a new chat message is received. */
8+
Created("message.created"),
9+
}
10+
11+
/**
12+
* Enum representing presence events.
13+
*/
14+
enum class PresenceEventType(val eventName: String) {
15+
/**
16+
* Event triggered when a user enters.
17+
*/
18+
Enter("enter"),
19+
20+
/**
21+
* Event triggered when a user leaves.
22+
*/
23+
Leave("leave"),
24+
25+
/**
26+
* Event triggered when a user updates their presence data.
27+
*/
28+
Update("update"),
29+
30+
/**
31+
* Event triggered when a user initially subscribes to presence.
32+
*/
33+
Present("present"),
34+
}
35+
36+
enum class TypingEventType(val eventName: String) {
37+
/** The set of currently typing users has changed. */
38+
Changed("typing.changed"),
39+
}
40+
41+
/**
42+
* Room reaction events. This is used for the realtime system since room reactions
43+
* have only one event: "roomReaction".
44+
*/
45+
enum class RoomReactionEventType(val eventName: String) {
46+
/**
47+
* Event triggered when a room reaction was received.
48+
*/
49+
Reaction("roomReaction"),
50+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.ably.chat
2+
3+
/**
4+
* Headers are a flat key-value map that can be attached to chat messages.
5+
*
6+
* The headers are a flat key-value map and are sent as part of the realtime
7+
* message's extras inside the `headers` property. They can serve similar
8+
* purposes as Metadata but as opposed to Metadata they are read by Ably and
9+
* can be used for features such as
10+
* [subscription filters](https://faqs.ably.com/subscription-filters).
11+
*
12+
* Do not use the headers for authoritative information. There is no
13+
* server-side validation. When reading the headers treat them like user
14+
* input.
15+
*
16+
* The key prefix `ably-chat` is reserved and cannot be used. Ably may add
17+
* headers prefixed with `ably-chat` in the future.
18+
*/
19+
typealias Headers = Map<String, String>

0 commit comments

Comments
 (0)