Skip to content

Commit f4d9445

Browse files
committed
feat: add live query listeners and fix dates conversion
1 parent f68babe commit f4d9445

File tree

4 files changed

+136
-28
lines changed

4 files changed

+136
-28
lines changed

android/src/main/java/com/cblreactnative/CblReactnativeModule.kt

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class CblReactnativeModule(reactContext: ReactApplicationContext) :
2929

3030
// Property to hold the context
3131
private val context: ReactApplicationContext = reactContext
32+
private val queryChangeListeners: MutableMap<String, ListenerToken> = mutableMapOf()
3233
private val replicatorChangeListeners: MutableMap<String, ListenerToken> = mutableMapOf()
3334
private val replicatorDocumentListeners: MutableMap<String, ListenerToken> = mutableMapOf()
3435

@@ -926,6 +927,88 @@ class CblReactnativeModule(reactContext: ReactApplicationContext) :
926927
}
927928
}
928929

930+
@ReactMethod
931+
fun query_AddChangeListener(
932+
changeListenerToken: String,
933+
query: String,
934+
parameters: ReadableMap?,
935+
name: String,
936+
promise: Promise
937+
) {
938+
GlobalScope.launch(Dispatchers.IO) {
939+
try {
940+
if (!DataValidation.validateDatabaseName(name, promise) || !DataValidation.validateQuery(query, promise)) {
941+
return@launch
942+
}
943+
val database = DatabaseManager.getDatabase(name)
944+
if (database == null) {
945+
context.runOnUiQueueThread {
946+
promise.reject("DATABASE_ERROR", "Could not find database with name $name")
947+
}
948+
return@launch
949+
}
950+
val queryObj = database.createQuery(query)
951+
if (parameters != null && parameters.keySetIterator().hasNextKey()) {
952+
val params = DataAdapter.readableMapToParameters(parameters)
953+
queryObj.parameters = params
954+
}
955+
val listener = queryObj.addChangeListener { change ->
956+
val resultMap = Arguments.createMap()
957+
resultMap.putString("token", changeListenerToken)
958+
change.results?.let { results ->
959+
val resultList = mutableListOf<String>()
960+
for (result in results) {
961+
resultList.add(result.toJSON())
962+
}
963+
val jsonArray = "[" + resultList.joinToString(",") + "]"
964+
resultMap.putString("data", jsonArray)
965+
}
966+
change.error?.let { error ->
967+
resultMap.putString("error", error.localizedMessage)
968+
}
969+
context.runOnUiQueueThread {
970+
sendEvent(context, "queryChange", resultMap)
971+
}
972+
}
973+
queryChangeListeners[changeListenerToken] = listener
974+
context.runOnUiQueueThread {
975+
promise.resolve(null)
976+
}
977+
} catch (e: Throwable) {
978+
context.runOnUiQueueThread {
979+
promise.reject("QUERY_ERROR", e.message)
980+
}
981+
}
982+
}
983+
}
984+
985+
@ReactMethod
986+
fun query_RemoveChangeListener(
987+
changeListenerToken: String,
988+
promise: Promise
989+
) {
990+
GlobalScope.launch(Dispatchers.IO) {
991+
try {
992+
if (queryChangeListeners.containsKey(changeListenerToken)) {
993+
val listener = queryChangeListeners[changeListenerToken]
994+
listener?.remove()
995+
queryChangeListeners.remove(changeListenerToken)
996+
context.runOnUiQueueThread {
997+
promise.resolve(null)
998+
}
999+
} else {
1000+
context.runOnUiQueueThread {
1001+
promise.reject("QUERY_ERROR", "No query listener found for token $changeListenerToken")
1002+
}
1003+
}
1004+
} catch (e: Throwable) {
1005+
context.runOnUiQueueThread {
1006+
promise.reject("QUERY_ERROR", e.message)
1007+
}
1008+
}
1009+
}
1010+
}
1011+
9291012
// Replicator Functions
9301013
@ReactMethod
9311014
fun replicator_AddChangeListener(

android/src/main/java/com/cblreactnative/DataAdapter.kt

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import java.net.URI
1414
import java.text.SimpleDateFormat
1515
import java.util.Date
1616
import java.util.Locale
17+
import java.util.TimeZone
1718
import com.couchbase.lite.Collection as CBLCollection
1819

1920
object DataAdapter {
@@ -336,36 +337,43 @@ object DataAdapter {
336337
val iterator = map.keySetIterator()
337338
var count = 0
338339
while (iterator.hasNextKey()) {
339-
val key = iterator.nextKey()
340-
val nestedMap = map.getMap(key)
341-
val nestedType = nestedMap?.getString("type")
342-
count += 1
343-
when (nestedType) {
344-
"int" -> queryParameters.setInt(key, nestedMap.getDouble("value").toInt())
345-
"long" -> queryParameters.setLong(key, nestedMap.getDouble("value").toLong())
346-
"float" -> queryParameters.setFloat(key, nestedMap.getDouble("value").toFloat())
347-
"double" -> queryParameters.setDouble(key, nestedMap.getDouble("value"))
348-
"boolean" -> queryParameters.setBoolean(key, nestedMap.getBoolean("value"))
349-
"string" -> queryParameters.setString(key, nestedMap.getString("value"))
350-
"date" -> {
351-
val stringValue = map.getString("value")
352-
stringValue?.let { strValue ->
353-
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
354-
val date = dateFormat.parse(strValue)
355-
date?.let { d ->
356-
queryParameters.setDate(key, d)
340+
val key = iterator.nextKey()
341+
val nestedMap = map.getMap(key)
342+
val nestedType = nestedMap?.getString("type")
343+
count += 1
344+
when (nestedType) {
345+
"int" -> queryParameters.setInt(key, nestedMap.getDouble("value").toInt())
346+
"long" -> queryParameters.setLong(key, nestedMap.getDouble("value").toLong())
347+
"float" -> queryParameters.setFloat(key, nestedMap.getDouble("value").toFloat())
348+
"double" -> queryParameters.setDouble(key, nestedMap.getDouble("value"))
349+
"boolean" -> queryParameters.setBoolean(key, nestedMap.getBoolean("value"))
350+
"string" -> queryParameters.setString(key, nestedMap.getString("value"))
351+
"date" -> {
352+
val stringValue = nestedMap.getString("value")
353+
stringValue?.let { strValue ->
354+
val date = parseIsoDate(strValue)
355+
date?.let { d ->
356+
queryParameters.setDate(key, d)
357+
}
358+
}
357359
}
358-
}
360+
"value" -> {
361+
val value = nestedMap.getDynamic("value")
362+
when (value.type) {
363+
ReadableType.Boolean -> queryParameters.setBoolean(key, value.asBoolean())
364+
ReadableType.Number -> queryParameters.setDouble(key, value.asDouble())
365+
ReadableType.String -> queryParameters.setString(key, value.asString())
366+
else -> queryParameters.setValue(key, value)
367+
}
368+
}
369+
else -> throw Exception("Error: Invalid parameter type: $nestedType")
359370
}
360-
361-
else -> throw Exception("Error: Invalid parameter type")
362-
}
363371
}
364372
if (count == 0) {
365-
return null
373+
return null
366374
}
367375
return queryParameters
368-
}
376+
}
369377

370378
/**
371379
* Converts a `ReadableMap` to a `ReplicatorConfiguration` object.
@@ -559,4 +567,21 @@ object DataAdapter {
559567
}
560568
return map
561569
}
570+
571+
private fun parseIsoDate(dateString: String): Date? {
572+
val formats = listOf(
573+
"yyyy-MM-dd'T'HH:mm:ss.SSSX", // Handles Z or +hh:mm
574+
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", // Handles Z
575+
"yyyy-MM-dd'T'HH:mm:ssX", // No milliseconds, with zone
576+
"yyyy-MM-dd'T'HH:mm:ss" // No milliseconds, no zone
577+
)
578+
for (format in formats) {
579+
try {
580+
val sdf = SimpleDateFormat(format, Locale.getDefault())
581+
sdf.timeZone = TimeZone.getTimeZone("UTC")
582+
return sdf.parse(dateString)
583+
} catch (_: Exception) { }
584+
}
585+
return null
586+
}
562587
}

expo-example/ios/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
PODS:
22
- boost (1.84.0)
3-
- cbl-reactnative (0.2.3):
3+
- cbl-reactnative (0.5.0):
44
- CouchbaseLite-Swift-Enterprise (= 3.2.1)
55
- DoubleConversion
66
- glog
@@ -2185,7 +2185,7 @@ EXTERNAL SOURCES:
21852185

21862186
SPEC CHECKSUMS:
21872187
boost: 1dca942403ed9342f98334bf4c3621f011aa7946
2188-
cbl-reactnative: aec81403856ac5f91d3792b18da6a67551402f76
2188+
cbl-reactnative: f3508d36c0f5c6846d7e3f77175a603a4ea15b2c
21892189
CouchbaseLite-Swift-Enterprise: 6a1eddeed0b450d00d2336bcf60c9a71e228f0e4
21902190
DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385
21912191
EXConstants: 277129d9a42ba2cf1fad375e7eaa9939005c60be

expo-example/ios/expoexample.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@
367367
);
368368
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
369369
PRODUCT_BUNDLE_IDENTIFIER = "com.couchbase.rn.expo-example";
370-
PRODUCT_NAME = expoexample;
370+
PRODUCT_NAME = "expoexample";
371371
SWIFT_OBJC_BRIDGING_HEADER = "expoexample/expoexample-Bridging-Header.h";
372372
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
373373
SWIFT_VERSION = 5.0;
@@ -395,7 +395,7 @@
395395
);
396396
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
397397
PRODUCT_BUNDLE_IDENTIFIER = "com.couchbase.rn.expo-example";
398-
PRODUCT_NAME = expoexample;
398+
PRODUCT_NAME = "expoexample";
399399
SWIFT_OBJC_BRIDGING_HEADER = "expoexample/expoexample-Bridging-Header.h";
400400
SWIFT_VERSION = 5.0;
401401
TARGETED_DEVICE_FAMILY = "1,2";

0 commit comments

Comments
 (0)