@saltcorn/mobile-app 0.8.7-beta.0 → 0.8.7-beta.2
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 +1 -1
- package/www/index.html +46 -0
- package/www/js/mocks/response.js +1 -1
- package/www/js/routes/api.js +55 -3
- package/www/js/routes/common.js +4 -2
- package/www/js/routes/init.js +5 -1
- package/www/js/utils/file_helpers.js +6 -1
- package/www/js/utils/global_utils.js +1 -1
- package/www/js/utils/iframe_view_utils.js +25 -20
package/package.json
CHANGED
package/www/index.html
CHANGED
|
@@ -177,6 +177,22 @@
|
|
|
177
177
|
});
|
|
178
178
|
};
|
|
179
179
|
|
|
180
|
+
const readSiteLogo = async (state) => {
|
|
181
|
+
try {
|
|
182
|
+
const base64 = await readText(
|
|
183
|
+
"encoded_site_logo.txt",
|
|
184
|
+
`${cordova.file.applicationDirectory}www`
|
|
185
|
+
);
|
|
186
|
+
state.mobileConfig.encodedSiteLogo = base64;
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.log(
|
|
189
|
+
`Unable to read the site logo file: ${
|
|
190
|
+
error.message ? error.message : "Unknown error"
|
|
191
|
+
}`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
180
196
|
// the app comes back from background
|
|
181
197
|
const onResume = async () => {
|
|
182
198
|
if (typeof saltcorn === "undefined") return;
|
|
@@ -212,6 +228,21 @@
|
|
|
212
228
|
}
|
|
213
229
|
};
|
|
214
230
|
|
|
231
|
+
const isPublicJwt = (jwt) => {
|
|
232
|
+
try {
|
|
233
|
+
if (!jwt) return false;
|
|
234
|
+
const decoded = jwt_decode(jwt);
|
|
235
|
+
return decoded.sub === "public";
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.log(
|
|
238
|
+
`Unable to inspect '${jwt}': ${
|
|
239
|
+
error.message ? error.message : "Unknown error"
|
|
240
|
+
}`
|
|
241
|
+
);
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
215
246
|
// device is ready
|
|
216
247
|
const init = async () => {
|
|
217
248
|
document.addEventListener("resume", onResume, false);
|
|
@@ -252,6 +283,7 @@
|
|
|
252
283
|
setVersionTtag();
|
|
253
284
|
const entryPoint = config.entry_point;
|
|
254
285
|
await initI18Next();
|
|
286
|
+
await readSiteLogo(state);
|
|
255
287
|
state.mobileConfig.networkState = navigator.connection.type;
|
|
256
288
|
document.addEventListener(
|
|
257
289
|
"offline",
|
|
@@ -320,6 +352,20 @@
|
|
|
320
352
|
alerts,
|
|
321
353
|
});
|
|
322
354
|
await replaceIframe(page.content);
|
|
355
|
+
} else if (isPublicJwt(jwt)) {
|
|
356
|
+
const config = state.mobileConfig;
|
|
357
|
+
config.role_id = 100;
|
|
358
|
+
config.user_name = "public";
|
|
359
|
+
config.language = "en";
|
|
360
|
+
config.isPublicUser = true;
|
|
361
|
+
i18next.changeLanguage(config.language);
|
|
362
|
+
addRoute({ route: entryPoint, query: undefined });
|
|
363
|
+
const page = await router.resolve({
|
|
364
|
+
pathname: entryPoint,
|
|
365
|
+
fullWrap: true,
|
|
366
|
+
alerts,
|
|
367
|
+
});
|
|
368
|
+
await replaceIframe(page.content);
|
|
323
369
|
} else {
|
|
324
370
|
const page = await router.resolve({
|
|
325
371
|
pathname: "get/auth/login",
|
package/www/js/mocks/response.js
CHANGED
package/www/js/routes/api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*global saltcorn, apiCall*/
|
|
1
|
+
/*global saltcorn, apiCall, MobileRequest, offlineHelper*/
|
|
2
2
|
|
|
3
3
|
// post/api/:tableName/:id
|
|
4
4
|
const updateTableRow = async (context) => {
|
|
@@ -26,8 +26,15 @@ const updateTableRow = async (context) => {
|
|
|
26
26
|
if (errors.length > 0) throw new Error(errors.join(", "));
|
|
27
27
|
const ins_res = await table.tryUpdateRow(row, id, user);
|
|
28
28
|
if (ins_res.error)
|
|
29
|
-
throw new Error(`Update ${table.name} error: ${ins_res.error}`);
|
|
30
|
-
|
|
29
|
+
throw new Error(`Update '${table.name}' error: ${ins_res.error}`);
|
|
30
|
+
if (
|
|
31
|
+
mobileConfig.isOfflineMode &&
|
|
32
|
+
!(await offlineHelper.getLastOfflineSession())
|
|
33
|
+
)
|
|
34
|
+
await offlineHelper.setOfflineSession({
|
|
35
|
+
offlineUser: mobileConfig.user_name,
|
|
36
|
+
});
|
|
37
|
+
return ins_res;
|
|
31
38
|
} else {
|
|
32
39
|
const response = await apiCall({
|
|
33
40
|
method: "POST",
|
|
@@ -37,3 +44,48 @@ const updateTableRow = async (context) => {
|
|
|
37
44
|
return response.data;
|
|
38
45
|
}
|
|
39
46
|
};
|
|
47
|
+
|
|
48
|
+
// post/api/:tableName
|
|
49
|
+
const insertTableRow = async (context) => {
|
|
50
|
+
const { tableName } = context.params;
|
|
51
|
+
const table = saltcorn.data.models.Table.findOne({ name: tableName });
|
|
52
|
+
const mobileConfig = saltcorn.data.state.getState().mobileConfig;
|
|
53
|
+
const user = {
|
|
54
|
+
id: mobileConfig.user_id,
|
|
55
|
+
role_id: mobileConfig.role_id || 100,
|
|
56
|
+
};
|
|
57
|
+
if (!table) throw new Error(`The table '${tableName}' does not exist.`);
|
|
58
|
+
if (
|
|
59
|
+
mobileConfig.isOfflineMode ||
|
|
60
|
+
mobileConfig.localTableIds.indexOf(table.id) >= 0
|
|
61
|
+
) {
|
|
62
|
+
const row = {};
|
|
63
|
+
for (const [k, v] of new URLSearchParams(context.query).entries()) {
|
|
64
|
+
row[k] = v;
|
|
65
|
+
}
|
|
66
|
+
const errors = await saltcorn.data.web_mobile_commons.prepare_insert_row(
|
|
67
|
+
row,
|
|
68
|
+
table.getFields()
|
|
69
|
+
);
|
|
70
|
+
if (errors.length > 0) throw new Error(errors.join(", "));
|
|
71
|
+
const ins_res = await table.tryInsertRow(row, user);
|
|
72
|
+
if (ins_res.error) {
|
|
73
|
+
throw new Error(`Insert '${table.name}' error: ${ins_res.error}`);
|
|
74
|
+
}
|
|
75
|
+
if (
|
|
76
|
+
mobileConfig.isOfflineMode &&
|
|
77
|
+
!(await offlineHelper.getLastOfflineSession())
|
|
78
|
+
)
|
|
79
|
+
await offlineHelper.setOfflineSession({
|
|
80
|
+
offlineUser: mobileConfig.user_name,
|
|
81
|
+
});
|
|
82
|
+
return ins_res;
|
|
83
|
+
} else {
|
|
84
|
+
const response = await apiCall({
|
|
85
|
+
method: "POST",
|
|
86
|
+
path: `/api/${tableName}`,
|
|
87
|
+
body: context.query,
|
|
88
|
+
});
|
|
89
|
+
return response.data;
|
|
90
|
+
}
|
|
91
|
+
};
|
package/www/js/routes/common.js
CHANGED
|
@@ -131,8 +131,10 @@ const wrapContents = (contents, title, context, req) => {
|
|
|
131
131
|
role: state.mobileConfig.role_id,
|
|
132
132
|
menu: getMenu(req),
|
|
133
133
|
headers: getHeaders(),
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
brand: {
|
|
135
|
+
name: state.getConfig("site_name") || "Saltcorn",
|
|
136
|
+
logo: state.mobileConfig.encodedSiteLogo,
|
|
137
|
+
},
|
|
136
138
|
bodyClass: "",
|
|
137
139
|
currentUrl: "",
|
|
138
140
|
})
|
package/www/js/routes/init.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*global postView, postViewRoute, getView, postToggleField, deleteRows, postPageAction, getPage, getLoginView, logoutAction, getSignupView, getErrorView, window, getSyncSettingsView, getAskDeleteOfflineData, getAskUploadNotEnded, updateTableRow */
|
|
1
|
+
/*global postView, postViewRoute, getView, postToggleField, deleteRows, postPageAction, getPage, getLoginView, logoutAction, getSignupView, getErrorView, window, getSyncSettingsView, getAskDeleteOfflineData, getAskUploadNotEnded, updateTableRow, insertTableRow */
|
|
2
2
|
// TODO module namespacese
|
|
3
3
|
|
|
4
4
|
const initRoutes = async () => {
|
|
@@ -19,6 +19,10 @@ const initRoutes = async () => {
|
|
|
19
19
|
path: "post/api/:tableName/:id",
|
|
20
20
|
action: updateTableRow,
|
|
21
21
|
},
|
|
22
|
+
{
|
|
23
|
+
path: "post/api/:tableName/",
|
|
24
|
+
action: insertTableRow,
|
|
25
|
+
},
|
|
22
26
|
{
|
|
23
27
|
path: "post/edit/toggle/:name/:id/:field_name",
|
|
24
28
|
action: postToggleField,
|
|
@@ -24,6 +24,11 @@ function getDirEntry(directory) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
async function readJSON(fileName, dirName) {
|
|
27
|
+
const text = await readText(fileName, dirName);
|
|
28
|
+
return JSON.parse(text);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function readText(fileName, dirName) {
|
|
27
32
|
const dirEntry = await getDirEntry(dirName);
|
|
28
33
|
return new Promise((resolve, reject) => {
|
|
29
34
|
dirEntry.getFile(
|
|
@@ -33,7 +38,7 @@ async function readJSON(fileName, dirName) {
|
|
|
33
38
|
fileEntry.file(function (file) {
|
|
34
39
|
let reader = new FileReader();
|
|
35
40
|
reader.onloadend = function (e) {
|
|
36
|
-
resolve(
|
|
41
|
+
resolve(this.result);
|
|
37
42
|
};
|
|
38
43
|
reader.readAsText(file);
|
|
39
44
|
});
|
|
@@ -201,7 +201,7 @@ async function handleRoute(route, query, files) {
|
|
|
201
201
|
clearHistory();
|
|
202
202
|
await gotoEntryView();
|
|
203
203
|
} else {
|
|
204
|
-
if (route === "/") return await gotoEntryView();
|
|
204
|
+
if (route === "/" || route === "get") return await gotoEntryView();
|
|
205
205
|
const safeRoute = route ? route : currentLocation();
|
|
206
206
|
addRoute({ route: safeRoute, query });
|
|
207
207
|
const page = await router.resolve({
|
|
@@ -213,6 +213,7 @@ async function publicLogin(entryPoint) {
|
|
|
213
213
|
await parent.setJwt(loginResult);
|
|
214
214
|
config.jwt = loginResult;
|
|
215
215
|
parent.i18next.changeLanguage(config.language);
|
|
216
|
+
parent.addRoute({ route: entryPoint, query: undefined });
|
|
216
217
|
const page = await parent.router.resolve({
|
|
217
218
|
pathname: entryPoint,
|
|
218
219
|
fullWrap: true,
|
|
@@ -530,19 +531,6 @@ async function make_unique_field(
|
|
|
530
531
|
}
|
|
531
532
|
}
|
|
532
533
|
|
|
533
|
-
async function buildEncodedImage(fileId, elementId) {
|
|
534
|
-
const base64Encoded = await parent.loadEncodedFile(fileId);
|
|
535
|
-
document.getElementById(elementId).src = base64Encoded;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
async function buildEncodedBgImage(fileId, elementId) {
|
|
539
|
-
const base64Encoded = await parent.loadEncodedFile(fileId);
|
|
540
|
-
// ensure that not unique IDs work, but should not happen
|
|
541
|
-
$(`#${elementId}`).each(function () {
|
|
542
|
-
$(this).prev()[0].style.backgroundImage = `url("${base64Encoded}")`;
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
|
|
546
534
|
function openFile(fileId) {
|
|
547
535
|
// TODO fileIds with whitespaces do not work
|
|
548
536
|
const config = parent.saltcorn.data.state.getState().mobileConfig;
|
|
@@ -574,14 +562,31 @@ async function clear_state() {
|
|
|
574
562
|
}
|
|
575
563
|
|
|
576
564
|
async function view_post(viewname, route, data, onDone) {
|
|
565
|
+
const mobileConfig = parent.saltcorn.data.state.getState().mobileConfig;
|
|
566
|
+
const view = parent.saltcorn.data.models.View.findOne({ name: viewname });
|
|
577
567
|
try {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
568
|
+
let respData = undefined;
|
|
569
|
+
if (
|
|
570
|
+
mobileConfig.isOfflineMode ||
|
|
571
|
+
(view?.table_id && mobileConfig.localTableIds.indexOf(view.table_id) >= 0)
|
|
572
|
+
) {
|
|
573
|
+
respData = await parent.router.resolve({
|
|
574
|
+
pathname: `post/view/${viewname}/${route}`,
|
|
575
|
+
data,
|
|
576
|
+
});
|
|
577
|
+
} else {
|
|
578
|
+
const response = await parent.apiCall({
|
|
579
|
+
method: "POST",
|
|
580
|
+
path: "/view/" + viewname + "/" + route,
|
|
581
|
+
body: data,
|
|
582
|
+
});
|
|
583
|
+
if (response) respData = response.data;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (!respData)
|
|
587
|
+
throw new Error(`The response of '${viewname}/${route}' is ${respData}`);
|
|
588
|
+
if (onDone) onDone(respData);
|
|
589
|
+
common_done(respData);
|
|
585
590
|
} catch (error) {
|
|
586
591
|
parent.errorAlert(error);
|
|
587
592
|
}
|