Compare commits
No commits in common. "d8583b9ad7d5d33c729357d7bd0b5280d07ab184" and "7220ee5667ef72f3d26897be54342c4735bbc97a" have entirely different histories.
d8583b9ad7
...
7220ee5667
8 changed files with 74 additions and 179 deletions
|
@ -2,13 +2,6 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
|
||||||
|
|
||||||
## [1.11.14](https://github.com/alerte-secours/as-app/compare/v1.11.13...v1.11.14) (2025-07-25)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **io:** headless + debug wip ([a795e82](https://github.com/alerte-secours/as-app/commit/a795e82bbe30a425698173156862311d0c964207))
|
|
||||||
|
|
||||||
## [1.11.13](https://github.com/alerte-secours/as-app/compare/v1.11.12...v1.11.13) (2025-07-24)
|
## [1.11.13](https://github.com/alerte-secours/as-app/compare/v1.11.12...v1.11.13) (2025-07-24)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -83,8 +83,8 @@ android {
|
||||||
applicationId 'com.alertesecours'
|
applicationId 'com.alertesecours'
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 204
|
versionCode 203
|
||||||
versionName "1.11.14"
|
versionName "1.11.13"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
testBuildType System.getProperty('testBuildType', 'debug')
|
testBuildType System.getProperty('testBuildType', 'debug')
|
||||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||||
|
|
59
index.js
59
index.js
|
@ -6,6 +6,7 @@ import "expo-splash-screen";
|
||||||
import BackgroundGeolocation from "react-native-background-geolocation";
|
import BackgroundGeolocation from "react-native-background-geolocation";
|
||||||
|
|
||||||
import { Platform } from "react-native";
|
import { Platform } from "react-native";
|
||||||
|
import BackgroundFetch from "react-native-background-fetch";
|
||||||
|
|
||||||
import notifee from "@notifee/react-native";
|
import notifee from "@notifee/react-native";
|
||||||
import messaging from "@react-native-firebase/messaging";
|
import messaging from "@react-native-firebase/messaging";
|
||||||
|
@ -55,4 +56,62 @@ const HeadlessTask = async (event) => {
|
||||||
|
|
||||||
if (Platform.OS === "android") {
|
if (Platform.OS === "android") {
|
||||||
BackgroundGeolocation.registerHeadlessTask(HeadlessTask);
|
BackgroundGeolocation.registerHeadlessTask(HeadlessTask);
|
||||||
|
} else if (Platform.OS === "ios") {
|
||||||
|
BackgroundGeolocation.onLocation(async () => {
|
||||||
|
await executeHeartbeatSync();
|
||||||
|
});
|
||||||
|
|
||||||
|
BackgroundGeolocation.onMotionChange(async () => {
|
||||||
|
await executeHeartbeatSync();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure BackgroundFetch for iOS (iOS-specific configuration)
|
||||||
|
BackgroundFetch.configure(
|
||||||
|
{
|
||||||
|
minimumFetchInterval: 15, // Only valid option for iOS - gives best chance of execution
|
||||||
|
},
|
||||||
|
// Event callback
|
||||||
|
async (taskId) => {
|
||||||
|
let syncResult = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Execute the shared heartbeat logic and get result
|
||||||
|
syncResult = await executeHeartbeatSync();
|
||||||
|
} catch (error) {
|
||||||
|
// silent error
|
||||||
|
} finally {
|
||||||
|
// CRITICAL: Always call finish with appropriate result
|
||||||
|
try {
|
||||||
|
if (taskId) {
|
||||||
|
let fetchResult;
|
||||||
|
|
||||||
|
if (syncResult?.error || !syncResult?.syncSuccessful) {
|
||||||
|
// Task failed
|
||||||
|
fetchResult = BackgroundFetch.FETCH_RESULT_FAILED;
|
||||||
|
} else if (
|
||||||
|
syncResult?.syncPerformed &&
|
||||||
|
syncResult?.syncSuccessful
|
||||||
|
) {
|
||||||
|
// Force sync was performed successfully - new data
|
||||||
|
fetchResult = BackgroundFetch.FETCH_RESULT_NEW_DATA;
|
||||||
|
} else {
|
||||||
|
// No sync was needed - no new data
|
||||||
|
fetchResult = BackgroundFetch.FETCH_RESULT_NO_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackgroundFetch.finish(taskId, fetchResult);
|
||||||
|
}
|
||||||
|
} catch (finishError) {
|
||||||
|
// silent error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Timeout callback (REQUIRED by BackgroundFetch API)
|
||||||
|
async (taskId) => {
|
||||||
|
// CRITICAL: Must call finish on timeout with FAILED result
|
||||||
|
BackgroundFetch.finish(taskId, BackgroundFetch.FETCH_RESULT_FAILED);
|
||||||
|
},
|
||||||
|
).catch(() => {
|
||||||
|
// silent error
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.11.14</string>
|
<string>1.11.13</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>204</string>
|
<string>203</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>LSApplicationQueriesSchemes</key>
|
<key>LSApplicationQueriesSchemes</key>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "alerte-secours",
|
"name": "alerte-secours",
|
||||||
"version": "1.11.14",
|
"version": "1.11.13",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "expo start --dev-client --private-key-path ./keys/private-key.pem",
|
"start": "expo start --dev-client --private-key-path ./keys/private-key.pem",
|
||||||
|
@ -50,8 +50,8 @@
|
||||||
"screenshot:android": "scripts/screenshot-android.sh"
|
"screenshot:android": "scripts/screenshot-android.sh"
|
||||||
},
|
},
|
||||||
"customExpoVersioning": {
|
"customExpoVersioning": {
|
||||||
"versionCode": 204,
|
"versionCode": 203,
|
||||||
"buildNumber": 204
|
"buildNumber": 203
|
||||||
},
|
},
|
||||||
"commit-and-tag-version": {
|
"commit-and-tag-version": {
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -25,8 +25,6 @@ import { useUpdates } from "~/updates";
|
||||||
import Error from "~/components/Error";
|
import Error from "~/components/Error";
|
||||||
|
|
||||||
import useTrackLocation from "~/hooks/useTrackLocation";
|
import useTrackLocation from "~/hooks/useTrackLocation";
|
||||||
import { initializeBackgroundFetch } from "~/services/backgroundFetch";
|
|
||||||
import useMount from "~/hooks/useMount";
|
|
||||||
|
|
||||||
const appLogger = createLogger({
|
const appLogger = createLogger({
|
||||||
module: SYSTEM_SCOPES.APP,
|
module: SYSTEM_SCOPES.APP,
|
||||||
|
@ -221,23 +219,6 @@ function AppContent() {
|
||||||
useNetworkListener();
|
useNetworkListener();
|
||||||
useTrackLocation();
|
useTrackLocation();
|
||||||
|
|
||||||
useMount(() => {
|
|
||||||
const setupBackgroundFetch = async () => {
|
|
||||||
try {
|
|
||||||
appLogger.info("Setting up BackgroundFetch");
|
|
||||||
await initializeBackgroundFetch();
|
|
||||||
appLogger.debug("BackgroundFetch setup completed");
|
|
||||||
} catch (error) {
|
|
||||||
lifecycleLogger.error("BackgroundFetch setup failed", {
|
|
||||||
error: error?.message,
|
|
||||||
});
|
|
||||||
errorHandler(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
setupBackgroundFetch();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle deep links after app is initialized with error handling
|
// Handle deep links after app is initialized with error handling
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let subscription;
|
let subscription;
|
||||||
|
|
|
@ -4,8 +4,8 @@ import { STORAGE_KEYS } from "~/storage/storageKeys";
|
||||||
import { createLogger } from "~/lib/logger";
|
import { createLogger } from "~/lib/logger";
|
||||||
|
|
||||||
// Constants for persistence
|
// Constants for persistence
|
||||||
// const FORCE_SYNC_INTERVAL = 12 * 60 * 60 * 1000;
|
const FORCE_SYNC_INTERVAL = 12 * 60 * 60 * 1000;
|
||||||
const FORCE_SYNC_INTERVAL = 1 * 60 * 1000; // DEBUGGING
|
// const FORCE_SYNC_INTERVAL = 5 * 60 * 1000; // DEBUGGING
|
||||||
|
|
||||||
const geolocBgLogger = createLogger({
|
const geolocBgLogger = createLogger({
|
||||||
service: "background-task",
|
service: "background-task",
|
||||||
|
@ -73,50 +73,21 @@ export const executeHeartbeatSync = async () => {
|
||||||
const lastSyncTime = await getLastSyncTime();
|
const lastSyncTime = await getLastSyncTime();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const timeSinceLastSync = now - lastSyncTime;
|
const timeSinceLastSync = now - lastSyncTime;
|
||||||
|
|
||||||
if (timeSinceLastSync >= FORCE_SYNC_INTERVAL) {
|
if (timeSinceLastSync >= FORCE_SYNC_INTERVAL) {
|
||||||
geolocBgLogger.info("Forcing location sync", {
|
geolocBgLogger.info("Forcing location sync");
|
||||||
timeSinceLastSync,
|
|
||||||
forceInterval: FORCE_SYNC_INTERVAL,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const syncResult = await Promise.race([
|
await Promise.race([
|
||||||
executeSync(),
|
async () => {
|
||||||
|
await executeSync();
|
||||||
|
},
|
||||||
new Promise((_, reject) =>
|
new Promise((_, reject) =>
|
||||||
setTimeout(() => reject(new Error("changePace timeout")), 10000),
|
setTimeout(() => reject(new Error("changePace timeout")), 10000),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await setLastSyncTime(now);
|
await setLastSyncTime(now);
|
||||||
|
|
||||||
geolocBgLogger.info("Force sync completed successfully", {
|
|
||||||
syncResult,
|
|
||||||
});
|
|
||||||
|
|
||||||
return syncResult;
|
|
||||||
} catch (syncError) {
|
} catch (syncError) {
|
||||||
geolocBgLogger.error("Force sync failed", {
|
geolocBgLogger.error("Force sync failed", { error: syncError });
|
||||||
error: syncError.message,
|
|
||||||
timeSinceLastSync,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
syncPerformed: true,
|
|
||||||
syncSuccessful: false,
|
|
||||||
error: syncError.message,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
geolocBgLogger.debug("Sync not needed yet", {
|
|
||||||
timeSinceLastSync,
|
|
||||||
forceInterval: FORCE_SYNC_INTERVAL,
|
|
||||||
timeUntilNextSync: FORCE_SYNC_INTERVAL - timeSinceLastSync,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
syncPerformed: false,
|
|
||||||
syncSuccessful: true,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,109 +0,0 @@
|
||||||
import { Platform } from "react-native";
|
|
||||||
import BackgroundFetch from "react-native-background-fetch";
|
|
||||||
import { createLogger } from "~/lib/logger";
|
|
||||||
import { executeHeartbeatSync } from "~/location/backgroundTask";
|
|
||||||
|
|
||||||
const backgroundFetchLogger = createLogger({
|
|
||||||
service: "background-fetch",
|
|
||||||
task: "service",
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize BackgroundFetch according to the documentation best practices.
|
|
||||||
* This should be called once when the root component mounts.
|
|
||||||
*/
|
|
||||||
export const initializeBackgroundFetch = async () => {
|
|
||||||
try {
|
|
||||||
backgroundFetchLogger.info("Initializing BackgroundFetch service");
|
|
||||||
|
|
||||||
// Configure BackgroundFetch for both platforms
|
|
||||||
const status = await BackgroundFetch.configure(
|
|
||||||
{
|
|
||||||
minimumFetchInterval: 15, // Only valid option - gives best chance of execution
|
|
||||||
},
|
|
||||||
// Event callback - handles both default fetch events and custom scheduled tasks
|
|
||||||
async (taskId) => {
|
|
||||||
backgroundFetchLogger.info("BackgroundFetch event received", {
|
|
||||||
taskId,
|
|
||||||
});
|
|
||||||
|
|
||||||
let syncResult = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Execute the shared heartbeat logic and get result
|
|
||||||
syncResult = await executeHeartbeatSync();
|
|
||||||
backgroundFetchLogger.debug("Heartbeat sync completed", {
|
|
||||||
syncResult,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
backgroundFetchLogger.error("Heartbeat sync failed", {
|
|
||||||
error: error.message,
|
|
||||||
taskId,
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
// CRITICAL: Always call finish with appropriate result
|
|
||||||
try {
|
|
||||||
if (taskId) {
|
|
||||||
let fetchResult;
|
|
||||||
|
|
||||||
if (syncResult?.error || !syncResult?.syncSuccessful) {
|
|
||||||
// Task failed
|
|
||||||
fetchResult = BackgroundFetch.FETCH_RESULT_FAILED;
|
|
||||||
} else if (
|
|
||||||
syncResult?.syncPerformed &&
|
|
||||||
syncResult?.syncSuccessful
|
|
||||||
) {
|
|
||||||
// Force sync was performed successfully - new data
|
|
||||||
fetchResult = BackgroundFetch.FETCH_RESULT_NEW_DATA;
|
|
||||||
} else {
|
|
||||||
// No sync was needed - no new data
|
|
||||||
fetchResult = BackgroundFetch.FETCH_RESULT_NO_DATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
BackgroundFetch.finish(taskId, fetchResult);
|
|
||||||
backgroundFetchLogger.debug("BackgroundFetch task finished", {
|
|
||||||
taskId,
|
|
||||||
fetchResult,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (finishError) {
|
|
||||||
backgroundFetchLogger.error(
|
|
||||||
"Failed to finish BackgroundFetch task",
|
|
||||||
{
|
|
||||||
error: finishError.message,
|
|
||||||
taskId,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Timeout callback (REQUIRED by BackgroundFetch API)
|
|
||||||
async (taskId) => {
|
|
||||||
backgroundFetchLogger.warn("BackgroundFetch task timeout", { taskId });
|
|
||||||
// CRITICAL: Must call finish on timeout with FAILED result
|
|
||||||
try {
|
|
||||||
BackgroundFetch.finish(taskId, BackgroundFetch.FETCH_RESULT_FAILED);
|
|
||||||
} catch (error) {
|
|
||||||
backgroundFetchLogger.error("Failed to finish timed out task", {
|
|
||||||
error: error.message,
|
|
||||||
taskId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
backgroundFetchLogger.info("BackgroundFetch configured successfully", {
|
|
||||||
status,
|
|
||||||
platform: Platform.OS,
|
|
||||||
});
|
|
||||||
|
|
||||||
return status;
|
|
||||||
} catch (error) {
|
|
||||||
backgroundFetchLogger.error("Failed to initialize BackgroundFetch", {
|
|
||||||
error: error.message,
|
|
||||||
stack: error.stack,
|
|
||||||
platform: Platform.OS,
|
|
||||||
});
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
Add table
Reference in a new issue