@saltcorn/mobile-app 1.1.0-beta.11 → 1.1.0-beta.13

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.
Files changed (39) hide show
  1. package/.babelrc +3 -0
  2. package/build_scripts/modify_android_manifest.js +47 -0
  3. package/build_scripts/modify_gradle_cfg.js +34 -0
  4. package/package.json +20 -11
  5. package/src/.eslintrc +21 -0
  6. package/src/helpers/api.js +41 -0
  7. package/src/helpers/auth.js +191 -0
  8. package/src/helpers/common.js +175 -0
  9. package/{www/js/utils/table_utils.js → src/helpers/db_schema.js} +18 -40
  10. package/src/helpers/file_system.js +102 -0
  11. package/{www/js/utils/global_utils.js → src/helpers/navigation.js} +169 -335
  12. package/src/helpers/offline_mode.js +645 -0
  13. package/src/index.js +20 -0
  14. package/src/init.js +424 -0
  15. package/src/routing/index.js +98 -0
  16. package/{www/js → src/routing}/mocks/request.js +5 -5
  17. package/{www/js → src/routing}/mocks/response.js +1 -1
  18. package/{www/js → src/routing}/routes/api.js +10 -15
  19. package/{www/js → src/routing}/routes/auth.js +12 -6
  20. package/{www/js → src/routing}/routes/delete.js +9 -6
  21. package/{www/js → src/routing}/routes/edit.js +9 -6
  22. package/src/routing/routes/error.js +6 -0
  23. package/{www/js → src/routing}/routes/fields.js +7 -2
  24. package/{www/js → src/routing}/routes/page.js +14 -9
  25. package/{www/js → src/routing}/routes/sync.js +9 -5
  26. package/{www/js → src/routing}/routes/view.js +16 -11
  27. package/{www/js/routes/common.js → src/routing/utils.js} +15 -13
  28. package/webpack.config.js +31 -0
  29. package/www/data/encoded_site_logo.js +1 -0
  30. package/www/index.html +23 -491
  31. package/www/js/{utils/iframe_view_utils.js → iframe_view_utils.js} +137 -269
  32. package/config.xml +0 -27
  33. package/res/icon/android/icon.png +0 -0
  34. package/res/screen/android/splash-icon.png +0 -0
  35. package/res/screen/ios/Default@2x~universal~anyany.png +0 -0
  36. package/www/js/routes/error.js +0 -5
  37. package/www/js/routes/init.js +0 -76
  38. package/www/js/utils/file_helpers.js +0 -108
  39. package/www/js/utils/offline_mode_helper.js +0 -625
