@roeehrl/tinode-sdk 0.25.1-sqlite.1 → 0.25.1-sqlite.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +349 -32
- package/package.json +7 -3
- package/src/connection.js +24 -2
- package/src/db.js +9 -18
- package/src/index.native.js +9 -1
- package/src/large-file.native.js +367 -0
- package/src/storage-sqlite.js +105 -158
- package/src/topic.js +0 -4
- package/types/index.d.ts +90 -2
- package/umd/tinode.dev.js +123 -3
- package/umd/tinode.dev.js.map +1 -1
- package/umd/tinode.prod.js +1 -1
- package/umd/tinode.prod.js.map +1 -1
- package/version.js +1 -0
package/README.md
CHANGED
|
@@ -1,47 +1,364 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @roeehrl/tinode-sdk
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
at https://web.tinode.co/ and https://sandbox.tinode.co/ ([full source](https://github.com/tinode/webapp)).
|
|
3
|
+
> Fork of [tinode-js](https://github.com/tinode/tinode-js) with React Native support and SQLite persistence
|
|
5
4
|
|
|
6
|
-
This is **
|
|
5
|
+
This is a **modified fork** of the official Tinode JavaScript SDK, specifically adapted for **React Native** applications. It adds persistent storage using SQLite and platform-specific file handling.
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
## What is Tinode?
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
[Tinode](https://github.com/tinode/chat) is an open-source instant messaging backend server. This SDK implements the client-side protocol for connecting to Tinode servers from JavaScript/TypeScript applications.
|
|
10
|
+
|
|
11
|
+
## Fork Overview
|
|
12
|
+
|
|
13
|
+
| Aspect | Original (tinode-js) | This Fork (@roeehrl/tinode-sdk) |
|
|
14
|
+
|--------|----------------------|----------------------------------|
|
|
15
|
+
| Storage | IndexedDB (browser only) | **SQLite via expo-sqlite** (React Native) |
|
|
16
|
+
| File Uploads | Blob API (browser only) | **File URI support** (React Native) |
|
|
17
|
+
| Platform | Web browsers | **React Native (iOS/Android)** |
|
|
18
|
+
| Persistence | Session-based (IndexedDB) | **Permanent** (SQLite database) |
|
|
19
|
+
|
|
20
|
+
## Key Changes
|
|
21
|
+
|
|
22
|
+
### 1. SQLite Storage (`storage-sqlite.js`)
|
|
23
|
+
|
|
24
|
+
**Problem:** Original SDK used IndexedDB, which doesn't exist in React Native.
|
|
25
|
+
|
|
26
|
+
**Solution:** Created `SQLiteStorage` class using `expo-sqlite` with the same API as the original `DB` class:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { Tinode, SQLiteStorage } from '@roeehrl/tinode-sdk';
|
|
30
|
+
|
|
31
|
+
// Create storage instance
|
|
32
|
+
const sqliteStorage = new SQLiteStorage('activon-chat.db');
|
|
33
|
+
|
|
34
|
+
// Set as storage provider BEFORE creating Tinode instance
|
|
35
|
+
Tinode.setStorageProvider(sqliteStorage);
|
|
36
|
+
|
|
37
|
+
// Now Tinode will persist all data to SQLite
|
|
38
|
+
const tinode = new Tinode({ host: '...', apiKey: '...' });
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Database Schema:**
|
|
42
|
+
- `topics` - Chat topics metadata
|
|
43
|
+
- `users` - User profiles
|
|
44
|
+
- `subscriptions` - Topic subscriptions
|
|
45
|
+
- `messages` - Chat messages
|
|
46
|
+
- `dellog` - Deletion log (for sync)
|
|
47
|
+
|
|
48
|
+
### 2. React Native Entry Point (`index.native.js`)
|
|
49
|
+
|
|
50
|
+
Metro bundler automatically selects this file for native platforms:
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
// package.json exports field
|
|
54
|
+
"exports": {
|
|
55
|
+
".": {
|
|
56
|
+
"react-native": "./src/index.native.js", // ← Selected by Metro
|
|
57
|
+
"browser": "./umd/tinode.prod.js"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 3. File Upload Support (`large-file.native.js`)
|
|
63
|
+
|
|
64
|
+
React Native doesn't have the Blob API. This fork provides `LargeFileHelperNative` that accepts **file URIs**:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const { uri } = await DocumentPicker.getDocumentAsync({});
|
|
68
|
+
tinode.getLargeFileHelper().uploadUri(
|
|
69
|
+
uri,
|
|
70
|
+
'photo.jpg',
|
|
71
|
+
'image/jpeg',
|
|
72
|
+
size,
|
|
73
|
+
null,
|
|
74
|
+
(progress) => console.log(progress),
|
|
75
|
+
(success) => console.log('Uploaded!'),
|
|
76
|
+
(error) => console.error(error)
|
|
77
|
+
);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Polyfill Requirements
|
|
83
|
+
|
|
84
|
+
The SDK requires several JavaScript polyfills because **Hermes** (React Native's JavaScript engine) doesn't provide all standard Web APIs.
|
|
85
|
+
|
|
86
|
+
### Required Polyfills
|
|
87
|
+
|
|
88
|
+
| Polyfill | Purpose | Why Needed |
|
|
89
|
+
|----------|---------|------------|
|
|
90
|
+
| `text-encoding-polyfill` | `TextEncoder` / `TextDecoder` | Hermes doesn't have these APIs |
|
|
91
|
+
| `core-js/stable/structured-clone` | `structuredClone()` | Hermes doesn't have this |
|
|
92
|
+
| `unicode-segmenter` | `Intl.Segmenter` | For text segmentation in Drafty parser |
|
|
93
|
+
|
|
94
|
+
### Installation
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
yarn add text-encoding-polyfill core-js unicode-segmenter
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Metro Configuration
|
|
103
|
+
|
|
104
|
+
**Critical:** Metro must have `package exports` enabled to properly resolve `.native.ts` files:
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
// metro.config.js
|
|
108
|
+
const { getDefaultConfig } = require('expo/metro-config');
|
|
109
|
+
|
|
110
|
+
const config = getDefaultConfig(__dirname);
|
|
111
|
+
|
|
112
|
+
// REQUIRED: Enable package exports for platform-specific module resolution
|
|
113
|
+
config.resolver.unstable_enablePackageExports = true;
|
|
114
|
+
|
|
115
|
+
module.exports = config;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Without this**, Metro will ignore the `exports` field in `package.json` and use the wrong entry point.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Setup Guide
|
|
123
|
+
|
|
124
|
+
### Step 1: Install Dependencies
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Tinode SDK fork
|
|
128
|
+
yarn add @roeehrl/tinode-sdk@^0.25.1-sqlite.8
|
|
129
|
+
|
|
130
|
+
# Required peer dependency
|
|
131
|
+
yarn add expo-sqlite
|
|
132
|
+
|
|
133
|
+
# Polyfills
|
|
134
|
+
yarn add text-encoding-polyfill core-js unicode-segmenter
|
|
15
135
|
```
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
136
|
+
|
|
137
|
+
### Step 2: Import Polyfills First
|
|
138
|
+
|
|
139
|
+
**Before** any Tinode imports, import polyfills in your app's entry point:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// App.tsx or index.tsx - TOP OF FILE
|
|
143
|
+
import 'text-encoding-polyfill';
|
|
144
|
+
import 'core-js/stable/structured-clone';
|
|
145
|
+
import 'unicode-segmenter/intl-polyfill';
|
|
146
|
+
|
|
147
|
+
// Now you can import Tinode
|
|
148
|
+
import { Tinode, SQLiteStorage } from '@roeehrl/tinode-sdk';
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Step 3: Configure SQLite Storage
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { Tinode, SQLiteStorage } from '@roeehrl/tinode-sdk';
|
|
155
|
+
|
|
156
|
+
// Create SQLite storage instance
|
|
157
|
+
const sqliteStorage = new SQLiteStorage('activon-chat.db');
|
|
158
|
+
|
|
159
|
+
// Initialize database
|
|
160
|
+
await sqliteStorage.initDatabase();
|
|
161
|
+
|
|
162
|
+
// Set as storage provider
|
|
163
|
+
Tinode.setStorageProvider(sqliteStorage);
|
|
21
164
|
```
|
|
22
165
|
|
|
23
|
-
|
|
166
|
+
### Step 4: Create Tinode Instance
|
|
24
167
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
168
|
+
```typescript
|
|
169
|
+
const tinode = new Tinode({
|
|
170
|
+
host: 'your-tinode-server.com',
|
|
171
|
+
apiKey: 'your-api-key',
|
|
172
|
+
secure: true // use wss:// for secure WebSocket
|
|
173
|
+
});
|
|
174
|
+
```
|
|
29
175
|
|
|
30
|
-
|
|
176
|
+
---
|
|
31
177
|
|
|
32
|
-
|
|
33
|
-
* Consider buying paid support: https://tinode.co/support.html
|
|
34
|
-
* If you are a software developer, send us your pull requests with bug fixes and new features.
|
|
35
|
-
* If you use the SDK and discover bugs or missing features, let us know by filing bug reports and feature requests. Vote for existing [feature requests](https://github.com/tinode/chat/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3A%22feature+request%22) you find most valuable.
|
|
178
|
+
## EAS Build Configuration
|
|
36
179
|
|
|
37
|
-
|
|
180
|
+
For EAS (Expo Application Services) builds, ensure `expo-sqlite` is properly configured:
|
|
38
181
|
|
|
39
|
-
|
|
182
|
+
### app.config.js
|
|
40
183
|
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
184
|
+
```javascript
|
|
185
|
+
export default {
|
|
186
|
+
expo: {
|
|
187
|
+
plugins: [
|
|
188
|
+
['expo-sqlite', {
|
|
189
|
+
// Optional: Enable database restoration on app launch
|
|
190
|
+
enableRestore: true,
|
|
191
|
+
}],
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
};
|
|
45
195
|
```
|
|
46
196
|
|
|
47
|
-
|
|
197
|
+
### eas.json Configuration
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"build": {
|
|
202
|
+
"ios": {
|
|
203
|
+
"bundleIdentifier": "com.yourapp"
|
|
204
|
+
},
|
|
205
|
+
"android": {
|
|
206
|
+
"bundleIdentifier": "com.yourapp"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Limitations & Known Issues
|
|
215
|
+
|
|
216
|
+
### 1. Database File Location
|
|
217
|
+
|
|
218
|
+
- **iOS:** `NSDocumentDirectory` (app's Documents folder)
|
|
219
|
+
- **Android:** App's internal storage
|
|
220
|
+
- Database file name: `activon-chat.db` (configurable)
|
|
221
|
+
|
|
222
|
+
### 2. Database Recovery
|
|
223
|
+
|
|
224
|
+
The SQLite storage includes automatic recovery for stale database handles after app lifecycle events. This handles cases where:
|
|
225
|
+
- App goes to background and comes back
|
|
226
|
+
- Device is locked/unlocked
|
|
227
|
+
- App is suspended and resumed
|
|
228
|
+
|
|
229
|
+
### 3. Hermes Limitations
|
|
230
|
+
|
|
231
|
+
These JavaScript APIs are **not available** in Hermes:
|
|
232
|
+
- `Blob` → Use file URIs instead
|
|
233
|
+
- `URL.createObjectURL()` → Not supported
|
|
234
|
+
- IndexedDB → Replaced by SQLite
|
|
235
|
+
|
|
236
|
+
### 4. Thread Safety
|
|
237
|
+
|
|
238
|
+
expo-sqlite is **not thread-safe**. All database operations must run on the JavaScript thread.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Migration from Original SDK
|
|
243
|
+
|
|
244
|
+
If migrating from the original `tinode-js`:
|
|
245
|
+
|
|
246
|
+
### Before (Original)
|
|
247
|
+
```typescript
|
|
248
|
+
import { Tinode } from 'tinode-sdk';
|
|
249
|
+
|
|
250
|
+
// Uses IndexedDB (doesn't work in React Native)
|
|
251
|
+
const tinode = new Tinode({ host: '...', apiKey: '...' });
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### After (This Fork)
|
|
255
|
+
```typescript
|
|
256
|
+
import 'text-encoding-polyfill';
|
|
257
|
+
import 'core-js/stable/structured-clone';
|
|
258
|
+
import 'unicode-segmenter/intl-polyfill';
|
|
259
|
+
import { Tinode, SQLiteStorage } from '@roeehrl/tinode-sdk';
|
|
260
|
+
|
|
261
|
+
// Setup SQLite storage
|
|
262
|
+
const storage = new SQLiteStorage('chat.db');
|
|
263
|
+
await storage.initDatabase();
|
|
264
|
+
Tinode.setStorageProvider(storage);
|
|
265
|
+
|
|
266
|
+
// Create Tinode instance
|
|
267
|
+
const tinode = new Tinode({ host: '...', apiKey: '...' });
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Platform-Specific Behavior
|
|
273
|
+
|
|
274
|
+
| Feature | iOS | Android | Web |
|
|
275
|
+
|---------|-----|---------|-----|
|
|
276
|
+
| SQLite Storage | ✅ expo-sqlite | ✅ expo-sqlite | ❌ Use IndexedDB |
|
|
277
|
+
| File Uploads | ✅ File URIs | ✅ File URIs | ✅ Blob API |
|
|
278
|
+
| WebSocket | ✅ WSS supported | ✅ WSS supported | ✅ WS supported |
|
|
279
|
+
| Polyfills Required | ✅ Yes | ✅ Yes | ❌ No |
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## API Reference
|
|
284
|
+
|
|
285
|
+
### SQLiteStorage
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
class SQLiteStorage {
|
|
289
|
+
constructor(dbName: string, onError?: Function, logger?: Function)
|
|
290
|
+
|
|
291
|
+
async initDatabase(): Promise<void>
|
|
292
|
+
async deleteDatabase(): Promise<boolean>
|
|
293
|
+
isReady(): boolean
|
|
294
|
+
|
|
295
|
+
// Topics
|
|
296
|
+
async updTopic(topic: Object): Promise<void>
|
|
297
|
+
async remTopic(name: string): Promise<void>
|
|
298
|
+
async mapTopics(callback: Function, context: Object): Promise<Array>
|
|
299
|
+
|
|
300
|
+
// Users
|
|
301
|
+
async updUser(uid: string, pub: Object): Promise<void>
|
|
302
|
+
async remUser(uid: string): Promise<void>
|
|
303
|
+
async mapUsers(callback: Function, context: Object): Promise<Array>
|
|
304
|
+
|
|
305
|
+
// Messages
|
|
306
|
+
async addMessage(msg: Object): Promise<void>
|
|
307
|
+
async remMessages(topic: string, from?: number, to?: number): Promise<void>
|
|
308
|
+
async readMessages(topic: string, query: Object, callback: Function, context: Object): Promise<Array>
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Tinode.setStorageProvider()
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
static setStorageProvider(storage: SQLiteStorage): void
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Sets the storage provider for all Tinode instances. Must be called **before** creating any Tinode instances.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Troubleshooting
|
|
323
|
+
|
|
324
|
+
### "Cannot find module 'expo-sqlite'"
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
yarn add expo-sqlite
|
|
328
|
+
npx expo prebuild --clean
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### "TextEncoder is not defined"
|
|
332
|
+
|
|
333
|
+
Make sure polyfills are imported **before** any Tinode imports:
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
// WRONG
|
|
337
|
+
import { Tinode } from '@roeehrl/tinode-sdk';
|
|
338
|
+
import 'text-encoding-polyfill';
|
|
339
|
+
|
|
340
|
+
// RIGHT
|
|
341
|
+
import 'text-encoding-polyfill';
|
|
342
|
+
import { Tinode } from '@roeehrl/tinode-sdk';
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### "setStorageProvider is not a function"
|
|
346
|
+
|
|
347
|
+
Make sure you're using the React Native entry point. Check that Metro is using `index.native.js`:
|
|
348
|
+
|
|
349
|
+
```javascript
|
|
350
|
+
// In metro.config.js
|
|
351
|
+
config.resolver.unstable_enablePackageExports = true; // ← MUST be true
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## License
|
|
357
|
+
|
|
358
|
+
Apache-2.0 (Same as original Tinode SDK)
|
|
359
|
+
|
|
360
|
+
## Original Project
|
|
361
|
+
|
|
362
|
+
- [Tinode Server](https://github.com/tinode/chat)
|
|
363
|
+
- [Original SDK](https://github.com/tinode/tinode-js)
|
|
364
|
+
- [Documentation](http://tinode.github.io/js-api/)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roeehrl/tinode-sdk",
|
|
3
3
|
"description": "Tinode SDK fork with Storage interface for SQLite persistence in React Native",
|
|
4
|
-
"version": "0.25.1-sqlite.
|
|
4
|
+
"version": "0.25.1-sqlite.11",
|
|
5
5
|
"types": "./types/index.d.ts",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"format": "js-beautify -r src/*.js",
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
"vers": "echo \"export const PACKAGE_VERSION = \\\"`node -p -e \"require('./package.json').version\"`\\\";\" > version.js",
|
|
14
14
|
"test": "jest"
|
|
15
15
|
},
|
|
16
|
-
"browserslist": [
|
|
16
|
+
"browserslist": [
|
|
17
|
+
"defaults"
|
|
18
|
+
],
|
|
17
19
|
"repository": {
|
|
18
20
|
"type": "git",
|
|
19
21
|
"url": "git+https://github.com/roeehrl/tinode-js.git"
|
|
@@ -24,7 +26,8 @@
|
|
|
24
26
|
"umd/tinode.prod.js",
|
|
25
27
|
"umd/tinode.prod.js.map",
|
|
26
28
|
"types/index.d.ts",
|
|
27
|
-
"src/"
|
|
29
|
+
"src/",
|
|
30
|
+
"version.js"
|
|
28
31
|
],
|
|
29
32
|
"keywords": [
|
|
30
33
|
"instant messenger",
|
|
@@ -44,6 +47,7 @@
|
|
|
44
47
|
"react-native": "./src/index.native.js",
|
|
45
48
|
"exports": {
|
|
46
49
|
".": {
|
|
50
|
+
"types": "./types/index.d.ts",
|
|
47
51
|
"react-native": "./src/index.native.js",
|
|
48
52
|
"browser": "./umd/tinode.prod.js",
|
|
49
53
|
"import": "./src/index.js",
|
package/src/connection.js
CHANGED
|
@@ -354,7 +354,11 @@ export default class Connection {
|
|
|
354
354
|
|
|
355
355
|
this.reconnect = force => {
|
|
356
356
|
this.#boffStop();
|
|
357
|
-
|
|
357
|
+
// Catch the promise to prevent unhandled rejection
|
|
358
|
+
// Errors are handled via onDisconnect callback instead
|
|
359
|
+
this.connect(null, force).catch(_ => {
|
|
360
|
+
Connection.#log('LP reconnect promise rejected, error handled via onDisconnect callback');
|
|
361
|
+
});
|
|
358
362
|
};
|
|
359
363
|
|
|
360
364
|
this.disconnect = _ => {
|
|
@@ -463,7 +467,11 @@ export default class Connection {
|
|
|
463
467
|
|
|
464
468
|
this.reconnect = force => {
|
|
465
469
|
this.#boffStop();
|
|
466
|
-
|
|
470
|
+
// Catch the promise to prevent unhandled rejection
|
|
471
|
+
// Errors are handled via onDisconnect callback instead
|
|
472
|
+
this.connect(null, force).catch(_ => {
|
|
473
|
+
Connection.#log('WS reconnect promise rejected, error handled via onDisconnect callback');
|
|
474
|
+
});
|
|
467
475
|
};
|
|
468
476
|
|
|
469
477
|
this.disconnect = _ => {
|
|
@@ -481,6 +489,20 @@ export default class Connection {
|
|
|
481
489
|
if (this.#socket && (this.#socket.readyState == this.#socket.OPEN)) {
|
|
482
490
|
this.#socket.send(msg);
|
|
483
491
|
} else {
|
|
492
|
+
// Socket is not connected - trigger disconnect callback if not already done
|
|
493
|
+
// This handles the case where the server dies but onclose hasn't fired yet
|
|
494
|
+
if (this.onDisconnect) {
|
|
495
|
+
// Use setTimeout to avoid blocking the throw
|
|
496
|
+
setTimeout(() => {
|
|
497
|
+
if (this.onDisconnect) {
|
|
498
|
+
this.onDisconnect(new CommError(NETWORK_ERROR_TEXT, NETWORK_ERROR), NETWORK_ERROR);
|
|
499
|
+
}
|
|
500
|
+
}, 0);
|
|
501
|
+
}
|
|
502
|
+
// Clean up the socket reference if it exists but isn't connected
|
|
503
|
+
if (this.#socket) {
|
|
504
|
+
this.#socket = null;
|
|
505
|
+
}
|
|
484
506
|
throw new Error("Websocket is not connected");
|
|
485
507
|
}
|
|
486
508
|
};
|
package/src/db.js
CHANGED
|
@@ -73,16 +73,10 @@ export default class DB {
|
|
|
73
73
|
* @returns {Promise} promise to be resolved/rejected when the DB is initialized.
|
|
74
74
|
*/
|
|
75
75
|
initDatabase() {
|
|
76
|
-
console.log('[DB] initDatabase CALLED, shouldDelegate:', this.#shouldDelegate(), 'delegateStorage:', !!this.#delegateStorage);
|
|
77
76
|
// Delegate to custom storage if set
|
|
78
77
|
if (this.#shouldDelegate()) {
|
|
79
|
-
|
|
80
|
-
return this.#delegateStorage.initDatabase().then(result => {
|
|
81
|
-
console.log('[DB] initDatabase: SQLiteStorage initialized successfully');
|
|
82
|
-
return result;
|
|
83
|
-
});
|
|
78
|
+
return this.#delegateStorage.initDatabase();
|
|
84
79
|
}
|
|
85
|
-
console.log('[DB] initDatabase using IndexedDB');
|
|
86
80
|
|
|
87
81
|
return new Promise((resolve, reject) => {
|
|
88
82
|
// Open the database and initialize callbacks.
|
|
@@ -220,7 +214,13 @@ export default class DB {
|
|
|
220
214
|
* @returns {Promise} promise resolved/rejected on operation completion.
|
|
221
215
|
*/
|
|
222
216
|
updTopic(topic) {
|
|
223
|
-
|
|
217
|
+
// Skip topics that haven't been confirmed by the server yet.
|
|
218
|
+
// The _new flag is true for topics created locally but not yet subscribed.
|
|
219
|
+
// Only persist after subscribe succeeds and server assigns the real topic name.
|
|
220
|
+
if (topic?._new) {
|
|
221
|
+
return Promise.resolve();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
224
|
// Delegate to custom storage if set
|
|
225
225
|
if (this.#shouldDelegate()) {
|
|
226
226
|
return this.#delegateStorage.updTopic(topic);
|
|
@@ -325,16 +325,10 @@ export default class DB {
|
|
|
325
325
|
* @return {Promise} promise resolved/rejected on operation completion.
|
|
326
326
|
*/
|
|
327
327
|
mapTopics(callback, context) {
|
|
328
|
-
console.log('[DB] mapTopics CALLED, shouldDelegate:', this.#shouldDelegate());
|
|
329
328
|
// Delegate to custom storage if set
|
|
330
329
|
if (this.#shouldDelegate()) {
|
|
331
|
-
|
|
332
|
-
return this.#delegateStorage.mapTopics(callback, context).then(result => {
|
|
333
|
-
console.log('[DB] mapTopics: SQLiteStorage returned', result ? result.length : 0, 'topics');
|
|
334
|
-
return result;
|
|
335
|
-
});
|
|
330
|
+
return this.#delegateStorage.mapTopics(callback, context);
|
|
336
331
|
}
|
|
337
|
-
console.log('[DB] mapTopics using IndexedDB');
|
|
338
332
|
return this.#mapObjects('topic', callback, context);
|
|
339
333
|
}
|
|
340
334
|
|
|
@@ -663,13 +657,10 @@ export default class DB {
|
|
|
663
657
|
* @return {Promise} promise resolved/rejected on operation completion.
|
|
664
658
|
*/
|
|
665
659
|
readMessages(topicName, query, callback, context) {
|
|
666
|
-
console.log('[DB] readMessages CALLED:', topicName, 'shouldDelegate:', this.#shouldDelegate(), 'delegateStorage:', !!this.#delegateStorage);
|
|
667
660
|
// Delegate to custom storage if set
|
|
668
661
|
if (this.#shouldDelegate()) {
|
|
669
|
-
console.log('[DB] readMessages DELEGATING to SQLiteStorage');
|
|
670
662
|
return this.#delegateStorage.readMessages(topicName, query, callback, context);
|
|
671
663
|
}
|
|
672
|
-
console.log('[DB] readMessages NOT delegating, using IndexedDB');
|
|
673
664
|
|
|
674
665
|
query = query || {};
|
|
675
666
|
|
package/src/index.native.js
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* This is the entry point for React Native (iOS/Android) environments.
|
|
5
5
|
* Metro bundler automatically picks this file for native platforms.
|
|
6
6
|
*
|
|
7
|
-
* Exports
|
|
7
|
+
* Exports:
|
|
8
|
+
* - SQLiteStorage for persistent storage using expo-sqlite
|
|
9
|
+
* - LargeFileHelperNative for file uploads using file URIs
|
|
8
10
|
*
|
|
9
11
|
* @module tinode-sdk
|
|
10
12
|
* @copyright 2015-2025 Tinode LLC, Activon
|
|
@@ -33,3 +35,9 @@ export {
|
|
|
33
35
|
default as SQLiteStorage
|
|
34
36
|
}
|
|
35
37
|
from './storage-sqlite.js';
|
|
38
|
+
|
|
39
|
+
// Export LargeFileHelperNative for React Native file uploads
|
|
40
|
+
export {
|
|
41
|
+
default as LargeFileHelperNative
|
|
42
|
+
}
|
|
43
|
+
from './large-file.native.js';
|