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

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
package/src/init.js ADDED
@@ -0,0 +1,424 @@
1
+ /*global saltcorn */
2
+
3
+ import {
4
+ startOfflineMode,
5
+ networkChangeCallback,
6
+ sync,
7
+ getLastOfflineSession,
8
+ } from "./helpers/offline_mode.js";
9
+ import {
10
+ updateScPlugins,
11
+ createSyncInfoTables,
12
+ dbUpdateNeeded,
13
+ updateDb,
14
+ getTableIds,
15
+ createJwtTable,
16
+ } from "./helpers/db_schema.js";
17
+ import { publicLogin, checkJWT } from "./helpers/auth.js";
18
+ import { router } from "./routing/index.js";
19
+ import {
20
+ replaceIframe,
21
+ clearHistory,
22
+ gotoEntryView,
23
+ addRoute,
24
+ } from "./helpers/navigation.js";
25
+
26
+ import i18next from "i18next";
27
+ import i18nextSprintfPostProcessor from "i18next-sprintf-postprocessor";
28
+ import { jwtDecode } from "jwt-decode";
29
+
30
+ import { Network } from "@capacitor/network";
31
+
32
+ async function addScript(scriptObj) {
33
+ let waited = 0;
34
+ const maxWait = 3000;
35
+
36
+ const moduleAvailable = () =>
37
+ window.saltcorn && window.saltcorn[scriptObj.name];
38
+
39
+ return new Promise((resolve, reject) => {
40
+ let script = document.createElement("script");
41
+ document.head.appendChild(script);
42
+
43
+ const waitForModule = () => {
44
+ waited += 100;
45
+ if (waited >= maxWait)
46
+ return reject(`unable to load '${scriptObj.name}'`);
47
+ console.log("waiting for " + scriptObj.name);
48
+ setTimeout(function () {
49
+ if (moduleAvailable()) return resolve();
50
+ else waitForModule();
51
+ }, 100);
52
+ };
53
+
54
+ script.onload = () => {
55
+ if (!scriptObj.name || moduleAvailable()) return resolve();
56
+ waitForModule();
57
+ };
58
+ script.src = scriptObj.src;
59
+ });
60
+ }
61
+
62
+ async function loadPlugin(plugin) {
63
+ await addScript({
64
+ src: `js/bundle/${plugin.name}.bundle.js`,
65
+ name: plugin.name,
66
+ });
67
+ }
68
+
69
+ async function loadPlugins(state) {
70
+ const plugins = (await saltcorn.data.models.Plugin.find()).filter(
71
+ (plugin) => !["base", "sbadmin2"].includes(plugin.name)
72
+ );
73
+ for (const plugin of plugins) {
74
+ await loadPlugin(plugin);
75
+ state.registerPlugin(
76
+ plugin.name,
77
+ saltcorn[plugin.name],
78
+ plugin.configuration
79
+ );
80
+ }
81
+ return plugins;
82
+ }
83
+
84
+ /**
85
+ * add <script> tags dynamically
86
+ */
87
+ async function addScripts(version_tag) {
88
+ const scripts = [
89
+ { src: `static_assets/${version_tag}/jquery-3.6.0.min.js` },
90
+ { src: "js/bundle/common_chunks.bundle.js" },
91
+ { src: "js/bundle/markup.bundle.js", name: "markup" },
92
+ { src: "js/bundle/data.bundle.js", name: "data" },
93
+ { src: "js/bundle/base_plugin.bundle.js", name: "base_plugin" },
94
+ { src: "js/bundle/sbadmin2.bundle.js", name: "sbadmin2" },
95
+ ];
96
+ for (const script of scripts) {
97
+ await addScript(script);
98
+ }
99
+ }
100
+
101
+ const prepareHeader = (header) => {
102
+ let result = Object.assign({}, header);
103
+ const replacer = (key) => {
104
+ const value = header[key];
105
+ if (value?.startsWith("/plugins") || value?.startsWith("plugins"))
106
+ result[key] = value.replace(/^\/?plugins/, "sc_plugins");
107
+ };
108
+ replacer("script");
109
+ replacer("css");
110
+ return result;
111
+ };
112
+
113
+ /*
114
+ A plugin exports headers either as array, as key values object, or
115
+ as a function with a configuration parameter that returns an Array.
116
+ */
117
+ const collectPluginHeaders = (pluginRows) => {
118
+ const config = saltcorn.data.state.getState().mobileConfig;
119
+ config.pluginHeaders = [];
120
+ for (const row of pluginRows) {
121
+ const pluginHeaders = saltcorn[row.name].headers;
122
+ if (pluginHeaders) {
123
+ if (Array.isArray(pluginHeaders))
124
+ for (const header of pluginHeaders) {
125
+ config.pluginHeaders.push(prepareHeader(header));
126
+ }
127
+ else if (typeof pluginHeaders === "function") {
128
+ const headerResult = pluginHeaders(row.configuration || {});
129
+ if (Array.isArray(headerResult)) {
130
+ for (const header of headerResult)
131
+ config.pluginHeaders.push(prepareHeader(header));
132
+ }
133
+ } else
134
+ for (const value of Object.values(pluginHeaders)) {
135
+ config.pluginHeaders.push(prepareHeader(value));
136
+ }
137
+ }
138
+ }
139
+ };
140
+
141
+ const getJwt = async () => {
142
+ const rows = await saltcorn.data.db.select("jwt_table");
143
+ return rows?.length > 0 ? rows[0].jwt : null;
144
+ };
145
+
146
+ const initJwt = async () => {
147
+ if (!(await saltcorn.data.db.tableExists("jwt_table"))) {
148
+ await createJwtTable();
149
+ } else {
150
+ const jwt = await getJwt();
151
+ if (jwt) {
152
+ const state = saltcorn.data.state.getState();
153
+ state.mobileConfig.jwt = jwt;
154
+ }
155
+ }
156
+ };
157
+
158
+ const initI18Next = async (allLanguages) => {
159
+ await i18next.use(i18nextSprintfPostProcessor).init({
160
+ lng: "en",
161
+ allLanguages,
162
+ });
163
+ };
164
+
165
+ const showErrorPage = async (error) => {
166
+ const state = saltcorn.data.state.getState();
167
+ state.mobileConfig.inErrorState = true;
168
+ const page = await router.resolve({
169
+ pathname: "get/error_page",
170
+ fullWrap: true,
171
+ alerts: [
172
+ {
173
+ type: "error",
174
+ msg: error.message ? error.message : "An error occured.",
175
+ },
176
+ ],
177
+ });
178
+ await replaceIframe(page.content);
179
+ };
180
+
181
+ // the app comes back from background
182
+ const onResume = async () => {
183
+ if (typeof saltcorn === "undefined") return;
184
+ const mobileConfig = saltcorn.data.state.getState().mobileConfig;
185
+ if (mobileConfig?.allowOfflineMode) {
186
+ const netStatus = await Network.getStatus();
187
+ mobileConfig.networkState = netStatus.connectionType;
188
+ if (
189
+ mobileConfig.networkState === "none" &&
190
+ !mobileConfig.isOfflineMode &&
191
+ mobileConfig.jwt
192
+ ) {
193
+ try {
194
+ await startOfflineMode();
195
+ clearHistory();
196
+ if (mobileConfig.user?.id) await gotoEntryView();
197
+ else {
198
+ const decodedJwt = jwtDecode(mobileConfig.jwt);
199
+ mobileConfig.user = decodedJwt.user;
200
+ mobileConfig.isPublicUser = false;
201
+ }
202
+ addRoute({ route: mobileConfig.entry_point, query: undefined });
203
+ const page = await router.resolve({
204
+ pathname: mobileConfig.entry_point,
205
+ fullWrap: true,
206
+ alerts: [],
207
+ });
208
+ } catch (error) {
209
+ await showErrorPage(error);
210
+ }
211
+ }
212
+ }
213
+ };
214
+
215
+ const isPublicJwt = (jwt) => {
216
+ try {
217
+ if (!jwt) return false;
218
+ const decoded = jwtDecode(jwt);
219
+ return decoded.sub === "public";
220
+ } catch (error) {
221
+ console.log(
222
+ `Unable to inspect '${jwt}': ${
223
+ error.message ? error.message : "Unknown error"
224
+ }`
225
+ );
226
+ return false;
227
+ }
228
+ };
229
+
230
+ const isPublicEntryPoint = async (entryPoint) => {
231
+ try {
232
+ const tokens = entryPoint.split("/");
233
+ if (tokens.length < 3) throw new Error("The format is incorrect");
234
+ const name = tokens[tokens.length - 1];
235
+ const entryObj =
236
+ tokens[tokens.length - 2] === "view"
237
+ ? saltcorn.data.models.View.findOne({ name: name })
238
+ : saltcorn.data.models.Page.findOne({ name: name });
239
+ if (!entryObj) throw new Error(`The object '${name}' does not exist`);
240
+ else return entryObj.min_role === 100;
241
+ } catch (error) {
242
+ console.log(
243
+ `Unable to inspect '${entryPoint}': ${
244
+ error.message ? error.message : "Unknown error"
245
+ }`
246
+ );
247
+ return false;
248
+ }
249
+ };
250
+
251
+ const showLogin = async (alerts) => {
252
+ const page = await router.resolve({
253
+ pathname: "get/auth/login",
254
+ alerts,
255
+ });
256
+ await replaceIframe(page.content);
257
+ };
258
+
259
+ const takeLastLocation = () => {
260
+ let result = null;
261
+ const lastLocation = localStorage.getItem("lastLocation");
262
+ localStorage.removeItem("lastLocation");
263
+ if (lastLocation) {
264
+ try {
265
+ result = JSON.parse(lastLocation);
266
+ } catch (error) {
267
+ console.log(
268
+ `Unable to parse the last location: ${
269
+ error.message ? error.message : "Unknown error"
270
+ }`
271
+ );
272
+ }
273
+ }
274
+ return result;
275
+ };
276
+
277
+ // device is ready
278
+ export async function init({
279
+ mobileConfig,
280
+ tablesSchema,
281
+ schemaCreatedAt,
282
+ translations,
283
+ siteLogo,
284
+ }) {
285
+ try {
286
+ const lastLocation = takeLastLocation();
287
+ document.addEventListener("resume", onResume, false);
288
+ const { created_at } = schemaCreatedAt;
289
+ await addScripts(mobileConfig.version_tag);
290
+ saltcorn.data.db.connectObj.version_tag = mobileConfig.version_tag;
291
+
292
+ await saltcorn.data.db.init();
293
+ const updateNeeded = await dbUpdateNeeded(created_at);
294
+ if (updateNeeded) {
295
+ // update '_sc_plugins' first because of loadPlugins()
296
+ await updateScPlugins(tablesSchema);
297
+ }
298
+ const state = saltcorn.data.state.getState();
299
+ state.mobileConfig = mobileConfig;
300
+ state.mobileConfig.user = {};
301
+ state.registerPlugin("base", saltcorn.base_plugin);
302
+ state.registerPlugin("sbadmin2", saltcorn.sbadmin2);
303
+ collectPluginHeaders(await loadPlugins(state));
304
+ if (updateNeeded) await updateDb(tablesSchema);
305
+ await createSyncInfoTables(mobileConfig.synchedTables);
306
+ await initJwt();
307
+ await state.refresh_tables();
308
+ await state.refresh_views();
309
+ await state.refresh_pages();
310
+ await state.refresh_page_groups();
311
+ await state.refresh_triggers();
312
+ state.mobileConfig.localTableIds = await getTableIds(
313
+ mobileConfig.localUserTables
314
+ );
315
+ await state.setConfig("base_url", mobileConfig.server_path);
316
+ const entryPoint = mobileConfig.entry_point;
317
+ await initI18Next(translations);
318
+ state.mobileConfig.encodedSiteLogo = siteLogo;
319
+
320
+ state.mobileConfig.networkState = await Network.getStatus();
321
+ Network.addListener("networkStatusChange", networkChangeCallback);
322
+ const networkDisabled = state.mobileConfig.networkState === "none";
323
+ const jwt = state.mobileConfig.jwt;
324
+ const alerts = [];
325
+ if ((networkDisabled && jwt) || (await checkJWT(jwt))) {
326
+ const mobileConfig = state.mobileConfig;
327
+ const decodedJwt = jwtDecode(mobileConfig.jwt);
328
+ mobileConfig.user = decodedJwt.user;
329
+ mobileConfig.isPublicUser = false;
330
+ await i18next.changeLanguage(mobileConfig.user.language);
331
+ if (mobileConfig.allowOfflineMode) {
332
+ const { offlineUser } = (await getLastOfflineSession()) || {};
333
+ if (networkDisabled) {
334
+ if (offlineUser && offlineUser !== mobileConfig.user.email)
335
+ throw new Error(
336
+ `The offline mode is not available, '${offlineUser}' has not yet uploaded offline data.`
337
+ );
338
+ else
339
+ try {
340
+ await startOfflineMode();
341
+ } catch (error) {
342
+ throw new Error(
343
+ `Neither an internet connection nor the offline mode are available: ${
344
+ error.message ? error.message : "Unknown error"
345
+ }`
346
+ );
347
+ }
348
+ } else if (offlineUser) {
349
+ if (offlineUser === mobileConfig.user.email) {
350
+ await sync();
351
+ alerts.push({
352
+ type: "info",
353
+ msg: "Synchronized your offline data.",
354
+ });
355
+ } else
356
+ alerts.push({
357
+ type: "warning",
358
+ msg: `'${offlineUser}' has not yet uploaded offline data.`,
359
+ });
360
+ } else {
361
+ await sync();
362
+ alerts.push({
363
+ type: "info",
364
+ msg: "Synchronized your offline data.",
365
+ });
366
+ }
367
+ }
368
+ let page = null;
369
+ if (!lastLocation) {
370
+ addRoute({ route: entryPoint, query: undefined });
371
+ page = await router.resolve({
372
+ pathname: entryPoint,
373
+ fullWrap: true,
374
+ alerts,
375
+ });
376
+ } else {
377
+ addRoute({
378
+ route: lastLocation.route,
379
+ query: lastLocation.query,
380
+ });
381
+ page = await router.resolve({
382
+ pathname: lastLocation.route,
383
+ query: lastLocation.query,
384
+ fullWrap: true,
385
+ alerts,
386
+ });
387
+ }
388
+ if (page.content) await replaceIframe(page.content, page.isFile);
389
+ } else if (isPublicJwt(jwt)) {
390
+ const config = state.mobileConfig;
391
+ config.user = { role_id: 100, email: "public", language: "en" };
392
+ config.isPublicUser = true;
393
+ i18next.changeLanguage(config.user.language);
394
+ addRoute({ route: entryPoint, query: undefined });
395
+ const page = await router.resolve({
396
+ pathname: entryPoint,
397
+ fullWrap: true,
398
+ alerts,
399
+ });
400
+ if (page.content) await replaceIframe(page.content, page.isFile);
401
+ } else if (
402
+ (await isPublicEntryPoint(entryPoint)) &&
403
+ state.mobileConfig.autoPublicLogin
404
+ ) {
405
+ if (networkDisabled)
406
+ throw new Error(
407
+ "No internet connection or previous login is available. " +
408
+ "Please go online and reload, the public login is not yet supported."
409
+ );
410
+ await publicLogin(entryPoint);
411
+ } else await showLogin(alerts);
412
+ } catch (error) {
413
+ if (typeof saltcorn === "undefined" || typeof router === "undefined") {
414
+ const msg = `An error occured: ${
415
+ error.message ? error.message : "Unknown error"
416
+ }`;
417
+ console.log(msg);
418
+ alert(msg);
419
+ } else {
420
+ if (error.httpCode === 401) await showLogin([]);
421
+ else await showErrorPage(error);
422
+ }
423
+ }
424
+ }
@@ -0,0 +1,98 @@
1
+ import UniversalRouter from "universal-router";
2
+
3
+ import { updateTableRow, insertTableRow } from "./routes/api";
4
+ import { getLoginView, logoutAction, getSignupView } from "./routes/auth";
5
+ import { deleteRows } from "./routes/delete";
6
+ import { postToggleField } from "./routes/edit";
7
+ import { getErrorView } from "./routes/error";
8
+ import { postShowCalculated } from "./routes/fields";
9
+ import { postPageAction, getPage } from "./routes/page";
10
+ import {
11
+ getSyncSettingsView,
12
+ getAskDeleteOfflineData,
13
+ getAskUploadNotEnded,
14
+ } from "./routes/sync";
15
+
16
+ import { postView, postViewRoute, getView } from "./routes/view";
17
+
18
+ const routes = [
19
+ // api
20
+ {
21
+ path: "post/api/:tableName/:id",
22
+ action: updateTableRow,
23
+ },
24
+ {
25
+ path: "post/api/:tableName/",
26
+ action: insertTableRow,
27
+ },
28
+ // auth
29
+ {
30
+ path: "get/auth/login",
31
+ action: getLoginView,
32
+ },
33
+ {
34
+ path: "get/auth/logout",
35
+ action: logoutAction,
36
+ },
37
+ {
38
+ path: "get/auth/signup",
39
+ action: getSignupView,
40
+ },
41
+ // delete
42
+ {
43
+ path: "post/delete/:name/:id",
44
+ action: deleteRows,
45
+ },
46
+ // edit
47
+ {
48
+ path: "post/edit/toggle/:name/:id/:field_name",
49
+ action: postToggleField,
50
+ },
51
+ // error
52
+ {
53
+ path: "get/error_page",
54
+ action: getErrorView,
55
+ },
56
+ // field
57
+ {
58
+ path: "post/field/show-calculated/:tableName/:fieldName/:fieldview",
59
+ action: postShowCalculated,
60
+ },
61
+ // page
62
+ {
63
+ path: "post/page/:page_name/action/:rndid",
64
+ action: postPageAction,
65
+ },
66
+ {
67
+ path: "get/page/:page_name",
68
+ action: getPage,
69
+ },
70
+ // sync
71
+ {
72
+ path: "get/sync/sync_settings",
73
+ action: getSyncSettingsView,
74
+ },
75
+ {
76
+ path: "get/sync/ask_upload_not_ended",
77
+ action: getAskUploadNotEnded,
78
+ },
79
+ {
80
+ path: "get/sync/ask_delete_offline_data",
81
+ action: getAskDeleteOfflineData,
82
+ },
83
+ // view
84
+ {
85
+ path: "post/view/:viewname",
86
+ action: postView,
87
+ },
88
+ {
89
+ path: "post/view/:viewname/:route",
90
+ action: postViewRoute,
91
+ },
92
+ {
93
+ path: "get/view/:viewname",
94
+ action: getView,
95
+ },
96
+ ];
97
+
98
+ export const router = new UniversalRouter(routes);
@@ -1,6 +1,8 @@
1
- /*global i18next, saltcorn, currentLocation*/
1
+ /*global saltcorn, */
2
2
 
