@saltcorn/mobile-app 0.9.1-beta.0 → 0.9.1-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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@saltcorn/mobile-app",
3
3
  "displayName": "Saltcorn mobile app",
4
- "version": "0.9.1-beta.0",
4
+ "version": "0.9.1-beta.2",
5
5
  "description": "Apache Cordova application with @saltcorn/markup",
6
6
  "main": "index.js",
7
7
  "scripts": {
package/www/index.html CHANGED
@@ -415,7 +415,7 @@
415
415
  fullWrap: true,
416
416
  alerts,
417
417
  });
418
- await replaceIframe(page.content);
418
+ if (page.content) await replaceIframe(page.content, page.isFile);
419
419
  } else if (isPublicJwt(jwt)) {
420
420
  const config = state.mobileConfig;
421
421
  config.role_id = 100;
@@ -429,7 +429,7 @@
429
429
  fullWrap: true,
430
430
  alerts,
431
431
  });
432
- await replaceIframe(page.content);
432
+ if (page.content) await replaceIframe(page.content, page.isFile);
433
433
  } else if (
434
434
  (await isPublicEntryPoint(entryPoint)) &&
435
435
  state.mobileConfig.autoPublicLogin
@@ -158,5 +158,9 @@ const wrapContents = (contents, title, context, req) => {
158
158
  alerts: prepareAlerts(context, req),
159
159
  role: state.mobileConfig.role_id,
160
160
  });
161
- return { content: wrappedContent, title: title };
161
+ return {
162
+ content: wrappedContent,
163
+ title: title,
164
+ replaceIframe: context.fullWrap,
165
+ };
162
166
  };
@@ -1,4 +1,4 @@
1
- /*global MobileRequest, parseQuery, MobileResponse, wrapContents, saltcorn*/
1
+ /*global MobileRequest, parseQuery, MobileResponse, wrapContents, saltcorn, loadFileAsText*/
2
2
 
3
3
  // post/page/:pagename/action/:rndid
4
4
  const postPageAction = async (context) => {
@@ -37,6 +37,13 @@ const getPage = async (context) => {
37
37
  const query = parseQuery(context.query);
38
38
  const res = new MobileResponse();
39
39
  const contents = await page.run(query, { res, req });
40
- const title = "title"; // TODO
41
- return wrapContents(contents, title, context, req);
40
+ if (contents.html_file) {
41
+ if (state.mobileConfig?.isOfflineMode)
42
+ throw new Error(req.__("Offline mode: cannot load file"));
43
+ const content = await loadFileAsText(contents.html_file);
44
+ return { content, title: "title", replaceIframe: true, isFile: true };
45
+ } else {
46
+ const title = "title"; // TODO
47
+ return wrapContents(contents, title, context, req);
48
+ }
42
49
  };
@@ -75,6 +75,7 @@ function showAlerts(alerts, toast = true) {
75
75
  const area = iframe.contentWindow.document.getElementById(
76
76
  toast ? "toasts-area" : "top-alert"
77
77
  );
78
+ if (!area) return false;
78
79
  const successIds = [];
79
80
  area.innerHTML = "";
80
81
  for (const { type, msg } of alerts) {
@@ -93,6 +94,7 @@ function showAlerts(alerts, toast = true) {
93
94
  }, 5000);
94
95
  }
95
96
  }
97
+ return true;
96
98
  }
97
99
 
