diff --git a/expo-example/android/build.gradle b/expo-example/android/build.gradle index b45d776..2282fe0 100644 --- a/expo-example/android/build.gradle +++ b/expo-example/android/build.gradle @@ -6,7 +6,7 @@ buildscript { minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '24') compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '35') targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '34') - kotlinVersion = findProperty('android.kotlinVersion') ?: '1.9.24' + kotlinVersion = findProperty('android.kotlinVersion') ?: '1.9.25' ndkVersion = "26.1.10909125" } diff --git a/expo-example/android/settings.gradle b/expo-example/android/settings.gradle index 8b2bb13..477a094 100644 --- a/expo-example/android/settings.gradle +++ b/expo-example/android/settings.gradle @@ -1,5 +1,5 @@ pluginManagement { - includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json')"].execute(null, rootDir).text.trim()).getParentFile().toString()) + includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().toString()) } plugins { id("com.facebook.react.settings") } @@ -31,7 +31,7 @@ dependencyResolutionManagement { } } -apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle"); + apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle"); useExpoModules() include ':app' diff --git a/expo-example/app/tests/custom-bug-fix.tsx b/expo-example/app/tests/custom-bug-fix.tsx index 2e3a412..52fa34f 100644 --- a/expo-example/app/tests/custom-bug-fix.tsx +++ b/expo-example/app/tests/custom-bug-fix.tsx @@ -14,7 +14,8 @@ import { Collection, MutableDocument, Document, - ConcurrencyControl + ConcurrencyControl, + CollectionConfiguration } from 'cbl-reactnative';import getFileDefaultPath from '@/service/file/getFileDefaultPath'; export default function CustomBugFixScreen() { @@ -63,22 +64,28 @@ export default function CustomBugFixScreen() { const connectToSyncGateway = async () => { setListOfLogs(prev => [...prev, 'Connecting to Sync Gateway']); // ✅ Use prev - const defaultCollection = await database?.defaultCollection(); + if(database === null || database === undefined) { + throw Error("Database is undefined") + } + const defaultCollection = await database.defaultCollection(); const syncGatewayUrl = "wss://nasm0fvdr-jnehnb.apps.cloud.couchbase.com:4984/testendpoint" const endpoint = new URLEndpoint(syncGatewayUrl); const username = "jayantdhingra" const password = "f9yu5QT4B5jpZep@" - const replicatorConfig = new ReplicatorConfiguration(endpoint) + + const collectionConfig = new CollectionConfiguration(defaultCollection) + + + const listOfCollectionConfig = [collectionConfig] + + const replicatorConfig = new ReplicatorConfiguration(listOfCollectionConfig, endpoint) replicatorConfig.setAuthenticator(new BasicAuthenticator(username, password)) // replicatorConfig.setContinuous(true) replicatorConfig.setAcceptOnlySelfSignedCerts(false); - if (defaultCollection) { - replicatorConfig.addCollection(defaultCollection) - } const replicator = await Replicator.create(replicatorConfig) diff --git a/expo-example/app/tests/replication-new.tsx b/expo-example/app/tests/replication-new.tsx new file mode 100644 index 0000000..37c6a30 --- /dev/null +++ b/expo-example/app/tests/replication-new.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import TestRunnerContainer from '@/components/TestRunnerContainer/TestRunnerContainer'; + +import { ReplicatorNewApiTests } from '../../cblite-js-tests/cblite-tests/e2e/replicator-new-api-test'; + +export default function TestsReplicatorScreen() { + function reset() {} + + async function update(): Promise { + try { + return ['']; + } catch (e) { + // @ts-ignore + return [error.message]; + } + } + + return ( + + ); +} diff --git a/expo-example/app/tests/replicator-listeners.tsx b/expo-example/app/tests/replicator-listeners-new.tsx similarity index 97% rename from expo-example/app/tests/replicator-listeners.tsx rename to expo-example/app/tests/replicator-listeners-new.tsx index c675788..055c284 100644 --- a/expo-example/app/tests/replicator-listeners.tsx +++ b/expo-example/app/tests/replicator-listeners-new.tsx @@ -8,7 +8,8 @@ import { ReplicatorConfiguration, URLEndpoint, BasicAuthenticator, - MutableDocument + MutableDocument, + CollectionConfiguration } from 'cbl-reactnative'; import getFileDefaultPath from '@/service/file/getFileDefaultPath'; @@ -61,12 +62,18 @@ export default function ReplicatorListenersScreen() { } const endpoint = new URLEndpoint(SYNC_GATEWAY_URL); - const replicatorConfig = new ReplicatorConfiguration(endpoint); + + //Create CollectionConfiguration + const collectionConfig = new CollectionConfiguration(defaultCollection); + + const listOfCollectionConfig = [collectionConfig] + + // Pass array of configs and endpoint to constructor + const replicatorConfig = new ReplicatorConfiguration(listOfCollectionConfig , endpoint); replicatorConfig.setAuthenticator(new BasicAuthenticator(USERNAME, PASSWORD)); replicatorConfig.setContinuous(true); replicatorConfig.setAcceptOnlySelfSignedCerts(false); - replicatorConfig.addCollection(defaultCollection); const replicator = await Replicator.create(replicatorConfig); setReplicator(replicator); diff --git a/expo-example/app/tests/replicator-listeners-old.tsx b/expo-example/app/tests/replicator-listeners-old.tsx new file mode 100644 index 0000000..bb27347 --- /dev/null +++ b/expo-example/app/tests/replicator-listeners-old.tsx @@ -0,0 +1,389 @@ +import React, { useState } from 'react'; +import { SafeAreaView, Text, Button, ScrollView } from 'react-native'; +import { View } from '@/components/Themed/Themed'; +import { + Database, + DatabaseConfiguration, + Replicator, + ReplicatorConfiguration, + URLEndpoint, + BasicAuthenticator, + MutableDocument, + CollectionConfig +} from 'cbl-reactnative'; +import getFileDefaultPath from '@/service/file/getFileDefaultPath'; + +export default function ReplicatorListenersOldScreen() { + const [listOfLogs, setListOfLogs] = useState([]); + const [errorLogs, setErrorLogs] = useState([]); + + const [database, setDatabase] = useState(null); + const [replicator, setReplicator] = useState(null); + + const [statusToken, setStatusToken] = useState(''); + const [documentToken, setDocumentToken] = useState(''); + const [listOfDocuments, setListOfDocuments] = useState([]); + + // Configuration for Sync Gateway + const SYNC_GATEWAY_URL = "wss://nasm0fvdr-jnehnb.apps.cloud.couchbase.com:4984/testendpoint"; + const USERNAME = "jayantdhingra"; + const PASSWORD = "f9yu5QT4B5jpZep@"; + + const openDatabase = async () => { + try { + setListOfLogs(prev => [...prev, 'Opening Database']); + const databaseName = 'replicator_test_db_old'; + const directory = await getFileDefaultPath(); + const dbConfig = new DatabaseConfiguration(); + const database = new Database(databaseName, dbConfig); + await database.open(); + setListOfLogs(prev => [...prev, `Database opened with name: ${database.getName()}`]); + setDatabase(database); + } catch (error) { + // @ts-ignore + setErrorLogs(prev => [...prev, `Error opening database: ${error.message}`]); + } + } + + const createReplicator = async () => { + try { + setListOfLogs(prev => [...prev, 'Creating Replicator with OLD API (Default Collection)']); + + if (!database) { + setErrorLogs(prev => [...prev, 'Database not opened yet']); + return; + } + + const defaultCollection = await database.defaultCollection(); + + if (!defaultCollection) { + setErrorLogs(prev => [...prev, 'Could not get default collection']); + return; + } + + const endpoint = new URLEndpoint(SYNC_GATEWAY_URL); + + // OLD API: Create config with endpoint only + const replicatorConfig = new ReplicatorConfiguration(endpoint); + + // OLD API: Create CollectionConfig and add collections + const collectionConfig = new CollectionConfig(); + // No channels set - will replicate all + + // OLD API: Add collection with config + replicatorConfig.addCollection(defaultCollection, collectionConfig); + + replicatorConfig.setAuthenticator(new BasicAuthenticator(USERNAME, PASSWORD)); + replicatorConfig.setContinuous(true); + replicatorConfig.setAcceptOnlySelfSignedCerts(false); + + const replicator = await Replicator.create(replicatorConfig); + setReplicator(replicator); + setListOfLogs(prev => [...prev, `Replicator created with OLD API, ID: ${replicator.getId()}`]); + } catch (error) { + // @ts-ignore + setErrorLogs(prev => [...prev, `Error creating replicator: ${error.message}`]); + } + } + + const startStatusChangeListener = async () => { + try { + if (!replicator) { + setErrorLogs(prev => [...prev, 'Replicator not created yet']); + return; + } + + setListOfLogs(prev => [...prev, 'Starting status change listener']); + + const token = await replicator.addChangeListener((change) => { + const date = new Date().toISOString(); + const status = change.status; + + // Status object has methods (it's a ReplicatorStatus instance in the listener) + const activityLevel = status.getActivityLevel(); + const progress = status.getProgress(); + const error = status.getError(); + + let logMessage = `${date} Status: ${activityLevel}`; + + if (progress) { + logMessage += ` | Progress: ${progress.getCompleted()}/${progress.getTotal()}`; + } + + if (error) { + setErrorLogs(prev => [...prev, `${date} Replicator Error: ${error}`]); + } + + setListOfLogs(prev => [...prev, logMessage]); + }); + + setStatusToken(token); + setListOfLogs(prev => [...prev, `Status change listener started with token: ${token}`]); + } catch (error) { + // @ts-ignore + setErrorLogs(prev => [...prev, `Error starting status listener: ${error.message}`]); + } + } + + const stopStatusChangeListener = async () => { + try { + if (replicator && statusToken) { + await replicator.removeChangeListener(statusToken); + setStatusToken(''); + setListOfLogs(prev => [...prev, 'Status change listener stopped']); + } else { + setErrorLogs(prev => [...prev, 'No active status listener to stop']); + } + } catch (error) { + // @ts-ignore + setErrorLogs(prev => [...prev, `Error stopping status listener: ${error.message}`]); + } + } + + const startDocumentChangeListener = async () => { + try { + if (!replicator) { + setErrorLogs(prev => [...prev, 'Replicator not created yet']); + return; + } + + setListOfLogs(prev => [...prev, 'Starting document change listener']); + + const token = await replicator.addDocumentChangeListener((documentReplication) => { + const date = new Date().toISOString(); + const docs = documentReplication.documents; + const direction = documentReplication.isPush ? 'PUSH' : 'PULL'; + + docs.forEach(doc => { + const flags = doc.flags ? doc.flags.join(', ') : 'none'; + const error = doc.error ? ` | Error: ${doc.error}` : ''; + const logMessage = `${date} ${direction} - Scope: ${doc.scopeName}, Collection: ${doc.collectionName}, ID: ${doc.id}, Flags: [${flags}]${error}`; + setListOfLogs(prev => [...prev, logMessage]); + }); + }); + + setDocumentToken(token); + setListOfLogs(prev => [...prev, `Document change listener started with token: ${token}`]); + } catch (error) { + // @ts-ignore + setErrorLogs(prev => [...prev, `Error starting document listener: ${error.message}`]); + } + } + + const stopDocumentChangeListener = async () => { + try { + if (replicator && documentToken) { + await replicator.removeChangeListener(documentToken); + setDocumentToken(''); + setListOfLogs(prev => [...prev, 'Document change listener stopped']); + } else { + setErrorLogs(prev => [...prev, 'No active document listener to stop']); + } + } catch (error) { + // @ts-ignore + setErrorLogs(prev => [...prev, `Error stopping document listener: ${error.message}`]); + } + } + + const startReplicator = async () => { + try { + if (replicator) { + setListOfLogs(prev => [...prev, 'Starting replicator']); + await replicator.start(false); + setListOfLogs(prev => [...prev, 'Replicator started']); + } else { + setErrorLogs(prev => [...prev, 'Replicator not created yet']); + } + } catch (error) { + // @ts-ignore + setErrorLogs(prev => [...prev, `Error starting replicator: ${error.message}`]); + } + } + + const stopReplicator = async () => { + try { + if (replicator) { + setListOfLogs(prev => [...prev, 'Stopping replicator']); + await replicator.stop(); + setListOfLogs(prev => [...prev, 'Replicator stopped']); + } else { + setErrorLogs(prev => [...prev, 'Replicator not created yet']); + } + } catch (error) { + // @ts-ignore + setErrorLogs(prev => [...prev, `Error stopping replicator: ${error.message}`]); + } + } + + const createDocument = async () => { + setListOfLogs(prev => [...prev, 'Creating Document']); + try { + const defaultCollection = await database?.defaultCollection(); + + if (!defaultCollection) { + setErrorLogs(prev => [...prev, 'Could not get default collection']); + return; + } + + const doc = new MutableDocument(); + doc.setString('type', 'test-replication-old-api'); + doc.setString('name', `Test Doc ${Date.now()}`); + doc.setString('description', 'This document should replicate to server (OLD API)'); + doc.setNumber('value', Math.floor(Math.random() * 1000)); + doc.setDate('createdAt', new Date()); + + await defaultCollection.save(doc); + setListOfLogs(prev => [...prev, `Document created with ID: ${doc.getId()}`]); + setListOfDocuments(prev => [...prev, doc.getId()]); + } catch (error) { + // @ts-ignore + setErrorLogs(prev => [...prev, `Error creating document: ${error.message}`]); + } + } + + const getReplicatorStatus = async () => { + try { + if (replicator) { + const status: any = await replicator.getStatus(); + + // Status is returned as a plain object from native, not a ReplicatorStatus instance + const activityLevel = status.activityLevel; + const progress = status.progress; + const error = status.error; + + let statusMessage = `Current Status: ${activityLevel}`; + if (progress) { + statusMessage += ` | Progress: ${progress.completed}/${progress.total}`; + } + if (error) { + statusMessage += ` | Error: ${error}`; + } + + setListOfLogs(prev => [...prev, statusMessage]); + } else { + setErrorLogs(prev => [...prev, 'Replicator not created yet']); + } + } catch (error) { + // @ts-ignore + setErrorLogs(prev => [...prev, `Error getting status: ${error.message}`]); + } + } + + return ( + + + + + + Replicator Listeners Test - OLD API + + + Using deprecated CollectionConfig and ReplicatorConfiguration(endpoint) constructor + + + Setup Steps: +