3
- function MobileRequest({
3
+ import i18next from "i18next";
4
+
5
+ export function MobileRequest({
4
6
  xhr = false,
5
7
  files = undefined,
6
8
  query = undefined,
@@ -8,8 +10,6 @@ function MobileRequest({
8
10
  refererRoute = undefined,
9
11
  } = {}) {
10
12
  const cfg = saltcorn.data.state.getState().mobileConfig;
11
- const roleId = cfg.role_id ? cfg.role_id : 100;
12
- const userId = cfg.user_id ? cfg.user_id : undefined;
13
13
  const flashMessages = [];
14
14
  const refererPath = refererRoute ? refererRoute.route : undefined;
15
15
  const referQuery =
@@ -33,7 +33,7 @@ function MobileRequest({
33
33
  },
34
34
  getLocale: () => {
35
35
  const mobileCfg = saltcorn.data.state.getState().mobileConfig;
36
- return mobileCfg?.language ? mobileCfg.language : "en";
36
+ return mobileCfg?.user?.language ? mobileCfg.user.language : "en";
37
37
  },
38
38
  user: cfg.user,
39
39
  flash: (type, msg) => {
@@ -1,4 +1,4 @@
1
- function MobileResponse() {
1
+ export function MobileResponse() {
2
2
  let jsonData = null;
3
3
  let sendData = null;
4
4
  let wrapHtml = null;
@@ -1,13 +1,12 @@
1
- /*global saltcorn, apiCall, MobileRequest, offlineHelper*/
1
+ /*global saltcorn */
2
+
3
+ import { apiCall } from "../../helpers/api";
4
+ import { setHasOfflineData } from "../../helpers/offline_mode";
2
5
 
3
6
  // post/api/:tableName/:id
4
- const updateTableRow = async (context) => {
7
+ export const updateTableRow = async (context) => {
5
8
  const { tableName, id } = context.params;
6
9
  const mobileConfig = saltcorn.data.state.getState().mobileConfig;
7
- const user = {
8
- id: mobileConfig.user_id,
9
- role_id: mobileConfig.role_id || 100,
10
- };
11
10
  const table = saltcorn.data.models.Table.findOne({ name: tableName });
12
11
  if (!table) throw new Error(`The table '${tableName}' does not exist.`);
13
12
  if (
@@ -24,10 +23,10 @@ const updateTableRow = async (context) => {
24
23
  id
25
24
  );
26
25
  if (errors.length > 0) throw new Error(errors.join(", "));
27
- const ins_res = await table.tryUpdateRow(row, id, user);
26
+ const ins_res = await table.tryUpdateRow(row, id, mobileConfig.user);
28
27
  if (ins_res.error)
29
28
  throw new Error(`Update '${table.name}' error: ${ins_res.error}`);
30
- if (mobileConfig.isOfflineMode) await offlineHelper.setHasOfflineData(true);
29
+ if (mobileConfig.isOfflineMode) await setHasOfflineData(true);
31
30
  return ins_res;
32
31
  } else {
33
32
  const response = await apiCall({
@@ -40,14 +39,10 @@ const updateTableRow = async (context) => {
40
39
  };
41
40
 
42
41
  // post/api/:tableName
43
- const insertTableRow = async (context) => {
42
+ export const insertTableRow = async (context) => {
44
43
  const { tableName } = context.params;
45
44
  const table = saltcorn.data.models.Table.findOne({ name: tableName });
46
45
  const mobileConfig = saltcorn.data.state.getState().mobileConfig;
47
- const user = {
48
- id: mobileConfig.user_id,
49
- role_id: mobileConfig.role_id || 100,
50
- };
51
46
  if (!table) throw new Error(`The table '${tableName}' does not exist.`);
52
47
  if (
53
48
  mobileConfig.isOfflineMode ||
@@ -62,11 +57,11 @@ const insertTableRow = async (context) => {
62
57
  table.getFields()
63
58
  );
64
59
  if (errors.length > 0) throw new Error(errors.join(", "));
65
- const ins_res = await table.tryInsertRow(row, user);
60
+ const ins_res = await table.tryInsertRow(row, mobileConfig.user);
66
61
  if (ins_res.error) {
67
62
  throw new Error(`Insert '${table.name}' error: ${ins_res.error}`);
68
63
  }
69
- if (mobileConfig.isOfflineMode) await offlineHelper.setHasOfflineData(true);
64
+ if (mobileConfig.isOfflineMode) await setHasOfflineData(true);
70
65
  return ins_res;
71
66
  } else {
72
67
  const response = await apiCall({
@@ -1,4 +1,10 @@
1
- /*global sbAdmin2Layout, apiCall, removeJwt, saltcorn, clearHistory, MobileRequest, MobileResponse, getHeaders*/
1
+ /*global saltcorn */
2
+ import { MobileRequest } from "../mocks/request";
3
+ import { MobileResponse } from "../mocks/response";
4
+ import { apiCall } from "../../helpers/api";
5
+ import { removeJwt } from "../../helpers/auth";
6
+ import { sbAdmin2Layout, getHeaders } from "../utils";
7
+ import { clearHistory } from "../../helpers/navigation";
2
8
 
3
9
  const prepareAuthForm = () => {
4
10
  return new saltcorn.data.models.Form({
@@ -75,7 +81,7 @@ const renderLoginView = async (entryPoint, versionTag, alerts = []) => {
75
81
  alerts,
76
82
  headers: [
77
83
  { css: `static_assets/${versionTag}/saltcorn.css` },
78
- { script: "js/utils/iframe_view_utils.js" },
84
+ { script: "js/iframe_view_utils.js" },
79
85
  ],
80
86
  csrfToken: false,
81
87
  });
@@ -92,13 +98,13 @@ const renderSignupView = (entryPoint, versionTag) => {
92
98
  alerts: [],
93
99
  headers: [
94
100
  { css: `static_assets/${versionTag}/saltcorn.css` },
95
- { script: "js/utils/iframe_view_utils.js" },
101
+ { script: "js/iframe_view_utils.js" },
96
102
  ],
97
103
  csrfToken: false,
98
104
  });
99
105
  };
100
106
 
101
- const getLoginView = async (context) => {
107
+ export const getLoginView = async (context) => {
102
108
  const mobileConfig = saltcorn.data.state.getState().mobileConfig;
103
109
  return {
104
110
  content: await renderLoginView(
@@ -110,7 +116,7 @@ const getLoginView = async (context) => {
110
116
  };
111
117
  };
112
118
 
113
- const getSignupView = async () => {
119
+ export const getSignupView = async () => {
114
120
  const config = saltcorn.data.state.getState().mobileConfig;
115
121
  return {
116
122
  content: renderSignupView(config.entry_point, config.version_tag),
@@ -118,7 +124,7 @@ const getSignupView = async () => {
118
124
  };
119
125
  };
120
126
 
121
- const logoutAction = async () => {
127
+ export const logoutAction = async () => {
122
128
  const config = saltcorn.data.state.getState().mobileConfig;
123
129
  const response = await apiCall({ method: "GET", path: "/auth/logout" });
124
130
  if (response.data.success) {
@@ -1,19 +1,22 @@
1
- /*global i18next, apiCall, saltcorn, offlineHelper*/
1
+ /*global saltcorn */
2
+ import { apiCall } from "../../helpers/api";
3
+ import { setHasOfflineData, hasOfflineRows } from "../../helpers/offline_mode";
4
+ import i18next from "i18next";
2
5
 
3
6
  // post/delete/:name/:id
4
- const deleteRows = async (context) => {
7
+ export const deleteRows = async (context) => {
5
8
  const { name, id } = context.params;
6
9
  const table = await saltcorn.data.models.Table.findOne({ name });
7
- const { isOfflineMode, localTableIds, role_id } =
10
+ const { isOfflineMode, localTableIds, user } =
8
11
  saltcorn.data.state.getState().mobileConfig;
9
12
  if (isOfflineMode || localTableIds.indexOf(table.id) >= 0) {
10
- if (role_id <= table.min_role_write) {
13
+ if (user.role_id <= table.min_role_write) {
11
14
  await table.deleteRows({ id });
12
15
  // TODO 'table.is_owner' check?
13
16
  } else
14
17
  throw new saltcorn.data.utils.NotAuthorized(i18next.t("Not authorized"));
15
- if (isOfflineMode && (await offlineHelper.hasOfflineRows()))
16
- await offlineHelper.setHasOfflineData(true);
18
+ if (isOfflineMode && (await hasOfflineRows()))
19
+ await setHasOfflineData(true);
17
20
  // if (isOfflineMode && !(await offlineHelper.hasOfflineRows())) {
18
21
  // await offlineHelper.setOfflineSession(null);
19
22
  // }