98
100
  function clearTopAlerts() {
@@ -103,6 +105,37 @@ function clearTopAlerts() {
103
105
  if (topAlert) topAlert.innerHTML = "";
104
106
  }
105
107
 
108
+ // TODO combine with loadEncodedFile
109
+ async function loadFileAsText(fileId) {
110
+ try {
111
+ const response = await apiCall({
112
+ method: "GET",
113
+ path: `/files/download/${fileId}`,
114
+ responseType: "blob",
115
+ });
116
+ return new Promise((resolve, reject) => {
117
+ const reader = new FileReader();
118
+ reader.onloadend = () => {
119
+ return resolve(reader.result);
120
+ };
121
+ reader.onerror = (error) => {
122
+ return reject(error);
123
+ };
124
+ reader.readAsText(response.data);
125
+ });
126
+ } catch (error) {
127
+ if (
128
+ !showAlerts([
129
+ {
130
+ type: "error",
131
+ msg: error.message ? error.message : "An error occured.",
132
+ },
133
+ ])
134
+ );
135
+ throw error;
136
+ }
137
+ }
138
+
106
139
  async function loadEncodedFile(fileId) {
107
140
  try {
108
141
  const response = await apiCall({
@@ -141,11 +174,36 @@ function splitPathQuery(url) {
141
174
  return { path, query };
142
175
  }
143
176
 
144
- async function replaceIframe(content) {
177
+ async function replaceIframe(content, isFile = false) {
178
+ const iframe = document.getElementById("content-iframe");
145
179
  await write("content.html", `${cordova.file.dataDirectory}`, content);
146
180
  const url = await getDirEntry(`${cordova.file.dataDirectory}content.html`);
147
- const iframe = document.getElementById("content-iframe");
148
181
  iframe.src = url.toURL();
182
+ if (isFile) {
183
+ iframe.setAttribute("is-html-file", true);
184
+ await new Promise((resolve, reject) => {
185
+ iframe.onload = () => {
186
+ try {
187
+ const _iframe = document.getElementById("content-iframe");
188
+ const iframeDoc = _iframe.contentWindow.document;
189
+ const baseEl = iframeDoc.createElement("base");
190
+ iframeDoc.head.appendChild(baseEl);
191
+ baseEl.href = "http://localhost";
192
+ const scriptEl = iframeDoc.createElement("script");
193
+ iframeDoc.body.appendChild(scriptEl);
194
+ scriptEl.onload = () => {
195
+ resolve();
196
+ };
197
+ scriptEl.src = "js/utils/iframe_view_utils.js";
198
+ } catch (e) {
199
+ reject(e);
200
+ }
201
+ };
202
+ iframe.onerror = () => {
203
+ reject();
204
+ };
205
+ });
206
+ } else iframe.setAttribute("is-html-file", false);
149
207
  }
150
208
 
151
209
  function addScriptToIframeHead(iframeDoc, script) {
@@ -227,6 +285,11 @@ function handleOpenModal() {
227
285
  return result;
228
286
  }
229
287
 
288
+ function isHtmlFile() {
289
+ const iframe = document.getElementById("content-iframe");
290
+ return iframe.getAttribute("is-html-file") === "true";
291
+ }
292
+
230
293
  async function handleRoute(route, query, files, data) {
231
294
  const mobileConfig = saltcorn.data.state.getState().mobileConfig;
232
295
  try {
@@ -249,6 +312,7 @@ async function handleRoute(route, query, files, data) {
249
312
  files: files,
250
313
  data: data,
251
314
  alerts: [],
315
+ fullWrap: isHtmlFile(), // fullWrap when it's currently a fixed-html-file
252
316
  });
253
317
  if (page.redirect) {
254
318
  const { moddalWasOpen, noSubmitReload } = handleOpenModal();
@@ -270,7 +334,7 @@ async function handleRoute(route, query, files, data) {
270
334
  }
271
335
  } else if (page.content) {
272
336
  if (!page.replaceIframe) await replaceIframeInnerContent(page.content);
273
- else await replaceIframe(page.content);
337
+ else await replaceIframe(page.content, page.isFile);
274
338
  } else {
275
339
  showAlerts([
276
340
  {
@@ -222,7 +222,7 @@ async function login(e, entryPoint, isSignup) {
222
222
  fullWrap: true,
223
223
  alerts,
224
224
  });
225
- await parent.replaceIframe(page.content);
225
+ if (page.content) await parent.replaceIframe(page.content, page.isFile);
226
226
  } else if (loginResult?.alerts) {
227
227
  parent.showAlerts(loginResult?.alerts);
228
228
  } else {
@@ -263,7 +263,7 @@ async function publicLogin(entryPoint) {
263
263
  },
264
264
  ],
265
265
  });
266
- await parent.replaceIframe(page.content);
266
+ if (page.content) await parent.replaceIframe(page.content, page.isFile);
267
267
  } else if (loginResult?.alerts) {
268
268
  parent.showAlerts(loginResult?.alerts);
269
269
  } else {
@@ -847,7 +847,7 @@ async function deleteOfflineData(noFeedback) {
847
847
  }
848
848
 
849
849
  function showLoadSpinner() {
850
- if ($("#scspinner").length === 0) {
850
+ if (!parent.isHtmlFile() && $("#scspinner").length === 0) {
851
851
  $("body").append(`
852
852
  <div
853
853
  id="scspinner"
@@ -883,7 +883,7 @@ function showLoadSpinner() {
883
883
  }
884
884
 
885
885
  function removeLoadSpinner() {
886
- $("#scspinner").remove();
886
+ if (!parent.isHtmlFile()) $("#scspinner").remove();
887
887
  }
888
888
 
889
889
  /**