@saltcorn/mobile-app 1.1.0-beta.9 → 1.1.0

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 (40) 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 +21 -0
  4. package/package.json +21 -12
  5. package/src/.eslintrc +21 -0
  6. package/src/helpers/api.js +43 -0
  7. package/src/helpers/auth.js +191 -0
  8. package/src/helpers/common.js +189 -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} +189 -332
  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 +15 -10
  25. package/{www/js → src/routing}/routes/sync.js +17 -8
  26. package/{www/js → src/routing}/routes/view.js +20 -15
  27. package/{www/js/routes/common.js → src/routing/utils.js} +18 -13
  28. package/unsecure-default-key.jks +0 -0
  29. package/webpack.config.js +31 -0
  30. package/www/data/encoded_site_logo.js +1 -0
  31. package/www/index.html +23 -493
  32. package/www/js/{utils/iframe_view_utils.js → iframe_view_utils.js} +193 -274
  33. package/config.xml +0 -27
  34. package/res/icon/android/icon.png +0 -0
  35. package/res/screen/android/splash-icon.png +0 -0
  36. package/res/screen/ios/Default@2x~universal~anyany.png +0 -0
  37. package/www/js/routes/error.js +0 -5
  38. package/www/js/routes/init.js +0 -76
  39. package/www/js/utils/file_helpers.js +0 -108
  40. package/www/js/utils/offline_mode_helper.js +0 -625
@@ -1,5 +1,5 @@
1
1
  /*eslint-env browser*/
2
- /*global $, KTDrawer, submitWithEmptyAction, is_paging_param, bootstrap, common_done, unique_field_from_rows, inline_submit_success*/
2
+ /*global $, KTDrawer, submitWithEmptyAction, is_paging_param, bootstrap, common_done, unique_field_from_rows, inline_submit_success, get_current_state_url, initialize_page */
3
3
 
