@@ -2,88 +2,147 @@ package io.github.jan.supabase.realtime
2
2
3
3
import io.github.jan.supabase.SupabaseSerializer
4
4
import io.github.jan.supabase.annotations.SupabaseInternal
5
- import io.github.jan.supabase.collections.AtomicMutableList
6
5
import io.github.jan.supabase.serializer.KotlinXSerializer
6
+ import kotlinx.collections.immutable.PersistentList
7
+ import kotlinx.collections.immutable.PersistentMap
8
+ import kotlinx.collections.immutable.persistentHashMapOf
9
+ import kotlinx.collections.immutable.persistentListOf
10
+ import kotlinx.collections.immutable.plus
7
11
import kotlinx.serialization.json.JsonObject
8
12
import kotlin.concurrent.atomics.AtomicInt
9
13
import kotlin.concurrent.atomics.AtomicReference
10
14
import kotlin.concurrent.atomics.fetchAndIncrement
15
+ import kotlin.concurrent.atomics.update
11
16
12
17
@SupabaseInternal
13
- sealed interface CallbackManager {
18
+ sealed class RealtimeCallbackId (val value : Int ) {
19
+
20
+ class Postgres (value : Int ) : RealtimeCallbackId(value)
21
+
22
+ class Presence (value : Int ) : RealtimeCallbackId(value)
23
+
24
+ class Broadcast (value : Int ) : RealtimeCallbackId(value)
25
+
26
+ }
27
+
28
+ @SupabaseInternal
29
+ interface CallbackManager {
14
30
15
31
fun triggerPostgresChange (ids : List <Int >, data : PostgresAction )
16
32
17
33
fun triggerBroadcast (event : String , data : JsonObject )
18
34
19
35
fun triggerPresenceDiff (joins : Map <String , Presence >, leaves : Map <String , Presence >)
20
36
21
- fun addBroadcastCallback ( event : String , callback : ( JsonObject ) -> Unit ): Int
37
+ fun hasPresenceCallback (): Boolean
22
38
23
- fun addPostgresCallback ( filter : PostgresJoinConfig , callback : (PostgresAction ) -> Unit ): Int
39
+ fun addBroadcastCallback ( event : String , callback : (JsonObject ) -> Unit ): RealtimeCallbackId . Broadcast
24
40
25
- fun addPresenceCallback ( callback : (PresenceAction ) -> Unit ): Int
41
+ fun addPostgresCallback ( filter : PostgresJoinConfig , callback : (PostgresAction ) -> Unit ): RealtimeCallbackId . Postgres
26
42
27
- fun removeCallbackById ( id : Int )
43
+ fun addPresenceCallback ( callback : ( PresenceAction ) -> Unit ): RealtimeCallbackId . Presence
28
44
29
- fun setServerChanges ( changes : List < PostgresJoinConfig > )
45
+ fun removeCallbackById ( id : RealtimeCallbackId )
30
46
31
- fun getCallbacks () : List <RealtimeCallback < * >>
47
+ fun setServerChanges ( changes : List <PostgresJoinConfig >)
32
48
33
49
}
34
50
51
+ private typealias BroadcastMap = PersistentMap <String , PersistentList <RealtimeCallback .BroadcastCallback >>
52
+ private typealias PresenceMap = PersistentMap <Int , RealtimeCallback .PresenceCallback >
53
+ private typealias PostgresMap = PersistentMap <Int , RealtimeCallback .PostgresCallback >
54
+
35
55
internal class CallbackManagerImpl (
36
56
private val serializer : SupabaseSerializer = KotlinXSerializer ()
37
57
) : CallbackManager {
38
58
39
59
private val nextId = AtomicInt (0 )
40
60
private val _serverChanges = AtomicReference (listOf<PostgresJoinConfig >())
41
61
val serverChanges: List <PostgresJoinConfig > get() = _serverChanges .load()
42
- private val callbacks = AtomicMutableList <RealtimeCallback <* >>()
43
62
44
- override fun getCallbacks (): List <RealtimeCallback <* >> {
45
- return callbacks.toList()
46
- }
63
+ private val presenceCallbacks = AtomicReference <PresenceMap >(persistentHashMapOf())
47
64
48
- override fun addBroadcastCallback (event : String , callback : (JsonObject ) -> Unit ): Int {
65
+ private val broadcastCallbacks = AtomicReference <BroadcastMap >(persistentHashMapOf())
66
+ // Additional map to know from which list a callback may be removed in broadcastCallbacks without searching through the whole map
67
+ private val broadcastEventId = AtomicReference <PersistentMap <Int , String >>(persistentHashMapOf())
68
+
69
+ private val postgresCallbacks = AtomicReference <PostgresMap >(persistentHashMapOf())
70
+
71
+ override fun addBroadcastCallback (event : String , callback : (JsonObject ) -> Unit ): RealtimeCallbackId .Broadcast {
49
72
val id = nextId.fetchAndIncrement()
50
- callbacks + = RealtimeCallback .BroadcastCallback (callback, event, id)
51
- return id
73
+ broadcastCallbacks.update {
74
+ val current = it[event] ? : persistentListOf()
75
+ it.put(event, current + RealtimeCallback .BroadcastCallback (callback, event, id))
76
+ }
77
+ broadcastEventId.update {
78
+ it.put(id, event)
79
+ }
80
+ return RealtimeCallbackId .Broadcast (id)
52
81
}
53
82
54
- override fun addPostgresCallback (filter : PostgresJoinConfig , callback : (PostgresAction ) -> Unit ): Int {
83
+ override fun addPostgresCallback (filter : PostgresJoinConfig , callback : (PostgresAction ) -> Unit ): RealtimeCallbackId . Postgres {
55
84
val id = nextId.fetchAndIncrement()
56
- callbacks + = RealtimeCallback .PostgresCallback (callback, filter, id)
57
- return id
85
+ postgresCallbacks.update {
86
+ it.put(id, RealtimeCallback .PostgresCallback (callback, filter, id))
87
+ }
88
+ return RealtimeCallbackId .Postgres (id)
58
89
}
59
90
60
91
override fun triggerPostgresChange (ids : List <Int >, data : PostgresAction ) {
61
92
val filter = serverChanges.filter { it.id in ids }
62
- val postgresCallbacks = callbacks.filterIsInstance<RealtimeCallback .PostgresCallback >()
63
93
val callbacks =
64
- postgresCallbacks.filter { cc -> filter.any { sc -> cc.filter == sc } }
94
+ postgresCallbacks.load().values. filter { cc -> filter.any { sc -> cc.filter == sc } }
65
95
callbacks.forEach { it.callback(data) }
66
96
}
67
97
68
98
override fun triggerBroadcast (event : String , data : JsonObject ) {
69
- val broadcastCallbacks = callbacks.filterIsInstance<RealtimeCallback .BroadcastCallback >()
70
- val callbacks = broadcastCallbacks.filter { it.event == event }
71
- callbacks.forEach { it.callback(data) }
99
+ broadcastCallbacks.load()[event]?.forEach { it.callback(data) }
72
100
}
73
101
74
102
override fun triggerPresenceDiff (joins : Map <String , Presence >, leaves : Map <String , Presence >) {
75
- val presenceCallbacks = callbacks.filterIsInstance<RealtimeCallback .PresenceCallback >()
76
- presenceCallbacks.forEach { it.callback(PresenceActionImpl (serializer, joins, leaves)) }
103
+ presenceCallbacks.load().values.forEach { it.callback(PresenceActionImpl (serializer, joins, leaves)) }
77
104
}
78
105
79
- override fun addPresenceCallback (callback : (PresenceAction ) -> Unit ): Int {
106
+ override fun hasPresenceCallback (): Boolean {
107
+ return presenceCallbacks.load().isNotEmpty()
108
+ }
109
+
110
+ override fun addPresenceCallback (callback : (PresenceAction ) -> Unit ): RealtimeCallbackId .Presence {
80
111
val id = nextId.fetchAndIncrement()
81
- callbacks + = RealtimeCallback .PresenceCallback (callback, id)
82
- return id
112
+ presenceCallbacks.update {
113
+ it.put(id, RealtimeCallback .PresenceCallback (callback, id))
114
+ }
115
+ return RealtimeCallbackId .Presence (id)
116
+ }
117
+
118
+ fun removeBroadcastCallbackById (id : Int ) {
119
+ val event = broadcastEventId.load()[id] ? : return
120
+ broadcastCallbacks.update {
121
+ it.put(event, it[event]?.removeAll { c -> c.id == id } ? : persistentListOf())
122
+ }
123
+ broadcastEventId.update {
124
+ it.remove(id)
125
+ }
126
+ }
127
+
128
+ fun removePresenceCallbackById (id : Int ) {
129
+ presenceCallbacks.update {
130
+ it.remove(id)
131
+ }
132
+ }
133
+
134
+ fun removePostgresCallbackById (id : Int ) {
135
+ postgresCallbacks.update {
136
+ it.remove(id)
137
+ }
83
138
}
84
139
85
- override fun removeCallbackById (id : Int ) {
86
- callbacks.indexOfFirst { it.id == id }.takeIf { it != - 1 }?.let { callbacks.removeAt(it) }
140
+ override fun removeCallbackById (id : RealtimeCallbackId ) {
141
+ when (id) {
142
+ is RealtimeCallbackId .Broadcast -> removeBroadcastCallbackById(id.value)
143
+ is RealtimeCallbackId .Presence -> removePresenceCallbackById(id.value)
144
+ is RealtimeCallbackId .Postgres -> removePostgresCallbackById(id.value)
145
+ }
87
146
}
88
147
89
148
override fun setServerChanges (changes : List <PostgresJoinConfig >) {
0 commit comments