@dataql/react-native
Version:
DataQL React Native SDK with offline-first capabilities and clean API
253 lines (252 loc) • 9.27 kB
JavaScript
import { BaseDataQLClient, } from "@dataql/core";
import { DatabaseClient } from "./db/client";
import { OfflineCacheManager } from "./cache/OfflineCacheManager";
import { SyncManager } from "./sync/SyncManager";
import { setGlobalInstances } from "./hooks/useDataQL";
export class DataQLClient extends BaseDataQLClient {
constructor(config) {
// Transform React Native config to base config
const baseConfig = {
appToken: config.appToken,
workerUrl: config.syncConfig?.serverUrl || "https://your-dataql-server.com/api",
schemas: config.schemas,
debug: config.debug,
env: config.env,
devPrefix: config.devPrefix,
};
const syncConfig = {
serverUrl: config.syncConfig?.serverUrl,
autoSync: config.syncConfig?.autoSync,
syncInterval: config.syncConfig?.syncInterval,
customConnection: config.syncConfig?.customConnection,
workerBinding: config.syncConfig?.workerBinding,
};
super(baseConfig, syncConfig);
this.reactNativeConfig = config;
// Initialize React Native specific components
this.dbClient = new DatabaseClient(config);
this.cacheManager = new OfflineCacheManager(this.dbClient);
// Handle potentially undefined syncConfig
const reactNativeSyncConfig = {
serverUrl: config.syncConfig?.serverUrl,
workerUrl: config.syncConfig?.workerUrl,
syncInterval: config.syncConfig?.syncInterval || 30000,
retryCount: config.syncConfig?.retryCount || 3,
batchSize: config.syncConfig?.batchSize || 10,
autoSync: config.syncConfig?.autoSync ?? true,
customConnection: config.syncConfig?.customConnection,
workerBinding: config.syncConfig?.workerBinding,
};
this.syncManager = new SyncManager(this.cacheManager, reactNativeSyncConfig);
// Set global instances for hooks
setGlobalInstances(this.cacheManager, this.syncManager, this.dbClient);
}
// Implement abstract methods from BaseDataQLClient
async initializeStorage() {
try {
const dbInitialized = await this.dbClient.initializeDatabase();
if (!dbInitialized) {
return false;
}
// Start auto sync if enabled
if (this.syncConfig?.autoSync) {
this.syncManager.startAutoSync();
}
return true;
}
catch (error) {
console.error("[DataQL React Native] Failed to initialize storage:", error);
return false;
}
}
async storeDataLocally(tableName, data) {
try {
await this.cacheManager.createOffline(tableName, data);
// Simple conversion for now - this can be refined later
return {
success: true,
data: data, // Return the original data since it was successfully stored
insertedId: data.id,
};
}
catch (error) {
return {
success: false,
error: error instanceof Error
? error.message
: "Failed to store data locally",
};
}
}
async retrieveDataLocally(tableName, filter) {
try {
const result = await this.cacheManager.queryOffline(tableName, filter);
// Handle both QueryResult format and direct array format
return Array.isArray(result) ? result : result?.data || [];
}
catch (error) {
console.error(`[DataQL React Native] Failed to retrieve data from ${tableName}:`, error);
return [];
}
}
async updateDataLocally(tableName, id, data) {
try {
await this.cacheManager.updateOffline(tableName, id, data);
// Simple conversion for now
return {
success: true,
data: data, // Return the updated data
modifiedCount: 1,
};
}
catch (error) {
return {
success: false,
error: error instanceof Error
? error.message
: "Failed to update data locally",
};
}
}
async deleteDataLocally(tableName, id) {
try {
await this.cacheManager.deleteOffline(tableName, id);
return {
success: true,
deletedCount: 1,
};
}
catch (error) {
return {
success: false,
error: error instanceof Error
? error.message
: "Failed to delete data locally",
};
}
}
async syncWithServer() {
try {
return await this.syncManager.syncNow();
}
catch (error) {
console.error("[DataQL React Native] Sync failed:", error);
return false;
}
}
async getStoredSyncStatus() {
try {
const status = await this.cacheManager.getSyncStatus();
// Convert React Native SyncStatus to Core SyncStatus
return {
lastSyncTime: status.lastSyncTime || undefined,
pendingOperations: status.pendingOperations,
failedOperations: status.failedOperations || 0,
isOnline: status.isOnline,
};
}
catch (error) {
console.error("[DataQL React Native] Failed to get sync status:", error);
return {
pendingOperations: 0,
failedOperations: 0,
isOnline: false,
};
}
}
// React Native specific methods (keeping backward compatibility)
getDatabase() {
return this.dbClient.getDatabase();
}
// Deprecated methods - keeping for backward compatibility but delegating to base class
/** @deprecated Use create() instead */
async createOffline(tableName, data) {
return this.create(tableName, data);
}
/** @deprecated Use update() instead */
async updateOffline(tableName, id, data) {
return this.update(tableName, id, data);
}
/** @deprecated Use delete() instead */
async deleteOffline(tableName, id) {
return this.delete(tableName, id);
}
/** @deprecated Use query() instead */
async queryOffline(tableName, filter) {
return this.query(tableName, filter);
}
// React Native specific sync methods
startAutoSync() {
this.syncManager.startAutoSync();
}
stopAutoSync() {
this.syncManager.stopAutoSync();
}
// Advanced: Access to React Native specific managers
getSyncManager() {
return this.syncManager;
}
getCacheManager() {
return this.cacheManager;
}
// Check online status
isOnline() {
return this.syncManager.getOnlineStatus();
}
// Configuration updates
updateSyncConfig(newConfig) {
// Update base config
if (this.syncConfig) {
Object.assign(this.syncConfig, newConfig);
}
// Update React Native config
this.reactNativeConfig.syncConfig = {
...this.reactNativeConfig.syncConfig,
...newConfig,
};
// Update sync manager
this.syncManager.updateConfig(newConfig);
}
// Override custom connection methods to also update React Native config
setCustomConnection(customConnection) {
super.setCustomConnection(customConnection);
this.reactNativeConfig.syncConfig = this.reactNativeConfig.syncConfig || {};
this.reactNativeConfig.syncConfig.customConnection = customConnection;
this.syncManager.updateConfig({ customConnection });
}
setWorkerBinding(workerBinding) {
super.setWorkerBinding(workerBinding);
this.reactNativeConfig.syncConfig = this.reactNativeConfig.syncConfig || {};
this.reactNativeConfig.syncConfig.workerBinding = workerBinding;
this.syncManager.updateConfig({ workerBinding });
}
clearCustomConnection() {
super.clearCustomConnection();
if (this.reactNativeConfig.syncConfig) {
this.reactNativeConfig.syncConfig.customConnection = undefined;
this.reactNativeConfig.syncConfig.workerBinding = undefined;
}
this.syncManager.updateConfig({
customConnection: undefined,
workerBinding: undefined,
});
}
// Override destroy to clean up React Native specific resources
async destroy() {
try {
this.syncManager.destroy();
await this.dbClient.close();
await super.destroy();
if (this.config.debug) {
console.log("[DataQL React Native] Client destroyed");
}
}
catch (error) {
console.error("[DataQL React Native] Error destroying client:", error);
}
}
// React Native specific getters
getReactNativeConfig() {
return { ...this.reactNativeConfig };
}
}