@saltcorn/mobile-app 1.5.0-beta.14 → 1.5.0-beta.15

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@saltcorn/mobile-app",
3
3
  "displayName": "Saltcorn mobile app",
4
- "version": "1.5.0-beta.14",
4
+ "version": "1.5.0-beta.15",
5
5
  "description": "Saltcorn mobile app for Android and iOS",
6
6
  "main": "index.js",
7
7
  "scripts": {
@@ -69,7 +69,7 @@ const initialSync = async (config) => {
69
69
  const alerts = [];
70
70
  const { offlineUser, hasOfflineData } = (await getLastOfflineSession()) || {};
71
71
  if (!offlineUser || offlineUser === config.user.email) {
72
- await sync();
72
+ await sync(false, alerts);
73
73
  } else {
74
74
  if (hasOfflineData)
75
75
  alerts.push({
@@ -78,7 +78,7 @@ const initialSync = async (config) => {
78
78
  });
79
79
  else {
80
80
  await deleteOfflineData(true);
81
- await sync();
81
+ await sync(false, alerts);
82
82
  }
83
83
  }
84
84
  return alerts;
@@ -318,6 +318,27 @@ const handleUniqueConflicts = async (uniqueConflicts, translatedIds) => {
318
318
  }
319
319
  };
320
320
 
321
+ const handleUpdateConflicts = async (dataConflicts, alerts) => {
322
+ let hasConflicts = false;
323
+ for (const [tblName, updates] of Object.entries(dataConflicts)) {
324
+ const table = saltcorn.data.models.Table.findOne({ name: tblName });
325
+ const pkName = table.pk_name || "id";
326
+ for (const update of updates) {
327
+ const { [pkName]: _sc_pkValue, ...rest } = update;
328
+ await table.updateRow(rest, _sc_pkValue);
329
+ hasConflicts = true;
330
+ }
331
+ }
332
+ if (hasConflicts) {
333
+ alerts.push({
334
+ type: "info",
335
+ msg:
336
+ "Some of your changes could not be applied because the data has changed on the server. " +
337
+ "Your local data has been updated accordingly.",
338
+ });
339
+ }
340
+ };
341
+
321
342
  const updateSyncInfos = async (
322
343
  offlineChanges,
323
344
  allTranslations,
@@ -357,7 +378,7 @@ const updateSyncInfos = async (
357
378
  }
358
379
  };
359
380
 
360
- const syncOfflineData = async (synchedTables, syncTimestamp) => {
381
+ const syncOfflineData = async (synchedTables, syncTimestamp, alerts) => {
361
382
  const offlineChanges = await loadOfflineChanges(synchedTables);
362
383
  if (Object.keys(offlineChanges).length === 0) return null;
363
384
  const uploadResp = await apiCall({
@@ -365,7 +386,8 @@ const syncOfflineData = async (synchedTables, syncTimestamp) => {
365
386
  path: "/sync/offline_changes",
366
387
  body: {
367
388
  changes: offlineChanges,
368
- syncTimestamp,
389
+ oldSyncTimestamp: await getLocalSyncTimestamp(),
390
+ newSyncTimestamp: syncTimestamp,
369
391
  },
370
392
  });
371
393
  const { syncDir } = uploadResp.data;
@@ -377,10 +399,12 @@ const syncOfflineData = async (synchedTables, syncTimestamp) => {
377
399
  path: `/sync/upload_finished?dir_name=${encodeURIComponent(syncDir)}`,
378
400
  });
379
401
  pollCount++;
380
- const { finished, translatedIds, uniqueConflicts, error } = pollResp.data;
402
+ const { finished, translatedIds, uniqueConflicts, dataConflicts, error } =
403
+ pollResp.data;
381
404
  if (finished) {
382
405
  if (error) throw new Error(error.message);
383
406
  else {
407
+ await handleUpdateConflicts(dataConflicts, alerts);
384
408
  await handleUniqueConflicts(uniqueConflicts, translatedIds);
385
409
  await handleTranslatedIds(uniqueConflicts, translatedIds);
386
410
  await updateSyncInfos(offlineChanges, translatedIds, syncTimestamp);
@@ -425,7 +449,7 @@ const checkCleanSync = async (uploadStarted, uploadStartTime, userName) => {
425
449
  return false;
426
450
  };
427
451
 
428
- const getSyncTimestamp = async () => {
452
+ const getServerTime = async () => {
429
453
  const resp = await apiCall({
430
454
  method: "GET",
431
455
  path: `/sync/sync_timestamp`,
@@ -433,6 +457,16 @@ const getSyncTimestamp = async () => {
433
457
  return resp.data.syncTimestamp;
434
458
  };
435
459
 
460
+ const setLocalSyncTimestamp = async (syncTimestamp) => {
461
+ const state = saltcorn.data.state.getState();
462
+ await state.setConfig("mobile_sync_timestamp", syncTimestamp);
463
+ };
464
+
465
+ const getLocalSyncTimestamp = async () => {
466
+ const state = saltcorn.data.state.getState();
467
+ return await state.getConfig("mobile_sync_timestamp");
468
+ };
469
+
436
470
  const setSpinnerText = () => {
437
471
  const iframeWindow = $("#content-iframe")[0].contentWindow;
438
472
  if (iframeWindow) {
@@ -451,7 +485,7 @@ export async function isSyncInProgress() {
451
485
  return syncInProgress;
452
486
  }
453
487
 
454
- export async function sync(background = false) {
488
+ export async function sync(background = false, alerts = []) {
455
489
  if (syncInProgress)
456
490
  throw new Error("A synchronization is already in progress.");
457
491
 
@@ -472,7 +506,7 @@ export async function sync(background = false) {
472
506
  uploadStartTime,
473
507
  user.email
474
508
  );
475
- const syncTimestamp = await getSyncTimestamp();
509
+ const syncTimestamp = await getServerTime();
476
510
  await setUploadStarted(true, syncTimestamp);
477
511
  let lock = null;
478
512
  if (!background) {
@@ -492,8 +526,9 @@ export async function sync(background = false) {
492
526
  if (cleanSync) await clearLocalData(true);
493
527
  const { synchedTables, syncInfos } = await prepare();
494
528
  await syncRemoteDeletes(syncInfos, syncTimestamp);
495
- syncDir = await syncOfflineData(synchedTables, syncTimestamp);
529
+ syncDir = await syncOfflineData(synchedTables, syncTimestamp, alerts);
496
530
  await syncRemoteData(syncInfos, syncTimestamp);
531
+ await setLocalSyncTimestamp(syncTimestamp);
497
532
  if (!background) await endOfflineMode(true);
498
533
  await setUploadStarted(false);
499
534
  await saltcorn.data.db.query("COMMIT");
@@ -663,8 +698,10 @@ export function addPushSyncHandler() {
663
698
  const state = saltcorn.data.state.getState();
664
699
  state.mobile_push_handler["push_sync"] = async (notification) => {
665
700
  console.log("Push sync received:", notification);
701
+ const alerts = [];
666
702
  try {
667
- await sync(true);
703
+ await sync(true, alerts);
704
+ if (alerts.length > 0) showAlerts(alerts);
668
705
  } catch (error) {
669
706
  console.log("Error during push sync:", error);
670
707
  }
package/src/init.js CHANGED
@@ -472,7 +472,7 @@ export async function init(mobileConfig) {
472
472
  }
473
473
  } else if (offlineUser) {
474
474
  if (offlineUser === mobileConfig.user.email) {
475
- await sync();
475
+ await sync(false, alerts);
476
476
  alerts.push({
477
477
  type: "info",
478
478
  msg: "Synchronized your offline data.",
@@ -483,7 +483,7 @@ export async function init(mobileConfig) {
483
483
  msg: `'${offlineUser}' has not yet uploaded offline data.`,
484
484
  });
485
485
  } else {
486
- await sync();
486
+ await sync(false, alerts);
487
487
  alerts.push({
488
488
  type: "info",
489
489
  msg: "Synchronized your offline data.",
@@ -555,7 +555,7 @@ export async function init(mobileConfig) {
555
555
  "Please go online and reload, the public login is not yet supported."
556
556
  );
557
557
  await publicLogin(getEntryPoint(100, state, state.mobileConfig));
558
- } else {
558
+ } else {
559
559
  // open login page
560
560
  await showLogin(alerts);
561
561
  }
@@ -892,15 +892,15 @@ async function callSync() {
892
892
  } else {
893
893
  const wasOffline = mobileConfig.isOfflineMode;
894
894
  showLoadSpinner();
895
- await parent.saltcorn.mobileApp.offlineMode.sync();
895
+ const alerts = [];
896
+ await parent.saltcorn.mobileApp.offlineMode.sync(false, alerts);
896
897
  parent.saltcorn.mobileApp.common.clearAlerts();
897
898
  if (!wasOffline) {
898
- parent.saltcorn.mobileApp.common.showAlerts([
899
- {
900
- type: "info",
901
- msg: "Synchronized your offline data.",
902
- },
903
- ]);
899
+ alerts.push({
900
+ type: "info",
901
+ msg: "Synchronized your offline data.",
902
+ });
903
+ parent.saltcorn.mobileApp.common.showAlerts(alerts);
904
904
  } else {
905
905
  setNetworSwitcherOn();
906
906
  parent.saltcorn.mobileApp.navigation.clearHistory();
@@ -908,12 +908,11 @@ async function callSync() {
908
908
  parent.saltcorn.mobileApp.navigation.addRoute({
909
909
  route: "get/sync/sync_settings",
910
910
  });
911
- parent.saltcorn.mobileApp.common.showAlerts([
912
- {
913
- type: "info",
914
- msg: "Synchronized your offline data, you are online again.",
915
- },
916
- ]);
911
+ alerts.push({
912
+ type: "info",
913
+ msg: "Synchronized your offline data, you are online again.",
914
+ });
915
+ parent.saltcorn.mobileApp.common.showAlerts(alerts);
917
916
  parent.saltcorn.mobileApp.common.clearTopAlerts();
918
917
  }
919
918
  }