@saltcorn/mobile-app 1.6.0-alpha.0 → 1.6.0-alpha.10

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.
@@ -0,0 +1,16 @@
1
+ require 'xcodeproj'
2
+
3
+ project_path = 'ios/App/App.xcodeproj'
4
+ target_name = 'App'
5
+ entitlements_relative_path = 'App/App.entitlements'
6
+
7
+ project = Xcodeproj::Project.open(project_path)
8
+ target = project.targets.find { |t| t.name == target_name }
9
+ abort("Target '#{target_name}' not found") unless target
10
+
11
+ target.build_configurations.each do |config|
12
+ config.build_settings['CODE_SIGN_ENTITLEMENTS'] = entitlements_relative_path
13
+ end
14
+
15
+ project.save
16
+ puts "Entitlements configured successfully for target '#{target_name}'"
@@ -12,11 +12,12 @@ import { Device } from "@capacitor/device";
12
12
  async function notifyTokenApi(config, isSubscribe) {
13
13
  console.log("notifyTokenApi subscribe:", isSubscribe);
14
14
  const { token, deviceId } = config.pushConfiguration;
15
+ const platform = Capacitor.getPlatform();
15
16
  try {
16
17
  const response = await apiCall({
17
18
  method: "POST",
18
19
  path: `/notifications/mobile-${isSubscribe ? "subscribe" : "remove-subscription"}`,
19
- body: { token, deviceId },
20
+ body: { token, deviceId, platform },
20
21
  });
21
22
  const data = response.data;
22
23
  if (data.success === "ok")
@@ -45,11 +46,12 @@ async function notifyTokenApi(config, isSubscribe) {
45
46
  async function syncTokenApi(config, isSubscribe) {
46
47
  console.log("syncTokenApi subscribe:", isSubscribe);
47
48
  const { token, deviceId } = config.pushConfiguration;
49
+ const platform = Capacitor.getPlatform();
48
50
  try {
49
51
  const response = await apiCall({
50
52
  method: "POST",
51
53
  path: `/sync/push_${isSubscribe ? "subscribe" : "unsubscribe"}`,
52
- body: { token, deviceId, synchedTables: config.synchedTables },
54
+ body: { token, deviceId, synchedTables: config.synchedTables, platform },
53
55
  });
54
56
  const data = response.data;
55
57
  if (data.success === "ok")
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.6.0-alpha.0",
4
+ "version": "1.6.0-alpha.10",
5
5
  "description": "Saltcorn mobile app for Android and iOS",
6
6
  "main": "index.js",
7
7
  "scripts": {
@@ -22,7 +22,11 @@
22
22
  "i18next-sprintf-postprocessor": "0.2.2",
23
23
  "crypto-browserify": "3.12.1",
24
24
  "vm-browserify": "1.1.2",
25
- "stream-browserify": "3.0.0"
25
+ "string_decoder": "1.3.0",
26
+ "buffer": "6.0.3",
27
+ "events": "3.3.0",
28
+ "stream-browserify": "3.0.0",
29
+ "jeep-sqlite": "^2.8.0"
26
30
  },
27
31
  "devDependencies": {
28
32
  "xml2js": "0.6.2",
@@ -30,6 +34,10 @@
30
34
  "@babel/preset-env": "^7.26.7",
31
35
  "babel-loader": "^9.2.1",
32
36
  "webpack": "5.97.1",
33
- "webpack-cli": "6.0.1"
37
+ "webpack-cli": "6.0.1",
38
+ "typescript": "5.9.2",
39
+ "kleur": "4.1.5",
40
+ "@ionic/cli-framework-output": "2.2.8",
41
+ "@ionic/utils-terminal": "2.3.5"
34
42
  }
35
43
  }
@@ -1,4 +1,4 @@
1
- /*global saltcorn*/
1
+ /*global saltcorn, Capacitor*/
2
2
  import i18next from "i18next";
3
3
 
4
4
  import { router } from "../routing/index";
@@ -80,7 +80,11 @@ export async function goBack(steps = 1, exitOnFirstPage = false) {
80
80
  routingHistory.length === 0 ||
81
81
  (exitOnFirstPage && routingHistory.length === 1)
82
82
  ) {
83
- navigator.app.exitApp();
83
+ if (Capacitor.getPlatform() === "android") {
84
+ navigator.app.exitApp();
85
+ } else if (Capacitor.getPlatform() === "ios") {
86
+ console.log("iOS app exit not supported");
87
+ }
84
88
  } else if (routingHistory.length <= steps) {
85
89
  try {
86
90
  if (iframe?.contentWindow?.showLoadSpinner)
@@ -262,9 +266,27 @@ export async function gotoEntryView() {
262
266
  }
263
267
  }
264
268
 
269
+ const iosSwipeBackHandler = () => {
270
+ const iframe = document.getElementById("content-iframe");
271
+ let touchStartX = 0;
272
+ const doc = iframe.contentDocument || iframe.contentWindow.document;
273
+ doc.addEventListener("pointerdown", (e) => {
274
+ touchStartX = e.clientX;
275
+ });
276
+
277
+ doc.addEventListener("pointerup", (e) => {
278
+ const deltaX = e.clientX - touchStartX;
279
+ if (touchStartX < 20 && deltaX > 100) {
280
+ saltcorn.mobileApp.navigation.goBack(1, true);
281
+ }
282
+ });
283
+ };
284
+
265
285
  export async function replaceIframe(content, isFile = false) {
266
286
  const iframe = document.getElementById("content-iframe");
267
287
  iframe.srcdoc = content;
288
+ if (Capacitor.getPlatform() === "ios")
289
+ iframe.addEventListener("load", iosSwipeBackHandler);
268
290
  if (isFile) {
269
291
  iframe.setAttribute("is-html-file", true);
270
292
  await new Promise((resolve, reject) => {
package/src/init.js CHANGED
@@ -211,14 +211,10 @@ const showErrorPage = async (error) => {
211
211
  const onResume = async () => {
212
212
  if (typeof saltcorn === "undefined") return;
213
213
  const mobileConfig = saltcorn.data.state.getState().mobileConfig;
214
- if (mobileConfig?.allowOfflineMode) {
214
+ if (mobileConfig?.allowOfflineMode && mobileConfig.jwt) {
215
215
  const netStatus = await Network.getStatus();
216
216
  mobileConfig.networkState = netStatus.connectionType;
217
- if (
218
- mobileConfig.networkState === "none" &&
219
- !mobileConfig.isOfflineMode &&
220
- mobileConfig.jwt
221
- ) {
217
+ if (mobileConfig.networkState === "none" && !mobileConfig.isOfflineMode) {
222
218
  try {
223
219
  await startOfflineMode();
224
220
  clearHistory();
@@ -226,6 +222,15 @@ const onResume = async () => {
226
222
  } catch (error) {
227
223
  await showErrorPage(error);
228
224
  }
225
+ } else if (
226
+ mobileConfig.networkState !== "none" &&
227
+ mobileConfig.syncOnAppResume
228
+ ) {
229
+ try {
230
+ await sync(false, false, []);
231
+ } catch (error) {
232
+ await showErrorPage(error);
233
+ }
229
234
  }
230
235
  }
231
236
  };
@@ -362,17 +367,19 @@ const getEntryPoint = (roleId, state, mobileConfig) => {
362
367
  // device is ready
363
368
  export async function init(mobileConfig) {
364
369
  try {
365
- if (Capacitor.getPlatform() === "web") {
370
+ const platform = Capacitor.getPlatform();
371
+ if (platform === "web") {
366
372
  defineCustomElements(window);
367
373
  await customElements.whenDefined("jeep-sqlite");
368
374
  const jeepSqlite = document.createElement("jeep-sqlite");
369
375
  document.body.appendChild(jeepSqlite);
370
376
  await jeepSqlite.componentOnReady();
377
+ } else if (platform === "android") {
378
+ App.addListener("backButton", async ({ canGoBack }) => {
379
+ await saltcorn.mobileApp.navigation.goBack(1, true);
380
+ });
371
381
  }
372
-
373
- App.addListener("backButton", async ({ canGoBack }) => {
374
- await saltcorn.mobileApp.navigation.goBack(1, true);
375
- });
382
+ // see navigation.js for ios
376
383
 
377
384
  App.addListener("appUrlOpen", async (event) => {
378
385
  try {
@@ -9,6 +9,7 @@ export const getHeaders = () => {
9
9
  const versionTag = config.version_tag;
10
10
  const stdHeaders = [
11
11
  { css: `static_assets/${versionTag}/saltcorn.css` },
12
+ { css: `static_assets/${versionTag}/saltcorn-mobile.css` },
12
13
  { script: `static_assets/${versionTag}/saltcorn-common.js` },
13
14
  { script: `static_assets/${versionTag}/dayjs.min.js` },
14
15
  { script: `static_assets/${versionTag}/socket.io.min.js` },
package/webpack.config.js CHANGED
@@ -42,7 +42,9 @@ module.exports = {
42
42
  "crypto": require.resolve("crypto-browserify"),
43
43
  vm: require.resolve("vm-browserify"),
44
44
  stream: require.resolve("stream-browserify"),
45
-
45
+ string_decoder: require.resolve("string_decoder/"),
46
+ buffer: require.resolve("buffer/"),
47
+ events: require.resolve("events/"),
46
48
  }
47
49
  },
48
50
  target: "web", // Use web target for browser compatibility
package/www/index.html CHANGED
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta
5
5
  name="viewport"
6
- content="width=device-width, initial-scale=1, maximum-scale=1"
6
+ content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover"
7
7
  />
8
8
  <script src="data/config.js"></script>
9
9
  <!-- only set in test mode -->