@saltcorn/mobile-app 1.3.0-beta.1 → 1.3.0-beta.3

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.3.0-beta.1",
4
+ "version": "1.3.0-beta.3",
5
5
  "description": "Saltcorn mobile app for Android and iOS",
6
6
  "main": "index.js",
7
7
  "scripts": {
@@ -21,7 +21,10 @@
21
21
  "axios": "^1.7.7",
22
22
  "jwt-decode": "4.0.0",
23
23
  "i18next": "24.2.2",
24
- "i18next-sprintf-postprocessor": "0.2.2"
24
+ "i18next-sprintf-postprocessor": "0.2.2",
25
+ "crypto-browserify": "3.12.1",
26
+ "vm-browserify": "1.1.2",
27
+ "stream-browserify": "3.0.0"
25
28
  },
26
29
  "devDependencies": {
27
30
  "xml2js": "0.6.2",
@@ -333,6 +333,7 @@ export async function replaceIframeInnerContent(content) {
333
333
  }
334
334
  iframe.contentWindow.scrollTo(0, 0);
335
335
  iframe.contentWindow.initialize_page();
336
+ iframeDocument.dispatchEvent(new Event("mobile-loaded"));
336
337
  }
337
338
 
338
339
  export function splitPathQuery(url) {
package/src/init.js CHANGED
@@ -1,4 +1,4 @@
1
- /*global saltcorn, Capacitor, cordova */
1
+ /*global saltcorn, Capacitor, cordova, _test_schema_ */
2
2
 
3
3
  import {
4
4
  startOfflineMode,
@@ -31,6 +31,8 @@ import { jwtDecode } from "jwt-decode";
31
31
 
32
32
  import { Network } from "@capacitor/network";
33
33
 
34
+ import { defineCustomElements } from "jeep-sqlite/loader";
35
+
34
36
  async function addScript(scriptObj) {
35
37
  let waited = 0;
36
38
  const maxWait = 3000;
@@ -162,13 +164,15 @@ const initI18Next = async () => {
162
164
  for (const key of Object.keys(
163
165
  saltcorn.data.models.config.available_languages
164
166
  )) {
165
- const localeFile = await readJSONCordova(
166
- `${key}.json`,
167
- `${cordova.file.applicationDirectory}public/data/locales`
168
- );
169
- resources[key] = {
170
- translation: localeFile,
171
- };
167
+ if (Capacitor.platform !== "web") {
168
+ const localeFile = await readJSONCordova(
169
+ `${key}.json`,
170
+ `${cordova.file.applicationDirectory}public/data/locales`
171
+ );
172
+ resources[key] = {
173
+ translation: localeFile,
174
+ };
175
+ }
172
176
  }
173
177
  await i18next.use(i18nextSprintfPostProcessor).init({
174
178
  lng: "en",
@@ -305,22 +309,28 @@ const postShare = async (shareData) => {
305
309
  };
306
310
 
307
311
  const readSchemaIfNeeded = async () => {
308
- let tablesJSON = null;
309
- const { created_at } = await readJSONCordova(
310
- "tables_created_at.json",
311
- `${cordova.file.applicationDirectory}${"public"}/data`
312
- );
313
- const updateNeeded = await dbUpdateNeeded(created_at);
314
- if (updateNeeded) {
315
- tablesJSON = await readJSONCordova(
316
- "tables.json",
312
+ if (Capacitor.platform !== "web") {
313
+ let tablesJSON = null;
314
+ const { created_at } = await readJSONCordova(
315
+ "tables_created_at.json",
317
316
  `${cordova.file.applicationDirectory}${"public"}/data`
318
317
  );
318
+ const updateNeeded = await dbUpdateNeeded(created_at);
319
+ if (updateNeeded) {
320
+ tablesJSON = await readJSONCordova(
321
+ "tables.json",
322
+ `${cordova.file.applicationDirectory}${"public"}/data`
323
+ );
324
+ }
325
+ return { updateNeeded, tablesJSON };
326
+ } else {
327
+ // test environment
328
+ return { updateNeeded: true, tablesJSON: _test_schema_ };
319
329
  }
320
- return { updateNeeded, tablesJSON };
321
330
  };
322
331
 
323
332
  const readSiteLogo = async (state) => {
333
+ if (Capacitor.platform === "web") return "";
324
334
  try {
325
335
  const base64 = await readTextCordova(
326
336
  "encoded_site_logo.txt",
@@ -340,6 +350,13 @@ const readSiteLogo = async (state) => {
340
350
  // device is ready
341
351
  export async function init(mobileConfig) {
342
352
  try {
353
+ if (Capacitor.platform === "web") {
354
+ defineCustomElements(window);
355
+ await customElements.whenDefined("jeep-sqlite");
356
+ const jeepSqlite = document.createElement("jeep-sqlite");
357
+ document.body.appendChild(jeepSqlite);
358
+ await jeepSqlite.componentOnReady();
359
+ }
343
360
  const lastLocation = takeLastLocation();
344
361
  document.addEventListener("resume", onResume, false);
345
362
  await addScripts(mobileConfig.version_tag);
@@ -1,6 +1,11 @@
1
1
  import UniversalRouter from "universal-router";
2
2
 
3
- import { updateTableRow, insertTableRow } from "./routes/api";
3
+ import {
4
+ runAction,
5
+ loadTableRows,
6
+ updateTableRow,
7
+ insertTableRow,
8
+ } from "./routes/api";
4
9
  import { getLoginView, logoutAction, getSignupView } from "./routes/auth";
5
10
  import { deleteRows } from "./routes/delete";
6
11
  import { postToggleField } from "./routes/edit";
@@ -22,14 +27,28 @@ import {
22
27
 
23
28
  const routes = [
24
29
  // api
30
+ {
31
+ path: "post/api/action/:action",
32
+ action: runAction,
33
+ },
34
+ {
35
+ path: "get/api/:tableName",
36
+ action: loadTableRows,
37
+ },
25
38
  {
26
39
  path: "post/api/:tableName/:id",
27
40
  action: updateTableRow,
28
41
  },
42
+
29
43
  {
30
44
  path: "post/api/:tableName/",
31
45
  action: insertTableRow,
32
46
  },
47
+ {
48
+ path: "post/api/:tableName",
49
+ action: insertTableRow,
50
+ },
51
+
33
52
  // auth
34
53
  {
35
54
  path: "get/auth/login",
@@ -44,10 +63,16 @@ const routes = [
44
63
  action: getSignupView,
45
64
  },
46
65
  // delete
66
+
67
+ {
68
+ path: "post/delete/:tableName/:id", // legacy
69
+ action: deleteRows,
70
+ },
47
71
  {
48
- path: "post/delete/:name/:id",
72
+ path: "delete/api/:tableName/:id",
49
73
  action: deleteRows,
50
74
  },
75
+
51
76
  // edit
52
77
  {
53
78
  path: "post/edit/toggle/:name/:id/:field_name",
@@ -1,7 +1,36 @@
1
- /*global saltcorn */
1
+ /* global saltcorn */
2
2
 
3
3
  import { apiCall } from "../../helpers/api";
4
4
  import { setHasOfflineData } from "../../helpers/offline_mode";
5
+ import { parseQuery } from "../utils";
6
+ import { MobileRequest } from "../mocks/request";
7
+
8
+ // get/api/:tableName
9
+ export const loadTableRows = async (context) => {
10
+ const { tableName } = context.params;
11
+ const queryString = context.query;
12
+ const query = queryString ? parseQuery(queryString) : {};
13
+
14
+ const mobileConfig = saltcorn.data.state.getState().mobileConfig;
15
+ const table = saltcorn.data.models.Table.findOne({ name: tableName });
16
+ if (!table) throw new Error(`The table '${tableName}' does not exist.`);
17
+ if (
18
+ mobileConfig.isOfflineMode ||
19
+ mobileConfig.localTableIds.indexOf(table.id) >= 0
20
+ ) {
21
+ const rows = await table.getRows(query, {
22
+ orderBy: table.pk_name,
23
+ forUser: mobileConfig.user,
24
+ });
25
+ return { success: rows };
26
+ } else {
27
+ const response = await apiCall({
28
+ method: "GET",
29
+ path: `/api/${tableName}${queryString ? `?${queryString}` : ""}`,
30
+ });
31
+ return response.data;
32
+ }
33
+ };
5
34
 
6
35
  // post/api/:tableName/:id
7
36
  export const updateTableRow = async (context) => {
@@ -32,7 +61,7 @@ export const updateTableRow = async (context) => {
32
61
  const response = await apiCall({
33
62
  method: "POST",
34
63
  path: `/api/${tableName}/${id}`,
35
- body: context.query,
64
+ body: context.body || context.query,
36
65
  });
37
66
  return response.data;
38
67
  }
@@ -67,8 +96,43 @@ export const insertTableRow = async (context) => {
67
96
  const response = await apiCall({
68
97
  method: "POST",
69
98
  path: `/api/${tableName}`,
70
- body: context.query,
99
+ body: context.body || context.query,
100
+ });
101
+ return response.data;
102
+ }
103
+ };
104
+
105
+ // post/api/:action
106
+ export const runAction = async (context) => {
107
+ const { action } = context.params;
108
+ const mobileConfig = saltcorn.data.state.getState().mobileConfig;
109
+ if (mobileConfig.isOfflineMode) {
110
+ const req = new MobileRequest();
111
+ const trigger = saltcorn.data.models.Trigger.findOne({
112
+ name: action,
113
+ });
114
+ if (!trigger) throw new Error(`Action '${action}' does not exist.`);
115
+ const actionObj = saltcorn.data.state.getState().actions[trigger.action];
116
+ const resp = await actionObj.run({
117
+ configuration: trigger.configuration,
118
+ body: context.body || {},
119
+ row: context.body || {},
120
+ req,
121
+ user: req.user,
71
122
  });
123
+ if (resp?.error) {
124
+ const { error, ...rest } = resp;
125
+ return { success: false, error, data: rest };
126
+ } else return { success: true, data: resp };
127
+ } else {
128
+ const response = await apiCall({
129
+ method: "POST",
130
+ path: `/api/action/${action}`,
131
+ body: context.body,
132
+ });
133
+ if (response.success === false) {
134
+ throw new Error(`Action '${action}' failed: ${response.error}`);
135
+ }
72
136
  return response.data;
73
137
  }
74
138
  };
@@ -5,8 +5,8 @@ import i18next from "i18next";
5
5
 
6
6
  // post/delete/:name/:id
7
7
  export const deleteRows = async (context) => {
8
- const { name, id } = context.params;
9
- const table = await saltcorn.data.models.Table.findOne({ name });
8
+ const { tableName, id } = context.params;
9
+ const table = await saltcorn.data.models.Table.findOne({ name: tableName });
10
10
  const { isOfflineMode, localTableIds, user } =
11
11
  saltcorn.data.state.getState().mobileConfig;
12
12
  if (isOfflineMode || localTableIds.indexOf(table.id) >= 0) {
@@ -21,7 +21,7 @@ export const deleteRows = async (context) => {
21
21
  // await offlineHelper.setOfflineSession(null);
22
22
  // }
23
23
  } else {
24
- await apiCall({ method: "POST", path: `/delete/${name}/${id}` });
24
+ await apiCall({ method: "POST", path: `/delete/${tableName}/${id}` });
25
25
  }
26
26
  const redirect = context.data?.after_delete_url
27
27
  ? context.data.after_delete_url === "/"
package/webpack.config.js CHANGED
@@ -37,6 +37,14 @@ module.exports = {
37
37
  },
38
38
  ],
39
39
  },
40
+ resolve: {
41
+ fallback: {
42
+ "crypto": require.resolve("crypto-browserify"),
43
+ vm: require.resolve("vm-browserify"),
44
+ stream: require.resolve("stream-browserify"),
45
+
46
+ }
47
+ },
40
48
  target: "web", // Use web target for browser compatibility
41
49
  mode: "development",
42
50
  };
package/www/index.html CHANGED
@@ -6,6 +6,8 @@
6
6
  content="width=device-width, initial-scale=1, maximum-scale=1"
7
7
  />
8
8
  <script src="data/config.js"></script>
9
+ <!-- only set in test mode -->
10
+ <script src="data/test_schema.js"></script>
9
11
  <script type="module" src="./dist/bundle.js"></script>
10
12
  <script src="js/iframe_view_utils.js"></script>
11
13
 
@@ -14,12 +16,16 @@
14
16
  if (window.saltcorn) window.saltcorn.mobileApp = mobileApp;
15
17
  else window.saltcorn = { mobileApp };
16
18
 
17
- document.addEventListener("deviceready", () => {
19
+ if (Capacitor.platform !== "web") {
20
+ document.addEventListener("deviceready", () => {
21
+ saltcorn.mobileApp.init(_sc_mobile_config);
22
+ });
23
+ document.addEventListener("backbutton", async () => {
24
+ await saltcorn.mobileApp.navigation.goBack(1, true);
25
+ });
26
+ } else {
18
27
  saltcorn.mobileApp.init(_sc_mobile_config);
19
- });
20
- document.addEventListener("backbutton", async () => {
21
- await saltcorn.mobileApp.navigation.goBack(1, true);
22
- });
28
+ }
23
29
  </script>
24
30
  </head>
25
31