4
4
  function combineFormAndQuery(form, query) {
5
5
  let paramsList = [];
@@ -28,19 +28,32 @@ async function execLink(url, linkSrc) {
28
28
  try {
29
29
  if (document.getElementById("scspinner")) return;
30
30
  showLoadSpinner();
31
- if (url.startsWith("javascript:")) eval(url.substring(11));
31
+ if (url.startsWith("#")) {
32
+ const anchor = url.substring(1);
33
+ parent.saltcorn.mobileApp.navigation.setAnchor(anchor);
34
+ } else if (url.startsWith("javascript:")) eval(url.substring(11));
32
35
  else {
33
- const { path, query } = parent.splitPathQuery(url);
34
- await parent.handleRoute(`get${path}`, query);
36
+ const { path, query } =
37
+ parent.saltcorn.mobileApp.navigation.splitPathQuery(url);
38
+ const safePath = path.startsWith("http")
39
+ ? new URL(path).pathname
40
+ : path;
41
+ await parent.saltcorn.mobileApp.navigation.handleRoute(
42
+ `get${safePath}`,
43
+ query
44
+ );
35
45
  }
46
+ } catch (error) {
47
+ parent.saltcorn.mobileApp.common.errorAlert(error);
36
48
  } finally {
37
49
  removeLoadSpinner();
38
50
  }
39
51
  }
40
52
 
41
53
  async function runUrl(url, method = "get") {
42
- const { path, query } = parent.splitPathQuery(url);
43
- const page = await parent.router.resolve({
54
+ const { path, query } =
55
+ parent.saltcorn.mobileApp.navigation.splitPathQuery(url);
56
+ const page = await parent.saltcorn.mobileApp.navigation.router.resolve({
44
57
  pathname: `${method}${path}`,
45
58
  query: query,
46
59
  });
@@ -84,17 +97,23 @@ async function formSubmit(e, urlSuffix, viewname, noSubmitCb, matchingState) {
84
97
  const tokens = entry[1].split("/");
85
98
  const fileName = tokens[tokens.length - 1];
86
99
  const directory = tokens.splice(0, tokens.length - 1).join("/");
87
- // read and add file to submit
88
- const binary = await parent.readBinary(fileName, directory);
89
- files[entry[0]] = new File([binary], fileName);
100
+ const { buffer, file } =
101
+ await parent.saltcorn.mobileApp.fileSystem.readBinaryCordova(
102
+ fileName,
103
+ directory
104
+ );
105
+ files[entry[0]] = {
106
+ blob: new Blob([buffer], { type: file.type }),
107
+ fileObj: file,
108
+ };
90
109
  } else if (!matchingState) urlParams.append(entry[0], entry[1]);
91
110
  else data[entry[0]] = entry[1];
92
111
  }
93
112
  }
94
113
  const queryStr = !matchingState
95
114
  ? urlParams.toString()
96
- : parent.currentQuery() || "";
97
- await parent.handleRoute(
115
+ : parent.saltcorn.mobileApp.navigation.currentQuery() || "";
116
+ await parent.saltcorn.mobileApp.navigation.handleRoute(
98
117
  `post${urlSuffix}${viewname}`,
99
118
  queryStr,
100
119
  files,
@@ -116,13 +135,13 @@ async function inline_local_submit(e, opts1) {
116
135
  urlParams.append(entry[0], entry[1]);
117
136
  }
118
137
  const url = form.attr("action");
119
- await parent.router.resolve({
138
+ await parent.saltcorn.mobileApp.navigation.router.resolve({
120
139
  pathname: `post${url}`,
121
140
  query: urlParams.toString(),
122
141
  });
123
142
  inline_submit_success(e, form, opts);
124
143
  } catch (error) {
125
- parent.showAlerts([
144
+ parent.saltcorn.mobileApp.common.showAlerts([
126
145
  {
127
146
  type: "error",
128
147
  msg: error.message ? error.message : "An error occured.",
@@ -145,7 +164,7 @@ async function saveAndContinue(e, action, k) {
145
164
  const form = $(e).closest("form");
146
165
  submitWithEmptyAction(form[0]);
147
166
  const queryStr = new URLSearchParams(new FormData(form[0])).toString();
148
- const res = await parent.router.resolve({
167
+ const res = await parent.saltcorn.mobileApp.navigation.router.resolve({
149
168
  pathname: `post${action}`,
150
169
  query: queryStr,
151
170
  xhr: true,
@@ -162,95 +181,16 @@ async function saveAndContinue(e, action, k) {
162
181
  }
163
182
  }
164
183
 
165
- async function loginRequest({ email, password, isSignup, isPublic }) {
166
- const opts = isPublic
167
- ? {
168
- method: "GET",
169
- path: "/auth/login-with/jwt",
170
- }
171
- : isSignup
172
- ? {
173
- method: "POST",
174
- path: "/auth/signup",
175
- body: {
176
- email,
177
- password,
178
- },
179
- }
180
- : {
181
- method: "GET",
182
- path: "/auth/login-with/jwt",
183
- params: {
184
- email,
185
- password,
186
- },
187
- };
188
- const response = await parent.apiCall(opts);
189
- return response.data;
190
- }
191
-
192
184
  async function login(e, entryPoint, isSignup) {
193
185
  try {
194
186
  showLoadSpinner();
195
187
  const formData = new FormData(e);
196
- const loginResult = await loginRequest({
188
+ await parent.saltcorn.mobileApp.auth.login({
197
189
  email: formData.get("email"),
198
190
  password: formData.get("password"),
199
191
  isSignup,
192
+ entryPoint,
200
193
  });
201
- if (typeof loginResult === "string") {
202
- // use it as a token
203
- const decodedJwt = parent.jwt_decode(loginResult);
204
- const state = parent.saltcorn.data.state.getState();
205
- const config = state.mobileConfig;
206
- config.role_id = decodedJwt.user.role_id ? decodedJwt.user.role_id : 100;
207
- config.user_name = decodedJwt.user.email;
208
- config.user_id = decodedJwt.user.id;
209
- config.language = decodedJwt.user.language;
210
- config.user = decodedJwt.user;
211
- config.isPublicUser = false;
212
- config.isOfflineMode = false;
213
- await parent.insertUser(config.user);
214
- await parent.setJwt(loginResult);
215
- config.jwt = loginResult;
216
- await parent.i18next.changeLanguage(config.language);
217
- const alerts = [];
218
- if (config.allowOfflineMode) {
219
- const { offlineUser, hasOfflineData } =
220
- (await parent.offlineHelper.getLastOfflineSession()) || {};
221
- if (!offlineUser || offlineUser === config.user_name) {
222
- await parent.offlineHelper.sync();
223
- } else {
224
- if (hasOfflineData)
225
- alerts.push({
226
- type: "warning",
227
- msg: `'${offlineUser}' has not yet uploaded offline data.`,
228
- });
229
- else {
230
- await deleteOfflineData(true);
231
- await parent.offlineHelper.sync();
232
- }
233
- }
234
- }
235
- alerts.push({
236
- type: "success",
237
- msg: parent.i18next.t("Welcome, %s!", {
238
- postProcess: "sprintf",
239
- sprintf: [config.user_name],
240
- }),
241
- });
242
- parent.addRoute({ route: entryPoint, query: undefined });
243
- const page = await parent.router.resolve({
244
- pathname: entryPoint,
245
- fullWrap: true,
246
- alerts,
247
- });
248
- if (page.content) await parent.replaceIframe(page.content, page.isFile);
249
- } else if (loginResult?.alerts) {
250
- parent.showAlerts(loginResult?.alerts);
251
- } else {
252
- throw new Error("The login failed.");
253
- }
254
194
  } finally {
255
195
  removeLoadSpinner();
256
196
  }
@@ -259,77 +199,16 @@ async function login(e, entryPoint, isSignup) {
259
199
  async function publicLogin(entryPoint) {
260
200
  try {
261
201
  showLoadSpinner();
262
- const loginResult = await loginRequest({ isPublic: true });
263
- if (typeof loginResult === "string") {
264
- const config = parent.saltcorn.data.state.getState().mobileConfig;
265
- config.user = {
266
- role_id: 100,
267
- user_name: "public",
268
- language: "en",
269
- };
270
- // TODO remove these, use 'user' everywhere
271
- config.role_id = 100;
272
- config.user_name = "public";
273
- config.language = "en";
274
-
275
- config.isPublicUser = true;
276
- await parent.setJwt(loginResult);
277
- config.jwt = loginResult;
278
- parent.i18next.changeLanguage(config.language);
279
- parent.addRoute({ route: entryPoint, query: undefined });
280
- const page = await parent.router.resolve({
281
- pathname: entryPoint,
282
- fullWrap: true,
283
- alerts: [
284
- {
285
- type: "success",
286
- msg: parent.i18next.t("Welcome to %s!", {
287
- postProcess: "sprintf",
288
- sprintf: [
289
- parent.saltcorn.data.state.getState().getConfig("site_name") ||
290
- "Saltcorn",
291
- ],
292
- }),
293
- },
294
- ],
295
- });
296
- if (page.content) await parent.replaceIframe(page.content, page.isFile);
297
- } else if (loginResult?.alerts) {
298
- parent.showAlerts(loginResult?.alerts);
299
- } else {
300
- throw new Error("The login failed.");
301
- }
302
- } catch (error) {
303
- console.log(error);
304
- parent.showAlerts([
305
- {
306
- type: "error",
307
- msg: error.message ? error.message : "An error occured.",
308
- },
309
- ]);
310
- throw error;
202
+ await parent.saltcorn.mobileApp.auth.publicLogin(entryPoint);
311
203
  } finally {
312
204
  removeLoadSpinner();
313
205
  }
314
206
  }
315
207
 
316
208
  async function logout() {
317
- const config = parent.saltcorn.data.state.getState().mobileConfig;
318
209
  try {
319
210
  showLoadSpinner();
320
- const page = await parent.router.resolve({
321
- pathname: "get/auth/logout",
322
- entryView: config.entry_point,
323
- versionTag: config.version_tag,
324
- });
325
- await parent.replaceIframe(page.content);
326
- } catch (error) {
327
- parent.showAlerts([
328
- {
329
- type: "error",
330
- msg: error.message ? error.message : "An error occured.",
331
- },
332
- ]);
211
+ await parent.saltcorn.mobileApp.auth.logout();
333
212
  } finally {
334
213
  removeLoadSpinner();
335
214
  }
@@ -339,7 +218,7 @@ async function signupFormSubmit(e, entryView) {
339
218
  try {
340
219
  await login(e, entryView, true);
341
220
  } catch (error) {
342
- parent.errorAlert(error);
221
+ parent.saltcorn.mobileApp.common.errorAlert(error);
343
222
  }
344
223
  }
345
224
 
@@ -353,7 +232,7 @@ async function loginFormSubmit(e, entryView) {
353
232
  }
354
233
  await login(e, safeEntryView, false);
355
234
  } catch (error) {
356
- parent.errorAlert(error);
235
+ parent.saltcorn.mobileApp.common.errorAlert(error);
357
236
  }
358
237
  }
359
238
 
@@ -363,8 +242,9 @@ async function local_post_btn(e) {
363
242
  const form = $(e).closest("form");
364
243
  const url = form.attr("action");
365
244
  const method = form.attr("method");
366
- const { path, query } = parent.splitPathQuery(url);
367
- await parent.handleRoute(
245
+ const { path, query } =
246
+ parent.saltcorn.mobileApp.navigation.splitPathQuery(url);
247
+ await parent.saltcorn.mobileApp.navigation.handleRoute(
368
248
  `${method}${path}`,
369
249
  combineFormAndQuery(form, query)
370
250
  );
@@ -382,7 +262,7 @@ async function stateFormSubmit(e, path) {
382
262
  try {
383
263
  showLoadSpinner();
384
264
  const formQuery = new URLSearchParams(new FormData(e)).toString();
385
- await parent.handleRoute(path, formQuery);
265
+ await parent.saltcorn.mobileApp.navigation.handleRoute(path, formQuery);
386
266
  } finally {
387
267
  removeLoadSpinner();
388
268
  }
@@ -428,11 +308,14 @@ function invalidate_pagings(currentQuery) {
428
308
  return newQuery;
429
309
  }
430
310
 
431
- async function set_state_fields(kvs, href) {
311
+ async function set_state_fields(kvs, disablePjax, e) {
432
312
  try {
433
313
  showLoadSpinner();
314
+ let newhref = get_current_state_url(e);
434
315
  let queryParams = [];
435
- let currentQuery = parent.currentQuery();
316
+ const { path, query } =
317
+ parent.saltcorn.mobileApp.navigation.splitPathQuery(newhref);
318
+ let currentQuery = query || {};
436
319
  if (Object.keys(kvs).some((k) => !is_paging_param(k))) {
437
320
  currentQuery = invalidate_pagings(currentQuery);
438
321
  }
@@ -446,17 +329,65 @@ async function set_state_fields(kvs, href) {
446
329
  for (const [k, v] of new URLSearchParams(currentQuery).entries()) {
447
330
  queryParams.push(`${k}=${v}`);
448
331
  }
449
- await parent.handleRoute(href, queryParams.join("&"));
332
+ const queryStr = queryParams.join("&");
333
+ if (disablePjax)
334
+ await parent.saltcorn.mobileApp.navigation.handleRoute(path, queryStr);
335
+ else await pjax_to(path, queryStr, e);
450
336
  } finally {
451
337
  removeLoadSpinner();
452
338
  }
453
339
  }
454
340
 
341
+ async function pjax_to(href, query, e) {
342
+ const safeHref = href.startsWith("get") ? href.substring(3) : href;
343
+ const path = `${safeHref}?${query}`;
344
+ let $modal = $("#scmodal");
345
+ const inModal = $modal.length && $modal.hasClass("show");
346
+ const localizer = e ? $(e).closest("[data-sc-local-state]") : [];
347
+ let $dest = localizer.length
348
+ ? localizer
349
+ : inModal
350
+ ? $("#scmodal .modal-body")
351
+ : $("#page-inner-content");
352
+ if (!$dest.length)
353
+ await parent.saltcorn.mobileApp.navigation.handleRoute(safeHref, query);
354
+ else
355
+ try {
356
+ const headers = {
357
+ pjaxpageload: "true",
358
+ };
359
+ if (localizer.length) headers.localizedstate = "true";
360
+ const result = await parent.saltcorn.mobileApp.api.apiCall({
361
+ path: path,
362
+ method: "GET",
363
+ additionalHeaders: headers,
364
+ });
365
+ if (!inModal && !localizer.length) {
366
+ // not sure for mobile
367
+ // window.history.pushState({ url: href }, "", href);
368
+ }
369
+ if (inModal && !localizer.length)
370
+ $(".sc-modal-linkout").attr("href", path);
371
+ $dest.html(result.data);
372
+ if (localizer.length) localizer.attr("data-sc-local-state", path);
373
+ initialize_page();
374
+ } catch (error) {
375
+ parent.saltcorn.mobileApp.common.errorAlert(error);
376
+ }
377
+ }
378
+
455
379
  async function set_state_field(key, value) {
456
380
  try {
457
381
  showLoadSpinner();
458
- const query = updateQueryStringParameter(parent.currentQuery(), key, value);
459
- await parent.handleRoute(parent.currentLocation(), query);
382
+ const query = updateQueryStringParameter(
383
+ parent.saltcorn.mobileApp.navigation.currentQuery(),
384
+ key,
385
+ value
386
+ );
387
+ await parent.saltcorn.mobileApp.navigation.handleRoute(
388
+ parent.saltcorn.mobileApp.navigation.currentLocation(),
389
+ query
390
+ );
460
391
  } finally {
461
392
  removeLoadSpinner();
462
393
  }
@@ -465,32 +396,37 @@ async function set_state_field(key, value) {
465
396
  async function unset_state_field(key) {
466
397
  try {
467
398
  showLoadSpinner();
468
- const href = parent.currentLocation();
469
- const query = removeQueryStringParameter(parent.currentLocation(), key);
470
- await parent.handleRoute(href, query);
399
+ const href = parent.saltcorn.mobileApp.navigation.currentLocation();
400
+ const query = removeQueryStringParameter(
401
+ parent.saltcorn.mobileApp.navigation.currentLocation(),
402
+ key
403
+ );
404
+ await parent.saltcorn.mobileApp.navigation.handleRoute(href, query);
471
405
  } finally {
472
406
  removeLoadSpinner();
473
407
  }
474
408
  }
475
409
 
476
- async function sortby(k, desc, viewIdentifier) {
410
+ async function sortby(k, desc, viewIdentifier, e) {
477
411
  await set_state_fields(
478
412
  {
479
413
  [`_${viewIdentifier}_sortby`]: k,
480
414
  [`_${viewIdentifier}_sortdesc`]: desc ? "on" : { unset: true },
481
415
  },
482
- parent.currentLocation()
416
+ false,
417
+ e
483
418
  );
484
419
  }
485
420
 
486
- async function gopage(n, pagesize, viewIdentifier, extra) {
421
+ async function gopage(n, pagesize, viewIdentifier, extra, e) {
487
422
  await set_state_fields(
488
423
  {
489
424
  ...extra,
490
425
  [`_${viewIdentifier}_page`]: n,
491
426
  [`_${viewIdentifier}_pagesize`]: pagesize,
492
427
  },
493
- parent.currentLocation()
428
+ false,
429
+ e
494
430
  );
495
431
  }
496
432
 
@@ -529,18 +465,19 @@ async function mobile_modal(url, opts = {}) {
529
465
  if (opts.submitReload === false) $("#scmodal").addClass("no-submit-reload");
530
466
  else $("#scmodal").removeClass("no-submit-reload");
531
467
  try {
532
- const { path, query } = parent.splitPathQuery(url);
468
+ const { path, query } =
469
+ parent.saltcorn.mobileApp.navigation.splitPathQuery(url);
533
470
  const mobileConfig = parent.saltcorn.data.state.getState().mobileConfig;
534
471
  if (
535
472
  mobileConfig.networkState === "none" &&
536
473
  mobileConfig.allowOfflineMode &&
537
474
  !mobileConfig.isOfflineMode
538
475
  ) {
539
- await parent.offlineHelper.startOfflineMode();
540
- parent.clearHistory();
541
- await parent.gotoEntryView();
476
+ await parent.saltcorn.mobileApp.offlineMode.startOfflineMode();
477
+ parent.saltcorn.mobileApp.navigation.clearHistory();
478
+ await parent.saltcorn.mobileApp.navigation.gotoEntryView();
542
479
  } else {
543
- const page = await parent.router.resolve({
480
+ const page = await parent.saltcorn.mobileApp.navigation.router.resolve({
544
481
  pathname: `get${path}`,
545
482
  query: query,
546
483
  alerts: [],
@@ -553,7 +490,7 @@ async function mobile_modal(url, opts = {}) {
553
490
  // onOpen onClose initialize_page?
554
491
  }
555
492
  } catch (error) {
556
- parent.showAlerts([
493
+ parent.saltcorn.mobileApp.common.showAlerts([
557
494
  {
558
495
  type: "error",
559
496
  msg: error.message ? error.message : "An error occured.",
@@ -569,14 +506,15 @@ function closeModal() {
569
506
  async function local_post(url, args) {
570
507
  try {
571
508
  showLoadSpinner();
572
- const result = await parent.router.resolve({
509
+ const result = await parent.saltcorn.mobileApp.navigation.router.resolve({
573
510
  pathname: `post${url}`,
574
511
  data: args,
575
512
  });
576
- if (result.redirect) await parent.handleRoute(result.redirect);
513
+ if (result.redirect)
514
+ await parent.saltcorn.mobileApp.navigation.handleRoute(result.redirect);
577
515
  else await common_done(result, "", false);
578
516
  } catch (error) {
579
- parent.errorAlert(error);
517
+ parent.saltcorn.mobileApp.common.errorAlert(error);
580
518
  } finally {
581
519
  removeLoadSpinner();
582
520
  }
@@ -585,17 +523,18 @@ async function local_post(url, args) {
585
523
  async function local_post_json(url, data, cb) {
586
524
  try {
587
525
  showLoadSpinner();
588
- const result = await parent.router.resolve({
526
+ const result = await parent.saltcorn.mobileApp.navigation.router.resolve({
589
527
  pathname: `post${url}`,
590
528
  data: data,
591
- query: parent.currentQuery(),
529
+ query: parent.saltcorn.mobileApp.navigation.currentQuery(),
592
530
  });
593
531
  if (result.server_eval) await evalServerCode(url);
594
- if (result.redirect) await parent.handleRoute(result.redirect);
532
+ if (result.redirect)
533
+ await parent.saltcorn.mobileApp.navigation.handleRoute(result.redirect);
595
534
  else await common_done(result, "", false);
596
535
  if (cb?.success) cb.success(result);
597
536
  } catch (error) {
598
- parent.errorAlert(error);
537
+ parent.saltcorn.mobileApp.common.errorAlert(error);
599
538
  if (cb?.error) cb.error(error);
600
539
  } finally {
601
540
  removeLoadSpinner();
@@ -603,7 +542,7 @@ async function local_post_json(url, data, cb) {
603
542
  }
604
543
 
605
544
  async function evalServerCode(url) {
606
- await parent.apiCall({
545
+ await parent.saltcorn.mobileApp.api.apiCall({
607
546
  method: "POST",
608
547
  path: url,
609
548
  });
@@ -626,7 +565,7 @@ async function make_unique_field(
626
565
  )}=${encodeURIComponent(value)}&fields=${encodeURIComponent(field_name)}`;
627
566
  try {
628
567
  // TODO ch support local tables
629
- const response = await parent.apiCall({
568
+ const response = await parent.saltcorn.mobileApp.api.apiCall({
630
569
  method: "GET",
631
570
  path,
632
571
  });
@@ -643,7 +582,7 @@ async function make_unique_field(
643
582
  );
644
583
  }
645
584
  } catch (error) {
646
- parent.showAlerts([
585
+ parent.saltcorn.mobileApp.common.showAlerts([
647
586
  {
648
587
  type: "error",
649
588
  msg: "unable to 'make_unique_field'",
@@ -688,11 +627,14 @@ async function select_id(id) {
688
627
  try {
689
628
  showLoadSpinner();
690
629
  const newQuery = updateQueryStringParameter(
691
- parent.currentQuery(),
630
+ parent.saltcorn.mobileApp.navigation.currentQuery(),
692
631
  "id",
693
632
  id
694
633
  );
695
- await parent.handleRoute(parent.currentLocation(), newQuery);
634
+ await parent.handleRoute(
635
+ parent.saltcorn.mobileApp.navigation.currentLocation(),
636
+ newQuery
637
+ );
696
638
  } finally {
697
639
  removeLoadSpinner();
698
640
  }
@@ -703,9 +645,16 @@ async function check_state_field(that) {
703
645
  showLoadSpinner();
704
646
  const name = that.name;
705
647
  const newQuery = that.checked
706
- ? updateQueryStringParameter(parent.currentQuery(), name, that.value)
648
+ ? updateQueryStringParameter(
649
+ parent.saltcorn.mobileApp.navigation.currentQuery(),
650
+ name,
651
+ that.value
652
+ )
707
653
  : removeQueryStringParameter(name);
708
- await parent.handleRoute(parent.currentLocation(), newQuery);
654
+ await parent.saltcorn.mobileApp.navigation.handleRoute(
655
+ parent.saltcorn.mobileApp.navigation.currentLocation(),
656
+ newQuery
657
+ );
709
658
  } finally {
710
659
  removeLoadSpinner();
711
660
  }
@@ -714,7 +663,10 @@ async function check_state_field(that) {
714
663
  async function clear_state() {
715
664
  try {
716
665
  showLoadSpinner();
717
- await parent.handleRoute(parent.currentLocation(), undefined);
666
+ await parent.saltcorn.mobileApp.navigation.handleRoute(
667
+ parent.saltcorn.mobileApp.navigation.currentLocation(),
668
+ undefined
669
+ );
718
670
  } finally {
719
671
  removeLoadSpinner();
720
672
  }
@@ -728,7 +680,7 @@ async function view_post(viewnameOrElem, route, data, onDone, sendState) {
728
680
  .closest("[data-sc-embed-viewname]")
729
681
  .attr("data-sc-embed-viewname");
730
682
  const buildQuery = () => {
731
- const query = parent.currentQuery();
683
+ const query = parent.saltcorn.mobileApp.navigation.currentQuery();
732
684
  return query ? `?${query}` : "";
733
685
  };
734
686
  const mobileConfig = parent.saltcorn.data.state.getState().mobileConfig;
@@ -741,13 +693,13 @@ async function view_post(viewnameOrElem, route, data, onDone, sendState) {
741
693
  mobileConfig.isOfflineMode ||
742
694
  (view?.table_id && mobileConfig.localTableIds.indexOf(view.table_id) >= 0)
743
695
  ) {
744
- respData = await parent.router.resolve({
696
+ respData = await parent.saltcorn.mobileApp.navigation.router.resolve({
745
697
  pathname: `post/view/${viewname}/${route}`,
746
698
  data,
747
699
  query,
748
700
  });
749
701
  } else {
750
- const response = await parent.apiCall({
702
+ const response = await parent.saltcorn.mobileApp.api.apiCall({
751
703
  method: "POST",
752
704
  path: "/view/" + viewname + "/" + route + query,
753
705
  body: data,
@@ -760,7 +712,7 @@ async function view_post(viewnameOrElem, route, data, onDone, sendState) {
760
712
  if (onDone) await onDone(respData);
761
713
  await common_done(respData, viewname, false);
762
714
  } catch (error) {
763
- parent.errorAlert(error);
715
+ parent.saltcorn.mobileApp.common.errorAlert(error);
764
716
  } finally {
765
717
  removeLoadSpinner();
766
718
  }
@@ -783,37 +735,41 @@ async function switchNetworkMode() {
783
735
  const state = parent.saltcorn.data.state.getState();
784
736
  const { isOfflineMode, networkState } = state.mobileConfig;
785
737
  if (!isOfflineMode) {
786
- await parent.offlineHelper.startOfflineMode();
787
- parent.clearHistory();
788
- parent.addRoute({ route: "/" });
789
- parent.addRoute({ route: "get/sync/sync_settings" });
790
- parent.showAlerts(
738
+ await parent.saltcorn.mobileApp.offlineMode.startOfflineMode();
739
+ parent.saltcorn.mobileApp.navigation.clearHistory();
740
+ parent.saltcorn.mobileApp.navigation.addRoute({ route: "/" });
741
+ parent.saltcorn.mobileApp.navigation.addRoute({
742
+ route: "get/sync/sync_settings",
743
+ });
744
+ parent.saltcorn.mobileApp.common.showAlerts(
791
745
  [
792
746
  {
793
747
  type: "info",
794
- msg: parent.offlineHelper.getOfflineMsg(),
748
+ msg: parent.saltcorn.mobileApp.offlineMode.getOfflineMsg(),
795
749
  },
796
750
  ],
797
751
  false
798
752
  );
799
- parent.clearAlerts();
753
+ parent.saltcorn.mobileApp.common.clearAlerts();
800
754
  } else {
801
755
  if (networkState === "none")
802
756
  throw new Error("No internet connection is available.");
803
- await parent.offlineHelper.endOfflineMode();
804
- parent.clearHistory();
805
- parent.addRoute({ route: "/" });
806
- parent.addRoute({ route: "get/sync/sync_settings" });
807
- parent.showAlerts([
757
+ await parent.saltcorn.mobileApp.offlineMode.endOfflineMode();
758
+ parent.saltcorn.mobileApp.navigation.clearHistory();
759
+ parent.saltcorn.mobileApp.navigation.addRoute({ route: "/" });
760
+ parent.saltcorn.mobileApp.navigation.addRoute({
761
+ route: "get/sync/sync_settings",
762
+ });
763
+ parent.saltcorn.mobileApp.common.showAlerts([
808
764
  {
809
765
  type: "info",
810
766
  msg: "You are online again.",
811
767
  },
812
768
  ]);
813
- parent.clearTopAlerts();
769
+ parent.saltcorn.mobileApp.common.clearTopAlerts();
814
770
  }
815
771
  } catch (error) {
816
- parent.showAlerts([
772
+ parent.saltcorn.mobileApp.common.showAlerts([
817
773
  {
818
774
  type: "error",
819
775
  msg: `Unable to change the network mode: ${
@@ -833,7 +789,7 @@ async function callSync() {
833
789
  try {
834
790
  const mobileConfig = parent.saltcorn.data.state.getState().mobileConfig;
835
791
  if (mobileConfig.networkState === "none") {
836
- parent.showAlerts([
792
+ parent.saltcorn.mobileApp.common.showAlerts([
837
793
  {
838
794
  type: "error",
839
795
  msg: "You don't have an internet connection.",
@@ -842,10 +798,10 @@ async function callSync() {
842
798
  } else {
843
799
  const wasOffline = mobileConfig.isOfflineMode;
844
800
  showLoadSpinner();
845
- await parent.offlineHelper.sync();
846
- parent.clearAlerts();
801
+ await parent.saltcorn.mobileApp.offlineMode.sync();
802
+ parent.saltcorn.mobileApp.common.clearAlerts();
847
803
  if (!wasOffline) {
848
- parent.showAlerts([
804
+ parent.saltcorn.mobileApp.common.showAlerts([
849
805
  {
850
806
  type: "info",
851
807
  msg: "Synchronized your offline data.",
@@ -853,38 +809,41 @@ async function callSync() {
853
809
  ]);
854
810
  } else {
855
811
  setNetworSwitcherOn();
856
- parent.clearHistory();
857
- parent.addRoute({ route: "/" });
858
- parent.addRoute({ route: "get/sync/sync_settings" });
859
- parent.showAlerts([
812
+ parent.saltcorn.mobileApp.navigation.clearHistory();
813
+ parent.saltcorn.mobileApp.navigation.addRoute({ route: "/" });
814
+ parent.saltcorn.mobileApp.navigation.addRoute({
815
+ route: "get/sync/sync_settings",
816
+ });
817
+ parent.saltcorn.mobileApp.common.showAlerts([
860
818
  {
861
819
  type: "info",
862
820
  msg: "Synchronized your offline data, you are online again.",
863
821
  },
864
822
  ]);
865
- parent.clearTopAlerts();
823
+ parent.saltcorn.mobileApp.common.clearTopAlerts();
866
824
  }
867
825
  }
868
826
  } catch (error) {
869
827
  console.log(error);
870
- parent.errorAlert(error);
828
+ parent.saltcorn.mobileApp.common.errorAlert(error);
871
829
  } finally {
872
830
  removeLoadSpinner();
873
831
  }
874
832
  }
875
833
 
876
834
  async function deleteOfflineDataClicked() {
877
- const lastOfflineSession = await parent.offlineHelper.getLastOfflineSession();
878
- const { user_name } = parent.saltcorn.data.state.getState().mobileConfig;
835
+ const lastOfflineSession =
836
+ await parent.saltcorn.mobileApp.offlineMode.getLastOfflineSession();
837
+ const { user } = parent.saltcorn.data.state.getState().mobileConfig;
879
838
  if (!lastOfflineSession?.offlineUser) {
880
- parent.showAlerts([
839
+ parent.saltcorn.mobileApp.common.showAlerts([
881
840
  {
882
841
  type: "error",
883
842
  msg: "You don't have any offline data.",
884
843
  },
885
844
  ]);
886
- } else if (lastOfflineSession.offlineUser !== user_name) {
887
- parent.showAlerts([
845
+ } else if (lastOfflineSession.offlineUser !== user.email) {
846
+ parent.saltcorn.mobileApp.common.showAlerts([
888
847
  {
889
848
  type: "error",
890
849
  msg: `The offline data is owned by '${lastOfflineSession.offlineUser}'.`,
@@ -895,30 +854,8 @@ async function deleteOfflineDataClicked() {
895
854
  }
896
855
  }
897
856
 
898
- async function deleteOfflineData(noFeedback) {
899
- const mobileConfig = parent.saltcorn.data.state.getState().mobileConfig;
900
- try {
901
- mobileConfig.inLoadState = true;
902
- if (!noFeedback) showLoadSpinner();
903
- await parent.offlineHelper.clearLocalData(false);
904
- await parent.offlineHelper.setHasOfflineData(false);
905
- if (!noFeedback)
906
- parent.showAlerts([
907
- {
908
- type: "info",
909
- msg: "Deleted your offline data.",
910
- },
911
- ]);
912
- } catch (error) {
913
- parent.errorAlert(error);
914
- } finally {
915
- mobileConfig.inLoadState = false;
916
- if (!noFeedback) removeLoadSpinner();
917
- }
918
- }
919
-
920
857
  function showLoadSpinner() {
921
- if (!parent.isHtmlFile()) {
858
+ if (!parent.saltcorn.mobileApp.navigation.isHtmlFile()) {
922
859
  const spinner = $("#scspinner");
923
860
  if (spinner.length === 0) {
924
861
  $("body").append(`
@@ -961,7 +898,7 @@ function showLoadSpinner() {
961
898
  }
962
899
 
963
900
  function removeLoadSpinner() {
964
- if (!parent.isHtmlFile()) {
901
+ if (!parent.saltcorn.mobileApp.navigation.isHtmlFile()) {
965
902
  const spinner = $("#scspinner");
966
903
  if (spinner.length > 0) {
967
904
  const count = parseInt(spinner.attr("spinner-count")) - 1;
@@ -977,29 +914,11 @@ function removeLoadSpinner() {
977
914
  * @param {*} fieldName
978
915
  */
979
916
  async function getPicture(fieldName) {
980
- const cameraOptions = {
981
- quality: 50,
982
- encodingType: parent.Camera.EncodingType.JPEG,
983
- destinationType: parent.Camera.DestinationType.FILE_URI,
984
- };
985
- const getPictureWithPromise = () => {
986
- return new Promise((resolve, reject) => {
987
- parent.navigator.camera.getPicture(
988
- (imageDate) => {
989
- return resolve(imageDate);
990
- },
991
- (message) => {
992
- return reject(message);
993
- },
994
- cameraOptions
995
- );
996
- });
997
- };
998
917
  try {
999
918
  const form = $(`#cptbtn${fieldName}`).closest("form");
1000
919
  const onsubmit = form.attr("onsubmit");
1001
920
  form.attr("onsubmit", "javascript:void(0)");
1002
- const fileURI = await getPictureWithPromise();
921
+ const fileURI = await parent.saltcorn.mobileApp.common.takePhoto("uri");
1003
922
  form.attr("onsubmit", onsubmit);
1004
923
  const inputId = `input${fieldName}`;
1005
924
  form.find(`#${inputId}`).remove();
@@ -1009,7 +928,7 @@ async function getPicture(fieldName) {
1009
928
  const tokens = fileURI.split("/");
1010
929
  $(`#cpt-file-name-${fieldName}`).text(tokens[tokens.length - 1]);
1011
930
  } catch (error) {
1012
- parent.errorAlert(error);
931
+ parent.saltcorn.mobileApp.common.errorAlert(error);
1013
932
  }
1014
933
  }
1015
934
 
@@ -1024,7 +943,7 @@ async function updateMatchingRows(e, viewname) {
1024
943
  true
1025
944
  );
1026
945
  } catch (error) {
1027
- parent.errorAlert(error);
946
+ parent.saltcorn.mobileApp.common.errorAlert(error);
1028
947
  }
1029
948
  }
1030
949