@@ -1,625 +0,0 @@
1
- /*global window, $, apiCall, saltcorn, navigator, clearAlerts*/
2
-
3
- var offlineHelper = (() => {
4
- const setUploadStarted = async (started, time) => {
5
- const state = saltcorn.data.state.getState();
6
- const oldSession = await state.getConfig("last_offline_session");
7
- const newSession = { ...oldSession };
8
- newSession.uploadStarted = started;
9
- if (started) newSession.uploadStartTime = time;
10
- else newSession.uploadStartTime = null;
11
- await state.setConfig("last_offline_session", newSession);
12
- };
13
-
14
- const maxLastModified = async (tblName) => {
15
- const result = await saltcorn.data.db.query(
16
- `select max(last_modified) "max" from "${saltcorn.data.db.sqlsanitize(
17
- tblName
18
- )}_sync_info"`
19
- );
20
- return result.rows?.length > 0 ? new Date(result.rows[0].max) : null;
21
- };
22
-
23
- const prepare = async () => {
24
- const state = saltcorn.data.state.getState();
25
- const { synchedTables } = state.mobileConfig;
26
- const syncInfos = {};
27
- for (const tblName of synchedTables) {
28
- const syncInfo = { maxLoadedId: 0 };
29
- const maxLm = await maxLastModified(tblName);
30
- if (maxLm) syncInfo.syncFrom = maxLm.valueOf();
31
- syncInfos[tblName] = syncInfo;
32
- }
33
- return { synchedTables, syncInfos };
34
- };
35
-
36
- const insertRemoteData = async (table, rows, syncTimestamp) => {
37
- const tblName = table.name;
38
- const pkName = table.pk_name;
39
- for (const row of rows) {
40
- const {
41
- _sync_info_tbl_ref_,
42
- _sync_info_tbl_last_modified_,
43
- _sync_info_tbl_deleted_,
44
- ...rest
45
- } = row;
46
- const ref = _sync_info_tbl_ref_,
47
- last_modified = _sync_info_tbl_last_modified_;
48
- await saltcorn.data.db.insert(tblName, rest, { replace: true });
49
- const infos = await saltcorn.data.db.select(
50
- `${saltcorn.data.db.sqlsanitize(tblName)}_sync_info`,
51
- {
52
- ref: rest[pkName],
53
- }
54
- );
55
- if (infos.length > 0) {
56
- await saltcorn.data.db.query(
57
- `update "${saltcorn.data.db.sqlsanitize(tblName)}_sync_info"
58
- set last_modified=${last_modified}, deleted=false, modified_local = false
59
- where ref=${rest[pkName]}`
60
- );
61
- } else {
62
- await saltcorn.data.db.insert(
63
- `${saltcorn.data.db.sqlsanitize(tblName)}_sync_info`,
64
- {
65
- ref,
66
- last_modified: syncTimestamp,
67
- deleted: false,
68
- modified_local: false,
69
- }
70
- );
71
- }
72
- }
73
- };
74
-
75
- const syncRemoteData = async (syncInfos, syncTimestamp) => {
76
- let iterations = 200;
77
- let hasMoreData = true;
78
- const idToTable = {};
79
- const getTable = (tblName) => {
80
- if (!idToTable[tblName])
81
- idToTable[tblName] = saltcorn.data.models.Table.findOne({
82
- name: tblName,
83
- });
84
- return idToTable[tblName];
85
- };
86
- while (hasMoreData && --iterations > 0) {
87
- hasMoreData = false;
88
- const loadResp = await apiCall({
89
- method: "POST",
90
- path: "/sync/load_changes",
91
- body: {
92
- syncInfos,
93
- loadUntil: syncTimestamp,
94
- },
95
- });
96
- for (const [tblName, { rows, maxLoadedId }] of Object.entries(
97
- loadResp.data
98
- )) {
99
- if (rows?.length > 0) {
100
- const table = getTable(tblName);
101
- hasMoreData = true;
102
- await insertRemoteData(table, rows, syncTimestamp);
103
- syncInfos[tblName].maxLoadedId = maxLoadedId;
104
- }
105
- }
106
- }
107
- };
108
-
109
- const prepDeletes = async (table, deletes) => {
110
- let result = [...deletes];
111
- const tblName = table.name;
112
- const pkName = table.pk_name;
113
- // don't delete if it's local modifed or unsynched
114
- const tblConflicts = await saltcorn.data.db.query(
115
- `select ref from "${saltcorn.data.db.sqlsanitize(tblName)}_sync_info"
116
- where ref in (${deletes.map(({ ref }) => ref).join(",")}) and
117
- (last_modified is null or modified_local = true)`
118
- );
119
- if (tblConflicts.rows.length > 0) {
120
- // make it an insert, so that it re-appears on the server
121
- const conflicts = tblConflicts.rows.map(({ ref }) => ref);
122
- await saltcorn.data.db.query(
123
- `update "${saltcorn.data.db.sqlsanitize(tblName)}_sync_info"
124
- set last_modified = null, modified_local = true
125
- where ref in (${conflicts.join(",")})`
126
- );
127
- const conflictsSet = new Set(conflicts);
128
- result = result.filter((del) => !conflictsSet.has(del.ref));
129
- }
130
-
131
- // don't delete if it's referenced by offline data
132
- for (const field of await saltcorn.data.models.Field.find({
133
- reftable_name: tblName,
134
- })) {
135
- const srcTbl = saltcorn.data.models.Table.findOne(field.table_id);
136
- const { synchedTables } = saltcorn.data.state.getState().mobileConfig;
137
- if (synchedTables.indexOf(srcTbl.name) >= 0) {
138
- const fkConflicts = await saltcorn.data.db.query(
139
- `select data_tbl."${saltcorn.data.db.sqlsanitize(
140
- field.name
141
- )}" from "${saltcorn.data.db.sqlsanitize(
142
- srcTbl.name
143
- )}" as data_tbl join "${saltcorn.data.db.sqlsanitize(
144
- srcTbl.name
145
- )}_sync_info" as info_tbl
146
- on data_tbl."${saltcorn.data.db.sqlsanitize(pkName)}" = info_tbl.ref
147
- where data_tbl."${saltcorn.data.db.sqlsanitize(
148
- field.name
149
- )}" in (${result.map(({ ref }) => ref).join(",")})
150
- and (info_tbl.last_modified is null or info_tbl.modified_local = true)`
151
- );
152
- if (fkConflicts.rows.length > 0) {
153
- // make it an insert
154
- const conflicts = fkConflicts.rows.map(
155
- (conflict) => conflict[field.name]
156
- );
157
- const conflictsSet = new Set(conflicts);
158
- result = result.filter((del) => !conflictsSet.has(del.ref));
159
- await saltcorn.data.db.query(
160
- `update "${saltcorn.data.db.sqlsanitize(tblName)}_sync_info"
161
- set last_modified = null, modified_local = true
162
- where ref in (${conflicts.join(",")})`
163
- );
164
- }
165
- }
166
- }
167
- return result;
168
- };
169
-
170
- const applyDeletes = async (allDeletes, syncTimestamp) => {
171
- for (const [tblName, deletes] of Object.entries(allDeletes)) {
172
- if (deletes.length > 0) {
173
- const table = saltcorn.data.models.Table.findOne({ name: tblName });
174
- const pkName = table.pk_name;
175
- const safeDeletes = await prepDeletes(table, deletes);
176
- if (safeDeletes.length > 0) {
177
- const delIds = safeDeletes.map(({ ref }) => ref).join(",");
178
- await saltcorn.data.db.query(
179
- `delete from "${saltcorn.data.db.sqlsanitize(
180
- tblName
181
- )}" where "${saltcorn.data.db.sqlsanitize(pkName)}" in (${delIds})`
182
- );
183
- await saltcorn.data.db.query(
184
- `update "${saltcorn.data.db.sqlsanitize(tblName)}_sync_info"
185
- set deleted = true, last_modified = ${syncTimestamp}, modified_local = false
186
- where ref in (${delIds}) and deleted = false`
187
- );
188
- }
189
- }
190
- }
191
- };
192
-
193
- const syncRemoteDeletes = async (syncInfos, syncTimestamp) => {
194
- const delResp = await apiCall({
195
- method: "POST",
196
- path: "/sync/deletes",
197
- body: {
198
- syncTimestamp,
199
- syncInfos,
200
- },
201
- });
202
- const { deletes } = delResp.data;
203
- await applyDeletes(deletes, syncTimestamp);
204
- };
205
-
206
- const loadOfflineChanges = async (synchedTbls) => {
207
- const result = {};
208
- for (const synchedTbl of synchedTbls) {
209
- const table = saltcorn.data.models.Table.findOne({ name: synchedTbl });
210
- const pkName = table.pk_name;
211
- const localModified = await saltcorn.data.db.query(
212
- `select
213
- info_tbl.ref as _sync_info_ref_, info_tbl.last_modified as _sync_info_last_modified_,
214
- info_tbl.deleted as _sync_info_deleted_, info_tbl.modified_local as _sync_info_modified_local_,
215
- data_tbl.*
216
- from "${saltcorn.data.db.sqlsanitize(
217
- synchedTbl
218
- )}_sync_info" as info_tbl left join "${saltcorn.data.db.sqlsanitize(
219
- synchedTbl
220
- )}" as data_tbl
221
- on info_tbl.ref = data_tbl."${saltcorn.data.db.sqlsanitize(pkName)}"
222
- where info_tbl.modified_local = true`
223
- );
224
- const inserts = [];
225
- const updates = [];
226
- const deletes = [];
227
- for (const row of localModified.rows) {
228
- const {
229
- _sync_info_ref_,
230
- _sync_info_last_modified_,
231
- _sync_info_deleted_,
232
- _sync_info_modified_local_,
233
- ...rest
234
- } = row;
235
- const ref = _sync_info_ref_,
236
- last_modified = _sync_info_last_modified_,
237
- deleted = _sync_info_deleted_;
238
- if (deleted)
239
- deletes.push({
240
- [pkName]: ref,
241
- last_modified,
242
- });
243
- else if (rest[pkName]) {
244
- if (!last_modified) inserts.push(rest);
245
- else updates.push(rest);
246
- }
247
- }
248
- const changes = {};
249
- if (inserts.length > 0) changes.inserts = inserts;
250
- if (updates.length > 0) changes.updates = updates;
251
- if (deletes.length > 0) changes.deletes = deletes;
252
- if (Object.keys(changes).length > 0) result[synchedTbl] = changes;
253
- }
254
- return result;
255
- };
256
-
257
- const handleTranslatedIds = async (allUniqueConflicts, allTranslations) => {
258
- const idToTable = {};
259
- for (const [tblName, translations] of Object.entries(allTranslations)) {
260
- const fks = await saltcorn.data.models.Field.find({
261
- reftable_name: tblName,
262
- });
263
- const uniqueConflicts = (allUniqueConflicts[tblName] =
264
- allUniqueConflicts[tblName] || []);
265
- const table = saltcorn.data.models.Table.findOne({ name: tblName });
266
- const transArr = Array.from(Object.entries(translations));
267
- transArr.sort((a, b) => parseInt(b[1]) - parseInt(a[1]));
268
- for (const [from, to] of transArr) {
269
- if (!uniqueConflicts.find((conf) => conf[table.pk_name] === to)) {
270
- await saltcorn.data.db.update(tblName, { [table.pk_name]: to }, from);
271
- await saltcorn.data.db.query(
272
- `update "${saltcorn.data.db.sqlsanitize(tblName)}_sync_info"
273
- set ref = ${to}
274
- where ref = ${from} and deleted = false`
275
- );
276
- }
277
- for (const fk of fks) {
278
- if (!idToTable[fk.table_id])
279
- idToTable[fk.table_id] = saltcorn.data.models.Table.findOne(
280
- fk.table_id
281
- );
282
- const refTable = idToTable[fk.table_id];
283
- await saltcorn.data.db.query(
284
- `update "${saltcorn.data.db.sqlsanitize(
285
- refTable.name
286
- )}" set "${saltcorn.data.db.sqlsanitize(fk.name)}" = ${to}
287
- where "${fk.name}" = ${from}`
288
- );
289
- }
290
- }
291
- }
292
- };
293
-
294
- const handleUniqueConflicts = async (uniqueConflicts, translatedIds) => {
295
- for (const [tblName, conflicts] of Object.entries(uniqueConflicts)) {
296
- const table = saltcorn.data.models.Table.findOne({ name: tblName });
297
- const pkName = table.pk_name || "id";
298
- const translated = translatedIds[tblName] || {};
299
- for (const conflict of conflicts) {
300
- for (const [from, to] of Object.entries(translated)) {
301
- if (to === conflict[pkName]) {
302
- await table.deleteRows({ [pkName]: from });
303
- await saltcorn.data.db.deleteWhere(`${table.name}_sync_info`, {
304
- ref: from,
305
- });
306
- }
307
- }
308
- await saltcorn.data.db.insert(tblName, conflict, { replace: true });
309
- }
310
- }
311
- };
312
-
313
- const updateSyncInfos = async (
314
- offlineChanges,
315
- allTranslations,
316
- syncTimestamp
317
- ) => {
318
- const update = async (tblName, changes, deleted = false) => {
319
- const table = saltcorn.data.models.Table.findOne({ name: tblName });
320
- const pkName = table.pk_name;
321
- const translated = allTranslations[tblName];
322
- const refIds = Array.from(
323
- new Set(
324
- changes.map((change) =>
325
- deleted
326
- ? change[pkName]
327
- : translated?.[change[pkName]] || change[pkName]
328
- )
329
- )
330
- );
331
- const values = refIds.map(
332
- (ref) => `(${ref}, ${syncTimestamp}, ${deleted}, false)`
333
- );
334
- await saltcorn.data.db.query(
335
- `delete from "${saltcorn.data.db.sqlsanitize(tblName)}_sync_info"
336
- where ref in (${refIds.join(",")})`
337
- );
338
- await saltcorn.data.db.query(
339
- `insert into "${saltcorn.data.db.sqlsanitize(tblName)}_sync_info"
340
- (ref, last_modified, deleted, modified_local)
341
- values ${values.join(",")}
342
- `
343
- );
344
- };
345
- for (const [tblName, tblChanges] of Object.entries(offlineChanges)) {
346
- if (tblChanges.inserts) await update(tblName, tblChanges.inserts);
347
- if (tblChanges.updates) await update(tblName, tblChanges.updates);
348
- if (tblChanges.deletes) await update(tblName, tblChanges.deletes, true);
349
- }
350
- };
351
-
352
- const syncOfflineData = async (synchedTables, syncTimestamp) => {
353
- const offlineChanges = await loadOfflineChanges(synchedTables);
354
- if (Object.keys(offlineChanges).length === 0) return null;
355
- const uploadResp = await apiCall({
356
- method: "POST",
357
- path: "/sync/offline_changes",
358
- body: {
359
- changes: offlineChanges,
360
- syncTimestamp,
361
- },
362
- });
363
- const { syncDir } = uploadResp.data;
364
- let pollCount = 0;
365
- while (pollCount < 60) {
366
- await saltcorn.data.utils.sleep(1000);
367
- const pollResp = await apiCall({
368
- method: "GET",
369
- path: `/sync/upload_finished?dir_name=${encodeURIComponent(syncDir)}`,
370
- });
371
- pollCount++;
372
- const { finished, translatedIds, uniqueConflicts, error } = pollResp.data;
373
- if (finished) {
374
- if (error) throw new Error(error.message);
375
- else {
376
- await handleUniqueConflicts(uniqueConflicts, translatedIds);
377
- await handleTranslatedIds(uniqueConflicts, translatedIds);
378
- await updateSyncInfos(offlineChanges, translatedIds, syncTimestamp);
379
- return syncDir;
380
- }
381
- } else
382
- console.log(`poll for syncResult '${syncTimestamp}': ${pollCount}`);
383
- }
384
- throw new Error("Unable to get the translatedIds");
385
- };
386
-
387
- const cleanSyncDir = async (syncDir) => {
388
- try {
389
- await apiCall({
390
- method: "POST",
391
- path: "/sync/clean_sync_dir",
392
- body: {
393
- dir_name: syncDir,
394
- },
395
- });
396
- } catch (error) {
397
- console.log(`Unable to clean ${syncDir}`);
398
- console.log(error);
399
- }
400
- };
401
-
402
- /*
403
- * check if the server did an upload which didnt't came back to the app
404
- * the phone shut down or the app was stopped in between
405
- * Then the data is already uploaded and to avoid conflicts, we do a clean full sync
406
- */
407
- const checkCleanSync = async (uploadStarted, uploadStartTime, userName) => {
408
- if (uploadStarted) {
409
- const oldSyncDir = `${uploadStartTime}_${userName}`;
410
- const resp = await apiCall({
411
- method: "GET",
412
- path: `/sync/upload_finished?dir_name=${encodeURIComponent(
413
- oldSyncDir
414
- )}`,
415
- });
416
- const { finished, error } = resp.data;
417
- if (finished && !error) return true;
418
- else await cleanSyncDir(oldSyncDir);
419
- }
420
- return false;
421
- };
422
-
423
- const getSyncTimestamp = async () => {
424
- const resp = await apiCall({
425
- method: "GET",
426
- path: `/sync/sync_timestamp`,
427
- });
428
- return resp.data.syncTimestamp;
429
- };
430
-
431
- const setSpinnerText = () => {
432
- const iframeWindow = $("#content-iframe")[0].contentWindow;
433
- if (iframeWindow) {
434
- const spinnerText =
435
- iframeWindow.document.getElementById("scspinner-text-id");
436
- if (spinnerText) {
437
- spinnerText.innerHTML = "Syncing, please don't turn off";
438
- spinnerText.classList.remove("d-none");
439
- }
440
- }
441
- };
442
-
443
- return {
444
- sync: async () => {
445
- setSpinnerText();
446
- const state = saltcorn.data.state.getState();
447
- const mobileConfig = state.mobileConfig;
448
- const { offlineUser, hasOfflineData, uploadStarted, uploadStartTime } =
449
- (await offlineHelper.getLastOfflineSession()) || {};
450
- if (
451
- offlineUser &&
452
- hasOfflineData &&
453
- offlineUser !== mobileConfig.user_name
454
- ) {
455
- throw new Error(
456
- `The sync is not available, '${offlineUser}' has not yet uploaded offline data.`
457
- );
458
- } else {
459
- let syncDir = null;
460
- let cleanSync = await checkCleanSync(
461
- uploadStarted,
462
- uploadStartTime,
463
- mobileConfig.user_name
464
- );
465
- const syncTimestamp = await getSyncTimestamp();
466
- await setUploadStarted(true, syncTimestamp);
467
- let lock = null;
468
- try {
469
- if (window.navigator?.wakeLock?.request)
470
- lock = await window.navigator.wakeLock.request();
471
- } catch (error) {
472
- console.log("wakeLock not available");
473
- console.log(error);
474
- }
475
- let transactionOpen = false;
476
- try {
477
- await saltcorn.data.db.query("PRAGMA foreign_keys = OFF;");
478
- await saltcorn.data.db.query("BEGIN");
479
- transactionOpen = true;
480
- if (cleanSync) await offlineHelper.clearLocalData(true);
481
- const { synchedTables, syncInfos } = await prepare();
482
- await syncRemoteDeletes(syncInfos, syncTimestamp);
483
- syncDir = await syncOfflineData(synchedTables, syncTimestamp);
484
- await syncRemoteData(syncInfos, syncTimestamp);
485
- await offlineHelper.endOfflineMode(true);
486
- await setUploadStarted(false);
487
- await saltcorn.data.db.query("COMMIT");
488
- transactionOpen = false;
489
- await saltcorn.data.db.query("PRAGMA foreign_keys = ON;");
490
- } catch (error) {
491
- if (transactionOpen) await saltcorn.data.db.query("ROLLBACK");
492
- await saltcorn.data.db.query("PRAGMA foreign_keys = ON;");
493
- console.log(error);
494
- throw error;
495
- } finally {
496
- if (syncDir) await cleanSyncDir(syncDir);
497
- if (lock) await lock.release();
498
- }
499
- }
500
- },
501
-
502
- startOfflineMode: async () => {
503
- const state = saltcorn.data.state.getState();
504
- const mobileConfig = state.mobileConfig;
505
- const oldSession = await offlineHelper.getLastOfflineSession();
506
- if (!oldSession) {
507
- await offlineHelper.setOfflineSession({
508
- offlineUser: mobileConfig.user_name,
509
- });
510
- } else if (
511
- oldSession.offlineUser &&
512
- oldSession.offlineUser !== mobileConfig.user_name
513
- ) {
514
- if (oldSession.hasOfflineData)
515
- throw new Error(
516
- `The offline mode is not available, '${oldSession.offlineUser}' has not yet uploaded offline data.`
517
- );
518
- } else if (oldSession.uploadStarted) {
519
- throw new Error(
520
- `A previous Synchronization did not finish. Please ${
521
- mobileConfig.networkState === "none" ? "go online and " : ""
522
- } try it again.`
523
- );
524
- } else {
525
- await offlineHelper.setOfflineSession({
526
- offlineUser: mobileConfig.user_name,
527
- });
528
- }
529
- mobileConfig.isOfflineMode = true;
530
- },
531
- endOfflineMode: async (endSession = false) => {
532
- const state = saltcorn.data.state.getState();
533
- const mobileConfig = state.mobileConfig;
534
- mobileConfig.isOfflineMode = false;
535
- const oldSession = await offlineHelper.getLastOfflineSession();
536
- if (
537
- (!oldSession?.uploadStarted &&
538
- !(await offlineHelper.hasOfflineRows())) ||
539
- endSession
540
- )
541
- await state.setConfig("last_offline_session", null);
542
- },
543
- getLastOfflineSession: async () => {
544
- const state = saltcorn.data.state.getState();
545
- return await state.getConfig("last_offline_session");
546
- },
547
- setOfflineSession: async (sessObj) => {
548
- const state = saltcorn.data.state.getState();
549
- await state.setConfig("last_offline_session", sessObj);
550
- },
551
- setHasOfflineData: async (hasOfflineData) => {
552
- const offlineSession = await offlineHelper.getLastOfflineSession();
553
- if (offlineSession?.hasOfflineData !== hasOfflineData) {
554
- offlineSession.hasOfflineData = hasOfflineData;
555
- await offlineHelper.setOfflineSession(offlineSession);
556
- }
557
- },
558
- clearLocalData: async (inTransaction) => {
559
- try {
560
- await saltcorn.data.db.query("PRAGMA foreign_keys = OFF;");
561
- if (!inTransaction) await saltcorn.data.db.query("BEGIN");
562
- const { synchedTables } = saltcorn.data.state.getState().mobileConfig;
563
- for (const tblName of synchedTables) {
564
- const table = saltcorn.data.models.Table.findOne({ name: tblName });
565
- await table.deleteRows();
566
- await saltcorn.data.db.deleteWhere(`${table.name}_sync_info`, {});
567
- }
568
- if (!inTransaction) await saltcorn.data.db.query("COMMIT");
569
- await saltcorn.data.db.query("PRAGMA foreign_keys = ON;");
570
- } catch (error) {
571
- if (!inTransaction) {
572
- await saltcorn.data.db.query("ROLLBACK");
573
- await saltcorn.data.db.query("PRAGMA foreign_keys = ON;");
574
- }
575
- throw error;
576
- }
577
- },
578
- offlineCallback: async () => {
579
- const mobileConfig = saltcorn.data.state.getState().mobileConfig;
580
- mobileConfig.networkState = navigator.connection.type;
581
- },
582
- onlineCallback: async () => {
583
- const mobileConfig = saltcorn.data.state.getState().mobileConfig;
584
- if (mobileConfig.isOfflineMode) {
585
- const iframeWindow = $("#content-iframe")[0].contentWindow;
586
- if (iframeWindow) {
587
- clearAlerts();
588
- iframeWindow.notifyAlert(
589
- `An internet connection is available, to end the offline mode click ${saltcorn.markup.a(
590
- {
591
- href: "javascript:execLink('/sync/sync_settings')",
592
- },
593
- "here"
594
- )}`
595
- );
596
- }
597
- }
598
- mobileConfig.networkState = navigator.connection.type;
599
- },
600
- hasOfflineRows: async () => {
601
- const { synchedTables } = saltcorn.data.state.getState().mobileConfig;
602
- for (const tblName of synchedTables) {
603
- const table = saltcorn.data.models.Table.findOne({ name: tblName });
604
- const pkName = table.pk_name;
605
- const { rows } = await saltcorn.data.db.query(
606
- `select count(info_tbl.ref)
607
- from "${saltcorn.data.db.sqlsanitize(
608
- tblName
609
- )}_sync_info" as info_tbl
610
- join "${saltcorn.data.db.sqlsanitize(tblName)}" as data_tbl
611
- on info_tbl.ref = data_tbl."${saltcorn.data.db.sqlsanitize(pkName)}"
612
- where info_tbl.modified_local = true`
613
- );
614
- if (rows?.length > 0 && parseInt(rows[0].count) > 0) return true;
615
- }
616
- return false;
617
- },
618
- getOfflineMsg: () => {
619
- const { networkState } = saltcorn.data.state.getState().mobileConfig;
620
- return networkState === "none"
621
- ? "You are offline."
622
- : "You are offline, an internet connection is available.";
623
- },
624
- };
625
- })();