@saltcorn/mobile-app 1.6.0 → 1.7.0-alpha.0
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/optional_sources/background.js +134 -0
- package/optional_sources/ios_silent_push.js +31 -0
- package/optional_sources/notifications.js +49 -13
- package/package.json +1 -1
- package/src/helpers/background_tasks.js +47 -0
- package/src/helpers/common.js +32 -16
- package/add_entitlements.rb +0 -16
- package/optional_sources/background_sync.js +0 -91
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { BackgroundFetch } from "@transistorsoft/capacitor-background-fetch";
|
|
2
|
+
import { performSync, performHeartbeat } from "./background_tasks.js";
|
|
3
|
+
|
|
4
|
+
const SYNC_TASK_ID = "com.transistorsoft.background_sync";
|
|
5
|
+
const HEARTBEAT_TASK_ID = "com.transistorsoft.push_sync_heartbeat";
|
|
6
|
+
|
|
7
|
+
let isConfigured = false;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Call BackgroundFetch.configure() with a task-routing callback.
|
|
11
|
+
* Sets isConfigured on success.
|
|
12
|
+
* @param {number} minimumFetchInterval hint to the OS in minutes
|
|
13
|
+
*/
|
|
14
|
+
async function configure(minimumFetchInterval) {
|
|
15
|
+
const status = await BackgroundFetch.configure(
|
|
16
|
+
{ minimumFetchInterval },
|
|
17
|
+
async (taskId) => {
|
|
18
|
+
switch (taskId) {
|
|
19
|
+
case SYNC_TASK_ID:
|
|
20
|
+
try {
|
|
21
|
+
await performSync();
|
|
22
|
+
} catch (e) {
|
|
23
|
+
console.error("Background sync error:", e);
|
|
24
|
+
}
|
|
25
|
+
break;
|
|
26
|
+
case HEARTBEAT_TASK_ID:
|
|
27
|
+
try {
|
|
28
|
+
await performHeartbeat();
|
|
29
|
+
} catch (e) {
|
|
30
|
+
console.error("Heartbeat error:", e);
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
default:
|
|
34
|
+
console.log("[BackgroundFetch] unknown task:", taskId);
|
|
35
|
+
}
|
|
36
|
+
BackgroundFetch.finish(taskId);
|
|
37
|
+
},
|
|
38
|
+
async (taskId) => {
|
|
39
|
+
console.log("[BackgroundFetch] TIMEOUT:", taskId);
|
|
40
|
+
BackgroundFetch.finish(taskId);
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
if (status === BackgroundFetch.STATUS_AVAILABLE) {
|
|
45
|
+
isConfigured = true;
|
|
46
|
+
console.log("BackgroundFetch configured successfully");
|
|
47
|
+
} else if (status === BackgroundFetch.STATUS_DENIED) {
|
|
48
|
+
console.log("BackgroundFetch: user denied background behavior");
|
|
49
|
+
} else if (status === BackgroundFetch.STATUS_RESTRICTED) {
|
|
50
|
+
console.log("BackgroundFetch: background updates restricted");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Schedule the configured named tasks via BackgroundFetch.scheduleTask.
|
|
56
|
+
* @param {number|undefined} syncInterval background-sync interval in minutes
|
|
57
|
+
* @param {number|undefined} heartbeatInterval push-sync heartbeat interval in minutes
|
|
58
|
+
*/
|
|
59
|
+
async function scheduleTasks(syncInterval, heartbeatInterval) {
|
|
60
|
+
if (syncInterval > 0) {
|
|
61
|
+
try {
|
|
62
|
+
await BackgroundFetch.scheduleTask({
|
|
63
|
+
taskId: SYNC_TASK_ID,
|
|
64
|
+
delay: syncInterval * 60 * 1000,
|
|
65
|
+
periodic: true,
|
|
66
|
+
requiresNetworkConnectivity: true,
|
|
67
|
+
});
|
|
68
|
+
console.log(
|
|
69
|
+
`Background sync scheduled, interval: ${syncInterval} minute(s)`
|
|
70
|
+
);
|
|
71
|
+
} catch (e) {
|
|
72
|
+
console.error("Error scheduling sync task:", e);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (heartbeatInterval > 0) {
|
|
77
|
+
try {
|
|
78
|
+
await BackgroundFetch.scheduleTask({
|
|
79
|
+
taskId: HEARTBEAT_TASK_ID,
|
|
80
|
+
delay: heartbeatInterval * 60 * 1000,
|
|
81
|
+
periodic: true,
|
|
82
|
+
requiresNetworkConnectivity: true,
|
|
83
|
+
});
|
|
84
|
+
console.log(
|
|
85
|
+
`Push sync heartbeat scheduled, interval: ${heartbeatInterval} minute(s)`
|
|
86
|
+
);
|
|
87
|
+
} catch (e) {
|
|
88
|
+
console.error("Error scheduling heartbeat task:", e);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Init background tasks. Routes task IDs to performSync / performHeartbeat
|
|
95
|
+
* from background_tasks.js. Only tasks with a configured interval are scheduled.
|
|
96
|
+
* @param {number|undefined} syncInterval background-sync interval in minutes
|
|
97
|
+
* @param {number|undefined} heartbeatInterval push-sync heartbeat interval in minutes
|
|
98
|
+
* @returns {Promise<boolean>} true if BackgroundFetch was configured successfully
|
|
99
|
+
*/
|
|
100
|
+
export async function initBackground(syncInterval, heartbeatInterval) {
|
|
101
|
+
const hasSyncInterval = syncInterval && syncInterval > 0;
|
|
102
|
+
const hasHeartbeatInterval = heartbeatInterval && heartbeatInterval > 0;
|
|
103
|
+
|
|
104
|
+
if (!hasSyncInterval && !hasHeartbeatInterval) {
|
|
105
|
+
console.log("initBackground: no intervals configured, skipping");
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!isConfigured) {
|
|
110
|
+
const minimumFetchInterval = Math.min(
|
|
111
|
+
hasSyncInterval ? syncInterval : Infinity,
|
|
112
|
+
hasHeartbeatInterval ? heartbeatInterval : Infinity
|
|
113
|
+
);
|
|
114
|
+
await configure(minimumFetchInterval);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (isConfigured) await scheduleTasks(syncInterval, heartbeatInterval);
|
|
118
|
+
|
|
119
|
+
return isConfigured;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Stop all background tasks (sync + heartbeat). Call on logout.
|
|
124
|
+
*/
|
|
125
|
+
export async function stopBackground() {
|
|
126
|
+
if (!isConfigured) return;
|
|
127
|
+
try {
|
|
128
|
+
await BackgroundFetch.stop();
|
|
129
|
+
isConfigured = false;
|
|
130
|
+
console.log("Background tasks stopped");
|
|
131
|
+
} catch (e) {
|
|
132
|
+
console.error("Error stopping background tasks:", e);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*global saltcorn*/
|
|
2
|
+
|
|
3
|
+
import { CapacitorSilentNotifications } from "capacitor-plugin-silent-notifications";
|
|
4
|
+
|
|
5
|
+
let silentNotificationListener = null;
|
|
6
|
+
|
|
7
|
+
export async function initIosSilentPushListener() {
|
|
8
|
+
if (silentNotificationListener) {
|
|
9
|
+
await silentNotificationListener.remove();
|
|
10
|
+
silentNotificationListener = null;
|
|
11
|
+
}
|
|
12
|
+
silentNotificationListener = await CapacitorSilentNotifications.addListener(
|
|
13
|
+
"silentNotificationReceived",
|
|
14
|
+
async (notification) => {
|
|
15
|
+
console.log("iOS silent push received:", notification);
|
|
16
|
+
const state = saltcorn.data.state.getState();
|
|
17
|
+
if (state.mobile_push_handler?.["push_sync"]) {
|
|
18
|
+
try {
|
|
19
|
+
await state.mobile_push_handler["push_sync"](notification);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error("Error handling push_sync silent notification:", error);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function removeIosSilentPushListener() {
|
|
29
|
+
await silentNotificationListener?.remove();
|
|
30
|
+
silentNotificationListener = null;
|
|
31
|
+
}
|
|
@@ -11,28 +11,40 @@ import { Device } from "@capacitor/device";
|
|
|
11
11
|
*/
|
|
12
12
|
async function notifyTokenApi(config, isSubscribe) {
|
|
13
13
|
console.log("notifyTokenApi subscribe:", isSubscribe);
|
|
14
|
+
if (!config.pushConfiguration) {
|
|
15
|
+
console.warn("notifyTokenApi: pushConfiguration is not set, skipping");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
14
18
|
const { token, deviceId } = config.pushConfiguration;
|
|
15
19
|
const platform = Capacitor.getPlatform();
|
|
16
20
|
try {
|
|
17
21
|
const response = await apiCall({
|
|
18
22
|
method: "POST",
|
|
19
|
-
path: `/notifications/mobile-${
|
|
23
|
+
path: `/notifications/mobile-${
|
|
24
|
+
isSubscribe ? "subscribe" : "remove-subscription"
|
|
25
|
+
}`,
|
|
20
26
|
body: { token, deviceId, platform },
|
|
21
27
|
});
|
|
22
28
|
const data = response.data;
|
|
23
29
|
if (data.success === "ok")
|
|
24
30
|
console.log(
|
|
25
|
-
`successfully ${
|
|
31
|
+
`successfully ${
|
|
32
|
+
isSubscribe ? "subscribed" : "unsubscribed"
|
|
33
|
+
} to push notifications`,
|
|
26
34
|
data
|
|
27
35
|
);
|
|
28
36
|
else
|
|
29
37
|
console.error(
|
|
30
|
-
`unable to ${
|
|
38
|
+
`unable to ${
|
|
39
|
+
isSubscribe ? "subscribe" : "unsubscribe"
|
|
40
|
+
} to push notifications`,
|
|
31
41
|
data
|
|
32
42
|
);
|
|
33
43
|
} catch (error) {
|
|
34
44
|
console.error(
|
|
35
|
-
`unable to ${
|
|
45
|
+
`unable to ${
|
|
46
|
+
isSubscribe ? "subscribe" : "unsubscribe"
|
|
47
|
+
} to push notifications`,
|
|
36
48
|
error
|
|
37
49
|
);
|
|
38
50
|
}
|
|
@@ -45,18 +57,30 @@ async function notifyTokenApi(config, isSubscribe) {
|
|
|
45
57
|
*/
|
|
46
58
|
async function syncTokenApi(config, isSubscribe) {
|
|
47
59
|
console.log("syncTokenApi subscribe:", isSubscribe);
|
|
60
|
+
if (!config.pushConfiguration) {
|
|
61
|
+
console.warn("syncTokenApi: pushConfiguration is not set, skipping");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
48
64
|
const { token, deviceId } = config.pushConfiguration;
|
|
49
65
|
const platform = Capacitor.getPlatform();
|
|
50
66
|
try {
|
|
51
67
|
const response = await apiCall({
|
|
52
68
|
method: "POST",
|
|
53
69
|
path: `/sync/push_${isSubscribe ? "subscribe" : "unsubscribe"}`,
|
|
54
|
-
body: {
|
|
70
|
+
body: {
|
|
71
|
+
token,
|
|
72
|
+
deviceId,
|
|
73
|
+
synchedTables: config.synchedTables,
|
|
74
|
+
platform,
|
|
75
|
+
apnsEnvironment: config.apnsEnvironment,
|
|
76
|
+
},
|
|
55
77
|
});
|
|
56
78
|
const data = response.data;
|
|
57
79
|
if (data.success === "ok")
|
|
58
80
|
console.log(
|
|
59
|
-
`successfully ${
|
|
81
|
+
`successfully ${
|
|
82
|
+
isSubscribe ? "subscribed" : "unsubscribed"
|
|
83
|
+
} to push sync`,
|
|
60
84
|
data
|
|
61
85
|
);
|
|
62
86
|
else
|
|
@@ -94,7 +118,6 @@ export async function initPushNotifications() {
|
|
|
94
118
|
await removePushListeners();
|
|
95
119
|
const permStatus = await PushNotifications.requestPermissions();
|
|
96
120
|
if (permStatus.receive === "granted") {
|
|
97
|
-
await PushNotifications.register();
|
|
98
121
|
registrationListener = PushNotifications.addListener(
|
|
99
122
|
"registration",
|
|
100
123
|
async (token) => {
|
|
@@ -109,8 +132,7 @@ export async function initPushNotifications() {
|
|
|
109
132
|
deviceId: identifier,
|
|
110
133
|
};
|
|
111
134
|
await notifyTokenApi(config, true);
|
|
112
|
-
if (config.
|
|
113
|
-
await syncTokenApi(config, true);
|
|
135
|
+
if (config.pushSync) await syncTokenApi(config, true);
|
|
114
136
|
}
|
|
115
137
|
}
|
|
116
138
|
);
|
|
@@ -126,6 +148,8 @@ export async function initPushNotifications() {
|
|
|
126
148
|
"pushNotificationReceived",
|
|
127
149
|
messageHandler
|
|
128
150
|
);
|
|
151
|
+
|
|
152
|
+
await PushNotifications.register();
|
|
129
153
|
} else {
|
|
130
154
|
console.warn("Push notification permission not granted");
|
|
131
155
|
}
|
|
@@ -134,17 +158,29 @@ export async function initPushNotifications() {
|
|
|
134
158
|
|
|
135
159
|
export async function unregisterPushNotifications() {
|
|
136
160
|
if (Capacitor.getPlatform() !== "web" && PushNotifications) {
|
|
161
|
+
const config = saltcorn.data.state.getState().mobileConfig;
|
|
137
162
|
try {
|
|
138
163
|
await PushNotifications.unregister();
|
|
139
|
-
const config = saltcorn.data.state.getState().mobileConfig;
|
|
140
164
|
await notifyTokenApi(config, false);
|
|
141
|
-
if (config.
|
|
165
|
+
if (config.pushSync) {
|
|
142
166
|
await syncTokenApi(config, false);
|
|
143
|
-
|
|
144
|
-
|
|
167
|
+
if (Capacitor.getPlatform() === "ios") {
|
|
168
|
+
try {
|
|
169
|
+
const { removeIosSilentPushListener } = await import(
|
|
170
|
+
"../helpers/ios_silent_push.js"
|
|
171
|
+
);
|
|
172
|
+
await removeIosSilentPushListener();
|
|
173
|
+
} catch (e) {
|
|
174
|
+
console.log("iOS silent push not available:", e);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
145
178
|
console.log("Push notifications unregistered successfully");
|
|
146
179
|
} catch (error) {
|
|
147
180
|
console.error("Error unregistering push notifications:", error);
|
|
181
|
+
} finally {
|
|
182
|
+
await removePushListeners();
|
|
183
|
+
config.pushConfiguration = null;
|
|
148
184
|
}
|
|
149
185
|
}
|
|
150
186
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/*global saltcorn*/
|
|
2
|
+
|
|
3
|
+
import { Capacitor } from "@capacitor/core";
|
|
4
|
+
import { apiCall } from "./api.js";
|
|
5
|
+
import { sync } from "./offline_mode.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Perform one background sync pass.
|
|
9
|
+
*/
|
|
10
|
+
export async function performSync() {
|
|
11
|
+
console.log("Background sync starting:", new Date().toISOString());
|
|
12
|
+
await sync(true);
|
|
13
|
+
console.log("Background sync finished:", new Date().toISOString());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Re-register the push sync subscription with the server.
|
|
18
|
+
*/
|
|
19
|
+
export async function performHeartbeat() {
|
|
20
|
+
const config = saltcorn.data.state.getState().mobileConfig;
|
|
21
|
+
if (!config?.pushSync || !config?.pushConfiguration) {
|
|
22
|
+
console.log("Push sync heartbeat: skipping (push sync not active)");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const { token, deviceId } = config.pushConfiguration;
|
|
26
|
+
const platform = Capacitor.getPlatform();
|
|
27
|
+
console.log("Push sync heartbeat: re-registering subscription");
|
|
28
|
+
try {
|
|
29
|
+
const response = await apiCall({
|
|
30
|
+
method: "POST",
|
|
31
|
+
path: "/sync/push_subscribe",
|
|
32
|
+
body: {
|
|
33
|
+
token,
|
|
34
|
+
deviceId,
|
|
35
|
+
platform,
|
|
36
|
+
synchedTables: config.synchedTables,
|
|
37
|
+
apnsEnvironment: config.apnsEnvironment,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
const data = response.data;
|
|
41
|
+
if (data.success === "ok")
|
|
42
|
+
console.log("Push sync heartbeat: subscription refreshed");
|
|
43
|
+
else console.error("Push sync heartbeat: server error", data);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
console.error("Push sync heartbeat error:", e);
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/helpers/common.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/*global saltcorn, $*/
|
|
2
2
|
|
|
3
3
|
import { apiCall } from "./api";
|
|
4
|
+
import { Capacitor } from "@capacitor/core";
|
|
4
5
|
import { Camera, CameraResultType } from "@capacitor/camera";
|
|
5
6
|
import { ScreenOrientation } from "@capacitor/screen-orientation";
|
|
6
7
|
import { SendIntent } from "send-intent";
|
|
@@ -224,7 +225,19 @@ export async function tryInitPush(config) {
|
|
|
224
225
|
try {
|
|
225
226
|
await initPushNotifications();
|
|
226
227
|
if (saltcorn.data.utils.isPushEnabled(config.user)) addPusNotifyHandler();
|
|
227
|
-
if (config.pushSync)
|
|
228
|
+
if (config.pushSync) {
|
|
229
|
+
addPushSyncHandler();
|
|
230
|
+
if (Capacitor.getPlatform() === "ios") {
|
|
231
|
+
try {
|
|
232
|
+
const { initIosSilentPushListener } = await import(
|
|
233
|
+
"../helpers/ios_silent_push.js"
|
|
234
|
+
);
|
|
235
|
+
await initIosSilentPushListener();
|
|
236
|
+
} catch (e) {
|
|
237
|
+
console.log("iOS silent push not available:", e);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
228
241
|
} catch (error) {
|
|
229
242
|
console.error("Error initializing push notifications:", error);
|
|
230
243
|
}
|
|
@@ -234,21 +247,26 @@ export async function tryInitPush(config) {
|
|
|
234
247
|
}
|
|
235
248
|
|
|
236
249
|
/**
|
|
237
|
-
* init background sync, if available
|
|
250
|
+
* init background tasks (sync and/or push-sync heartbeat), if available
|
|
238
251
|
*/
|
|
239
252
|
export async function tryInitBackgroundSync(config) {
|
|
253
|
+
if (
|
|
254
|
+
!(config.syncInterval && config.syncInterval > 0) &&
|
|
255
|
+
!(config.pushSyncHeartbeatInterval && config.pushSyncHeartbeatInterval > 0)
|
|
256
|
+
)
|
|
257
|
+
return;
|
|
240
258
|
try {
|
|
241
|
-
const {
|
|
242
|
-
"../helpers/background_sync.js"
|
|
243
|
-
);
|
|
259
|
+
const { initBackground } = await import("../helpers/background.js");
|
|
244
260
|
try {
|
|
245
|
-
|
|
246
|
-
|
|
261
|
+
await initBackground(
|
|
262
|
+
config.syncInterval,
|
|
263
|
+
config.pushSyncHeartbeatInterval
|
|
264
|
+
);
|
|
247
265
|
} catch (error) {
|
|
248
|
-
console.error("Error initializing background
|
|
266
|
+
console.error("Error initializing background tasks:", error);
|
|
249
267
|
}
|
|
250
268
|
} catch (error) {
|
|
251
|
-
console.log("Background
|
|
269
|
+
console.log("Background module not available:", error);
|
|
252
270
|
}
|
|
253
271
|
}
|
|
254
272
|
|
|
@@ -271,19 +289,17 @@ export async function tryUnregisterPush() {
|
|
|
271
289
|
}
|
|
272
290
|
|
|
273
291
|
/**
|
|
274
|
-
* stop background sync, if available
|
|
292
|
+
* stop background tasks (sync and/or push-sync heartbeat), if available
|
|
275
293
|
*/
|
|
276
294
|
export async function tryStopBackgroundSync() {
|
|
277
295
|
try {
|
|
278
|
-
const {
|
|
279
|
-
"../helpers/background_sync.js"
|
|
280
|
-
);
|
|
296
|
+
const { stopBackground } = await import("../helpers/background.js");
|
|
281
297
|
try {
|
|
282
|
-
await
|
|
298
|
+
await stopBackground();
|
|
283
299
|
} catch (error) {
|
|
284
|
-
console.error("Error stopping
|
|
300
|
+
console.error("Error stopping background tasks:", error);
|
|
285
301
|
}
|
|
286
302
|
} catch (error) {
|
|
287
|
-
console.
|
|
303
|
+
console.log("Background module not available:", error);
|
|
288
304
|
}
|
|
289
305
|
}
|
package/add_entitlements.rb
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
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}'"
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { BackgroundFetch } from "@transistorsoft/capacitor-background-fetch";
|
|
2
|
-
import { sync } from "./offline_mode.js";
|
|
3
|
-
|
|
4
|
-
let isConfigured = false;
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Init the periodic background sync with a min interval.
|
|
8
|
-
* This runs the sync even if the app is in background or was swipe closed.
|
|
9
|
-
* If no internet connection is available it fails silently.
|
|
10
|
-
* @param {number} interval min time interval in minutes. The system decides when to actually do it
|
|
11
|
-
* @returns {Promise<boolean>} True if configuration was successful, false otherwise.
|
|
12
|
-
*/
|
|
13
|
-
export async function startPeriodicBackgroundSync(interval = 15) {
|
|
14
|
-
if (isConfigured) {
|
|
15
|
-
console.log("Background sync is already configured. Skipping.");
|
|
16
|
-
return true;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
console.log("Configuring background sync with interval (minutes):", interval);
|
|
20
|
-
|
|
21
|
-
const status = await BackgroundFetch.configure(
|
|
22
|
-
{
|
|
23
|
-
minimumFetchInterval: interval,
|
|
24
|
-
},
|
|
25
|
-
async (taskId) => {
|
|
26
|
-
console.log(
|
|
27
|
-
"Starting background sync:",
|
|
28
|
-
taskId,
|
|
29
|
-
new Date().toISOString()
|
|
30
|
-
);
|
|
31
|
-
await sync(true);
|
|
32
|
-
console.log(
|
|
33
|
-
"Background sync finished:",
|
|
34
|
-
taskId,
|
|
35
|
-
new Date().toISOString()
|
|
36
|
-
);
|
|
37
|
-
BackgroundFetch.finish(taskId);
|
|
38
|
-
},
|
|
39
|
-
async (taskId) => {
|
|
40
|
-
console.log("[BackgroundFetch] TIMEOUT:", taskId);
|
|
41
|
-
BackgroundFetch.finish(taskId);
|
|
42
|
-
}
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
if (status === BackgroundFetch.STATUS_AVAILABLE) {
|
|
46
|
-
console.log("Background sync successfully configured.");
|
|
47
|
-
isConfigured = true; // Set the flag only on success
|
|
48
|
-
return true;
|
|
49
|
-
} else {
|
|
50
|
-
// Handle error statuses
|
|
51
|
-
if (status === BackgroundFetch.STATUS_DENIED) {
|
|
52
|
-
console.log(
|
|
53
|
-
"The user explicitly disabled background behavior for this app or for the whole system."
|
|
54
|
-
);
|
|
55
|
-
} else if (status === BackgroundFetch.STATUS_RESTRICTED) {
|
|
56
|
-
console.log(
|
|
57
|
-
"Background updates are unavailable and the user cannot enable them again."
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
isConfigured = false;
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Stops/Unregisters the periodic background sync.
|
|
67
|
-
* This should be called when the user logs out.
|
|
68
|
-
*/
|
|
69
|
-
export async function stopPeriodicBackgroundSync() {
|
|
70
|
-
if (!isConfigured) {
|
|
71
|
-
console.log("Background sync is not currently configured. Skipping stop.");
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
console.log("Stopping background sync");
|
|
76
|
-
try {
|
|
77
|
-
await BackgroundFetch.stop();
|
|
78
|
-
console.log("Background sync successfully stopped.");
|
|
79
|
-
isConfigured = false;
|
|
80
|
-
} catch (error) {
|
|
81
|
-
console.error("Error stopping background sync:", error);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Check if the background sync is currently configured/enabled.
|
|
87
|
-
* @returns {boolean} True if the sync is configured, false otherwise.
|
|
88
|
-
*/
|
|
89
|
-
export function isBackgroundSyncActive() {
|
|
90
|
-
return isConfigured;
|
|
91
|
-
}
|