Skip to content

Commit 2ec16e0

Browse files
committed
add expects for DataSnapshot.ref, DataSnapshot.value, DataSnapshot.hasChildren
implement in js and android
1 parent 9e9d68b commit 2ec16e0

File tree

4 files changed

+84
-54
lines changed
  • firebase-common/src/jsMain/kotlin/dev/gitlive/firebase
  • firebase-database/src
    • androidMain/kotlin/dev/gitlive/firebase/database
    • commonMain/kotlin/dev/gitlive/firebase/database
    • jsMain/kotlin/dev/gitlive/firebase/database

4 files changed

+84
-54
lines changed

firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ external object firebase {
310310
fun endAt(value: Any, key: String? = definedExternally): Query
311311
fun equalTo(value: Any, key: String? = definedExternally): Query
312312
fun limitToFirst(limit: Int): Query
313-
fun limitToLast (limit: Int): Query
313+
fun limitToLast(limit: Int): Query
314314
}
315315

316316
open class Reference: Query {
@@ -330,8 +330,10 @@ external object firebase {
330330

331331
open class DataSnapshot {
332332
val key: String?
333-
fun `val`(): Any
333+
val ref: Reference
334+
fun `val`(): Any?
334335
fun exists(): Boolean
336+
fun hasChildren(): Boolean
335337
fun forEach(action: (a: DataSnapshot) -> Boolean): Boolean
336338
fun numChildren(): Int
337339
fun child(path: String): DataSnapshot

firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ actual open class Query internal constructor(
112112
get() = callbackFlow {
113113
val listener = object : ValueEventListener {
114114
override fun onDataChange(snapshot: com.google.firebase.database.DataSnapshot) {
115-
trySend(DataSnapshot(snapshot))
115+
trySend(DataSnapshot(snapshot, persistenceEnabled))
116116
}
117117

118118
override fun onCancelled(error: com.google.firebase.database.DatabaseError) {
@@ -128,22 +128,22 @@ actual open class Query internal constructor(
128128

129129
val moved by lazy { types.contains(Type.MOVED) }
130130
override fun onChildMoved(snapshot: com.google.firebase.database.DataSnapshot, previousChildName: String?) {
131-
if(moved) trySend(ChildEvent(DataSnapshot(snapshot), Type.MOVED, previousChildName))
131+
if(moved) trySend(ChildEvent(DataSnapshot(snapshot, persistenceEnabled), Type.MOVED, previousChildName))
132132
}
133133

134134
val changed by lazy { types.contains(Type.CHANGED) }
135135
override fun onChildChanged(snapshot: com.google.firebase.database.DataSnapshot, previousChildName: String?) {
136-
if(changed) trySend(ChildEvent(DataSnapshot(snapshot), Type.CHANGED, previousChildName))
136+
if(changed) trySend(ChildEvent(DataSnapshot(snapshot, persistenceEnabled), Type.CHANGED, previousChildName))
137137
}
138138

139139
val added by lazy { types.contains(Type.ADDED) }
140140
override fun onChildAdded(snapshot: com.google.firebase.database.DataSnapshot, previousChildName: String?) {
141-
if(added) trySend(ChildEvent(DataSnapshot(snapshot), Type.ADDED, previousChildName))
141+
if(added) trySend(ChildEvent(DataSnapshot(snapshot, persistenceEnabled), Type.ADDED, previousChildName))
142142
}
143143

144144
val removed by lazy { types.contains(Type.REMOVED) }
145145
override fun onChildRemoved(snapshot: com.google.firebase.database.DataSnapshot) {
146-
if(removed) trySend(ChildEvent(DataSnapshot(snapshot), Type.REMOVED, null))
146+
if(removed) trySend(ChildEvent(DataSnapshot(snapshot, persistenceEnabled), Type.REMOVED, null))
147147
}
148148

149149
override fun onCancelled(error: com.google.firebase.database.DatabaseError) {
@@ -207,7 +207,7 @@ actual class DatabaseReference internal constructor(
207207
if (error != null) {
208208
deferred.completeExceptionally(error.toException())
209209
} else {
210-
deferred.complete(DataSnapshot(snapshot!!))
210+
deferred.complete(DataSnapshot(snapshot!!, persistenceEnabled))
211211
}
212212
}
213213

@@ -216,20 +216,28 @@ actual class DatabaseReference internal constructor(
216216
}
217217
}
218218
@Suppress("UNCHECKED_CAST")
219-
actual class DataSnapshot internal constructor(val android: com.google.firebase.database.DataSnapshot) {
219+
actual class DataSnapshot internal constructor(
220+
val android: com.google.firebase.database.DataSnapshot,
221+
private val persistenceEnabled: Boolean
222+
) {
220223

221224
actual val exists get() = android.exists()
222225

223226
actual val key get() = android.key
224227

228+
actual val ref: DatabaseReference get() = DatabaseReference(android.ref, persistenceEnabled)
229+
230+
actual val value get() = android.value
231+
225232
actual inline fun <reified T> value() =
226233
decode<T>(value = android.value)
227234

228235
actual fun <T> value(strategy: DeserializationStrategy<T>) =
229236
decode(strategy, android.value)
230237

231-
actual fun child(path: String) = DataSnapshot(android.child(path))
232-
actual val children: Iterable<DataSnapshot> get() = android.children.map { DataSnapshot(it) }
238+
actual fun child(path: String) = DataSnapshot(android.child(path), persistenceEnabled)
239+
actual val hasChildren get() = android.hasChildren()
240+
actual val children: Iterable<DataSnapshot> get() = android.children.map { DataSnapshot(it, persistenceEnabled) }
233241
}
234242

235243
actual class OnDisconnect internal constructor(

firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,12 @@ expect class DatabaseReference : Query {
8080
expect class DataSnapshot {
8181
val exists: Boolean
8282
val key: String?
83+
val ref: DatabaseReference
84+
val value: Any?
8385
inline fun <reified T> value(): T
8486
fun <T> value(strategy: DeserializationStrategy<T>): T
8587
fun child(path: String): DataSnapshot
88+
val hasChildren: Boolean
8689
val children: Iterable<DataSnapshot>
8790
}
8891

firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt

Lines changed: 60 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,27 @@ actual fun Firebase.database(app: FirebaseApp, url: String) =
2929
rethrow { dev.gitlive.firebase.database; FirebaseDatabase(app.js.database(url)) }
3030

3131
actual class FirebaseDatabase internal constructor(val js: firebase.database.Database) {
32-
actual fun reference(path: String) = rethrow { DatabaseReference(js.ref(path)) }
33-
actual fun reference() = rethrow { DatabaseReference(js.ref()) }
32+
actual fun reference(path: String) = rethrow { DatabaseReference(js.ref(path), js) }
33+
actual fun reference() = rethrow { DatabaseReference(js.ref(), js) }
3434
actual fun setPersistenceEnabled(enabled: Boolean) {}
3535
actual fun setLoggingEnabled(enabled: Boolean) = rethrow { firebase.database.enableLogging(enabled) }
3636
actual fun useEmulator(host: String, port: Int) = rethrow { js.useEmulator(host, port) }
3737
}
3838

39-
actual open class Query internal constructor(open val js: firebase.database.Query) {
39+
actual open class Query internal constructor(
40+
open val js: firebase.database.Query,
41+
val database: firebase.database.Database
42+
) {
4043

41-
actual fun orderByKey() = Query(js.orderByKey())
42-
actual fun orderByValue() = Query(js.orderByValue())
43-
actual fun orderByChild(path: String) = Query(js.orderByChild(path))
44+
actual fun orderByKey() = Query(js.orderByKey(), database)
45+
actual fun orderByValue() = Query(js.orderByValue(), database)
46+
actual fun orderByChild(path: String) = Query(js.orderByChild(path), database)
4447

4548
actual val valueEvents get() = callbackFlow<DataSnapshot> {
4649
val listener = rethrow {
4750
js.on(
4851
"value",
49-
{ it, _ -> trySend(DataSnapshot(it)) },
52+
{ it, _ -> trySend(DataSnapshot(it, database)) },
5053
{ close(DatabaseException(it)).run { Unit } }
5154
)
5255
}
@@ -62,7 +65,7 @@ actual open class Query internal constructor(open val js: firebase.database.Quer
6265
{ snapshot, previousChildName ->
6366
trySend(
6467
ChildEvent(
65-
DataSnapshot(snapshot),
68+
DataSnapshot(snapshot, database),
6669
type,
6770
previousChildName
6871
)
@@ -76,51 +79,54 @@ actual open class Query internal constructor(open val js: firebase.database.Quer
7679
awaitClose { rethrow { listeners.forEach { (eventType, listener) -> js.off(eventType, listener) } } }
7780
}
7881

79-
actual fun startAt(value: String, key: String?) = Query(js.startAt(value, key ?: undefined))
82+
actual fun startAt(value: String, key: String?) = Query(js.startAt(value, key ?: undefined), database)
8083

81-
actual fun startAt(value: Double, key: String?) = Query(js.startAt(value, key ?: undefined))
84+
actual fun startAt(value: Double, key: String?) = Query(js.startAt(value, key ?: undefined), database)
8285

83-
actual fun startAt(value: Boolean, key: String?) = Query(js.startAt(value, key ?: undefined))
86+
actual fun startAt(value: Boolean, key: String?) = Query(js.startAt(value, key ?: undefined), database)
8487

85-
actual fun endAt(value: String, key: String?) = Query(js.endAt(value, key ?: undefined))
88+
actual fun endAt(value: String, key: String?) = Query(js.endAt(value, key ?: undefined), database)
8689

87-
actual fun endAt(value: Double, key: String?) = Query(js.endAt(value, key ?: undefined))
90+
actual fun endAt(value: Double, key: String?) = Query(js.endAt(value, key ?: undefined), database)
8891

89-
actual fun endAt(value: Boolean, key: String?) = Query(js.endAt(value, key ?: undefined))
92+
actual fun endAt(value: Boolean, key: String?) = Query(js.endAt(value, key ?: undefined), database)
9093

91-
actual fun limitToFirst(limit: Int) = Query(js.limitToFirst(limit))
94+
actual fun limitToFirst(limit: Int) = Query(js.limitToFirst(limit), database)
9295

93-
actual fun limitToLast(limit: Int) = Query(js.limitToLast(limit))
96+
actual fun limitToLast(limit: Int) = Query(js.limitToLast(limit), database)
9497

95-
actual fun equalTo(value: String, key: String?) = Query(js.equalTo(value, key ?: undefined))
98+
actual fun equalTo(value: String, key: String?) = Query(js.equalTo(value, key ?: undefined), database)
9699

97-
actual fun equalTo(value: Double, key: String?) = Query(js.equalTo(value, key ?: undefined))
100+
actual fun equalTo(value: Double, key: String?) = Query(js.equalTo(value, key ?: undefined), database)
98101

99-
actual fun equalTo(value: Boolean, key: String?) = Query(js.equalTo(value, key ?: undefined))
102+
actual fun equalTo(value: Boolean, key: String?) = Query(js.equalTo(value, key ?: undefined), database)
100103

101104
override fun toString() = js.toString()
102105

103106
}
104107

105-
actual class DatabaseReference internal constructor(override val js: firebase.database.Reference): Query(js) {
108+
actual class DatabaseReference internal constructor(
109+
override val js: firebase.database.Reference,
110+
database: firebase.database.Database
111+
): Query(js, database) {
106112

107113
actual val key get() = rethrow { js.key }
108-
actual fun push() = rethrow { DatabaseReference(js.push()) }
109-
actual fun child(path: String) = rethrow { DatabaseReference(js.child(path)) }
114+
actual fun push() = rethrow { DatabaseReference(js.push(), database) }
115+
actual fun child(path: String) = rethrow { DatabaseReference(js.child(path), database) }
110116

111-
actual fun onDisconnect() = rethrow { OnDisconnect(js.onDisconnect()) }
117+
actual fun onDisconnect() = rethrow { OnDisconnect(js.onDisconnect(), database) }
112118

113119
actual suspend fun updateChildren(update: Map<String, Any?>, encodeDefaults: Boolean) =
114-
rethrow { js.update(encode(update, encodeDefaults)).awaitWhileOnline() }
120+
rethrow { js.update(encode(update, encodeDefaults)).awaitWhileOnline(database) }
115121

116-
actual suspend fun removeValue() = rethrow { js.remove().awaitWhileOnline() }
122+
actual suspend fun removeValue() = rethrow { js.remove().awaitWhileOnline(database) }
117123

118124
actual suspend inline fun <reified T> setValue(value: T?, encodeDefaults: Boolean) = rethrow {
119-
js.set(encode(value, encodeDefaults)).awaitWhileOnline()
125+
js.set(encode(value, encodeDefaults)).awaitWhileOnline(database)
120126
}
121127

122128
actual suspend fun <T> setValue(strategy: SerializationStrategy<T>, value: T, encodeDefaults: Boolean) =
123-
rethrow { js.set(encode(strategy, value, encodeDefaults)).awaitWhileOnline() }
129+
rethrow { js.set(encode(strategy, value, encodeDefaults)).awaitWhileOnline(database) }
124130

125131
actual suspend fun <T> runTransaction(strategy: KSerializer<T>, transactionUpdate: (currentData: T) -> T): DataSnapshot {
126132
val deferred = CompletableDeferred<DataSnapshot>()
@@ -130,7 +136,7 @@ actual class DatabaseReference internal constructor(override val js: firebase.da
130136
if (error != null) {
131137
deferred.completeExceptionally(error)
132138
} else {
133-
deferred.complete(DataSnapshot(snapshot!!))
139+
deferred.complete(DataSnapshot(snapshot!!, database))
134140
}
135141
},
136142
applyLocally = false
@@ -140,7 +146,15 @@ actual class DatabaseReference internal constructor(override val js: firebase.da
140146

141147
}
142148

143-
actual class DataSnapshot internal constructor(val js: firebase.database.DataSnapshot) {
149+
actual class DataSnapshot internal constructor(
150+
val js: firebase.database.DataSnapshot,
151+
val database: firebase.database.Database
152+
) {
153+
154+
actual val value get(): Any? {
155+
check(!hasChildren) { "DataSnapshot.value can only be used for primitive values (snapshots without children)" }
156+
return js.`val`()
157+
}
144158

145159
actual inline fun <reified T> value() =
146160
rethrow { decode<T>(value = js.`val`()) }
@@ -150,29 +164,34 @@ actual class DataSnapshot internal constructor(val js: firebase.database.DataSna
150164

151165
actual val exists get() = rethrow { js.exists() }
152166
actual val key get() = rethrow { js.key }
153-
actual fun child(path: String) = DataSnapshot(js.child(path))
154-
167+
actual fun child(path: String) = DataSnapshot(js.child(path), database)
168+
actual val hasChildren get() = js.hasChildren()
155169
actual val children: Iterable<DataSnapshot> = rethrow {
156170
ArrayList<DataSnapshot>(js.numChildren()).also {
157-
js.forEach { snapshot -> it.add(DataSnapshot(snapshot)) }
171+
js.forEach { snapshot -> it.add(DataSnapshot(snapshot, database)) }
158172
}
159173
}
174+
actual val ref: DatabaseReference
175+
get() = DatabaseReference(js.ref, database)
160176

161177
}
162178

163-
actual class OnDisconnect internal constructor(val js: firebase.database.OnDisconnect) {
179+
actual class OnDisconnect internal constructor(
180+
val js: firebase.database.OnDisconnect,
181+
val database: firebase.database.Database
182+
) {
164183

165-
actual suspend fun removeValue() = rethrow { js.remove().awaitWhileOnline() }
166-
actual suspend fun cancel() = rethrow { js.cancel().awaitWhileOnline() }
184+
actual suspend fun removeValue() = rethrow { js.remove().awaitWhileOnline(database) }
185+
actual suspend fun cancel() = rethrow { js.cancel().awaitWhileOnline(database) }
167186

168187
actual suspend fun updateChildren(update: Map<String, Any?>, encodeDefaults: Boolean) =
169-
rethrow { js.update(encode(update, encodeDefaults)).awaitWhileOnline() }
188+
rethrow { js.update(encode(update, encodeDefaults)).awaitWhileOnline(database) }
170189

171190
actual suspend inline fun <reified T> setValue(value: T, encodeDefaults: Boolean) =
172-
rethrow { js.set(encode(value, encodeDefaults)).awaitWhileOnline() }
191+
rethrow { js.set(encode(value, encodeDefaults)).awaitWhileOnline(database) }
173192

174193
actual suspend fun <T> setValue(strategy: SerializationStrategy<T>, value: T, encodeDefaults: Boolean) =
175-
rethrow { js.set(encode(strategy, value, encodeDefaults)).awaitWhileOnline() }
194+
rethrow { js.set(encode(strategy, value, encodeDefaults)).awaitWhileOnline(database) }
176195
}
177196

178197
actual class DatabaseException actual constructor(message: String?, cause: Throwable?) : RuntimeException(message, cause) {
@@ -191,14 +210,12 @@ inline fun <R> rethrow(function: () -> R): R {
191210
}
192211
}
193212

194-
suspend fun <T> Promise<T>.awaitWhileOnline(): T = coroutineScope {
213+
suspend fun <T> Promise<T>.awaitWhileOnline(database: firebase.database.Database): T = coroutineScope {
195214

196-
val notConnected = Firebase.database
215+
val notConnected = FirebaseDatabase(database)
197216
.reference(".info/connected")
198217
.valueEvents
199-
.filter {
200-
!it.value<Boolean>()
201-
}
218+
.filter { !it.value<Boolean>() }
202219
.produceIn(this)
203220

204221
select<T> {

0 commit comments

Comments
 (0)