Compare commits
2 commits
8183c7e4af
...
144ed88229
Author | SHA1 | Date | |
---|---|---|---|
144ed88229 | |||
6ea01c0c6d |
12 changed files with 121 additions and 56 deletions
|
@ -2,6 +2,13 @@
|
||||||
|
|
||||||
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.16](https://github.com/alerte-secours/as-app/compare/v1.11.15...v1.11.16) (2025-07-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **io:** headless ([6ea01c0](https://github.com/alerte-secours/as-app/commit/6ea01c0c6d7f3cb8dbff51138b20f3fbba5b9766))
|
||||||
|
|
||||||
## [1.11.15](https://github.com/alerte-secours/as-app/compare/v1.11.14...v1.11.15) (2025-07-26)
|
## [1.11.15](https://github.com/alerte-secours/as-app/compare/v1.11.14...v1.11.15) (2025-07-26)
|
||||||
|
|
||||||
## [1.11.14](https://github.com/alerte-secours/as-app/compare/v1.11.13...v1.11.14) (2025-07-25)
|
## [1.11.14](https://github.com/alerte-secours/as-app/compare/v1.11.13...v1.11.14) (2025-07-25)
|
||||||
|
|
|
@ -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 205
|
versionCode 206
|
||||||
versionName "1.11.15"
|
versionName "1.11.16"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
testBuildType System.getProperty('testBuildType', 'debug')
|
testBuildType System.getProperty('testBuildType', 'debug')
|
||||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||||
|
|
|
@ -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.15</string>
|
<string>1.11.16</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>205</string>
|
<string>206</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.15",
|
"version": "1.11.16",
|
||||||
"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": 205,
|
"versionCode": 206,
|
||||||
"buildNumber": 205
|
"buildNumber": 206
|
||||||
},
|
},
|
||||||
"commit-and-tag-version": {
|
"commit-and-tag-version": {
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as Location from "expo-location";
|
import * as Location from "expo-location";
|
||||||
import { useState, useRef, useEffect, useCallback } from "react";
|
import { useState, useRef, useEffect, useCallback } from "react";
|
||||||
import { storeLocation, getStoredLocation } from "~/utils/location/storage";
|
import { storeLocation, getStoredLocation } from "~/location/storage";
|
||||||
import { createLogger } from "~/lib/logger";
|
import { createLogger } from "~/lib/logger";
|
||||||
import { BACKGROUND_SCOPES, UI_SCOPES } from "~/lib/logger/scopes";
|
import { BACKGROUND_SCOPES, UI_SCOPES } from "~/lib/logger/scopes";
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
|
import { Platform } from "react-native";
|
||||||
import BackgroundGeolocation from "react-native-background-geolocation";
|
import BackgroundGeolocation from "react-native-background-geolocation";
|
||||||
import { memoryAsyncStorage } from "~/storage/memoryAsyncStorage";
|
import { memoryAsyncStorage } from "~/storage/memoryAsyncStorage";
|
||||||
import { STORAGE_KEYS } from "~/storage/storageKeys";
|
import { STORAGE_KEYS } from "~/storage/storageKeys";
|
||||||
import { createLogger } from "~/lib/logger";
|
import { createLogger } from "~/lib/logger";
|
||||||
|
import { getStoredLocation } from "./storage";
|
||||||
|
import { getAuthState } from "~/stores";
|
||||||
|
import env from "~/env";
|
||||||
|
|
||||||
// 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 = 1 * 60 * 1000; // DEBUGGING
|
||||||
|
|
||||||
const geolocBgLogger = createLogger({
|
const geolocBgLogger = createLogger({
|
||||||
service: "background-task",
|
service: "background-task",
|
||||||
|
@ -35,38 +39,110 @@ const setLastSyncTime = async (time) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Shared heartbeat logic - mutualized between Android and iOS
|
const executeSyncAndroid = async () => {
|
||||||
const executeSync = async () => {
|
await BackgroundGeolocation.changePace(true);
|
||||||
let syncPerformed = false;
|
await BackgroundGeolocation.sync();
|
||||||
let syncSuccessful = false;
|
};
|
||||||
|
|
||||||
|
const executeSyncIOS = async () => {
|
||||||
try {
|
try {
|
||||||
syncPerformed = true;
|
const locationData = await getStoredLocation();
|
||||||
|
|
||||||
try {
|
if (!locationData) {
|
||||||
// Change pace to ensure location updates
|
geolocBgLogger.debug("No stored location data found, skipping sync");
|
||||||
await BackgroundGeolocation.changePace(true);
|
return;
|
||||||
|
|
||||||
// Perform sync
|
|
||||||
await BackgroundGeolocation.sync();
|
|
||||||
|
|
||||||
syncSuccessful = true;
|
|
||||||
} catch (syncError) {
|
|
||||||
syncSuccessful = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return result information for BackgroundFetch
|
const { timestamp, coords } = locationData;
|
||||||
return {
|
|
||||||
syncPerformed,
|
// Check if timestamp is too old (> 2 weeks)
|
||||||
syncSuccessful,
|
const now = new Date();
|
||||||
|
const locationTime = new Date(timestamp);
|
||||||
|
const twoWeeksInMs = 14 * 24 * 60 * 60 * 1000; // 2 weeks in milliseconds
|
||||||
|
|
||||||
|
if (now - locationTime > twoWeeksInMs) {
|
||||||
|
geolocBgLogger.debug("Stored location is too old, skipping sync", {
|
||||||
|
locationAge: now - locationTime,
|
||||||
|
maxAge: twoWeeksInMs,
|
||||||
|
timestamp: timestamp,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get auth token
|
||||||
|
const { userToken } = getAuthState();
|
||||||
|
|
||||||
|
if (!userToken) {
|
||||||
|
geolocBgLogger.debug("No auth token available, skipping sync");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate coordinates
|
||||||
|
if (
|
||||||
|
!coords ||
|
||||||
|
typeof coords.latitude !== "number" ||
|
||||||
|
typeof coords.longitude !== "number"
|
||||||
|
) {
|
||||||
|
geolocBgLogger.error("Invalid coordinates in stored location", {
|
||||||
|
coords,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare payload according to API spec
|
||||||
|
const payload = {
|
||||||
|
location: {
|
||||||
|
event: "heartbeat",
|
||||||
|
coords: {
|
||||||
|
latitude: coords.latitude,
|
||||||
|
longitude: coords.longitude,
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
geolocBgLogger.debug("Syncing location to server", {
|
||||||
|
url: env.GEOLOC_SYNC_URL,
|
||||||
|
coords: payload.location.coords,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make HTTP request
|
||||||
|
const response = await fetch(env.GEOLOC_SYNC_URL, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${userToken}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseData = await response.json();
|
||||||
|
|
||||||
|
if (responseData.ok !== true) {
|
||||||
|
throw new Error(`API returned ok: ${responseData.ok}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
geolocBgLogger.info("iOS location sync completed successfully", {
|
||||||
|
status: response.status,
|
||||||
|
coords: payload.location.coords,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Return error result for BackgroundFetch
|
geolocBgLogger.error("iOS location sync failed", {
|
||||||
return {
|
|
||||||
syncPerformed,
|
|
||||||
syncSuccessful: false,
|
|
||||||
error: error.message,
|
error: error.message,
|
||||||
};
|
stack: error.stack,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Shared heartbeat logic - mutualized between Android and iOS
|
||||||
|
const executeSync = async () => {
|
||||||
|
if (Platform.OS === "ios") {
|
||||||
|
await executeSyncIOS();
|
||||||
|
} else if (Platform.OS === "android") {
|
||||||
|
await executeSyncAndroid();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const executeHeartbeatSync = async () => {
|
export const executeHeartbeatSync = async () => {
|
||||||
|
@ -84,7 +160,7 @@ export const executeHeartbeatSync = async () => {
|
||||||
const syncResult = await Promise.race([
|
const syncResult = await Promise.race([
|
||||||
executeSync(),
|
executeSync(),
|
||||||
new Promise((_, reject) =>
|
new Promise((_, reject) =>
|
||||||
setTimeout(() => reject(new Error("changePace timeout")), 10000),
|
setTimeout(() => reject(new Error("changePace timeout")), 20000),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { initEmulatorMode } from "./emulatorService";
|
||||||
import { getAuthState, subscribeAuthState, permissionsActions } from "~/stores";
|
import { getAuthState, subscribeAuthState, permissionsActions } from "~/stores";
|
||||||
|
|
||||||
import setLocationState from "~/location/setLocationState";
|
import setLocationState from "~/location/setLocationState";
|
||||||
import { storeLocation } from "~/utils/location/storage";
|
import { storeLocation } from "~/location/storage";
|
||||||
|
|
||||||
import env from "~/env";
|
import env from "~/env";
|
||||||
|
|
||||||
|
|
|
@ -21,15 +21,6 @@ export default async function notifGeolocationHeartbeatSync(data) {
|
||||||
|
|
||||||
heartbeatLogger.info("Triggering geolocation heartbeat sync");
|
heartbeatLogger.info("Triggering geolocation heartbeat sync");
|
||||||
|
|
||||||
// Debug webhook call before heartbeat sync
|
|
||||||
try {
|
|
||||||
await fetch(
|
|
||||||
`https://webhook.site/fc954dfe-8c1e-4efc-a75e-3f9a8917f503?source=notifGeolocationHeartbeatSync`,
|
|
||||||
);
|
|
||||||
} catch (webhookError) {
|
|
||||||
// Silently ignore webhook setup errors
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the heartbeat sync to force location update
|
// Execute the heartbeat sync to force location update
|
||||||
await executeHeartbeatSync();
|
await executeHeartbeatSync();
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
import { deepEqual } from "fast-equals";
|
import { deepEqual } from "fast-equals";
|
||||||
|
|
||||||
import { useAlertState } from "~/stores";
|
import { useAlertState } from "~/stores";
|
||||||
import { storeLocation } from "~/utils/location/storage";
|
import { storeLocation } from "~/location/storage";
|
||||||
import useLocation from "~/hooks/useLocation";
|
import useLocation from "~/hooks/useLocation";
|
||||||
|
|
||||||
import withConnectivity from "~/hoc/withConnectivity";
|
import withConnectivity from "~/hoc/withConnectivity";
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { getDistance } from "geolib";
|
||||||
import { routeToInstructions } from "~/lib/geo/osrmTextInstructions";
|
import { routeToInstructions } from "~/lib/geo/osrmTextInstructions";
|
||||||
import getRouteState from "~/lib/geo/getRouteState";
|
import getRouteState from "~/lib/geo/getRouteState";
|
||||||
import shallowCompare from "~/utils/array/shallowCompare";
|
import shallowCompare from "~/utils/array/shallowCompare";
|
||||||
import { storeLocation } from "~/utils/location/storage";
|
import { storeLocation } from "~/location/storage";
|
||||||
import useLocation from "~/hooks/useLocation";
|
import useLocation from "~/hooks/useLocation";
|
||||||
|
|
||||||
import withConnectivity from "~/hoc/withConnectivity";
|
import withConnectivity from "~/hoc/withConnectivity";
|
||||||
|
|
|
@ -30,15 +30,6 @@ export const initializeBackgroundFetch = async () => {
|
||||||
let syncResult = null;
|
let syncResult = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Debug webhook call before heartbeat sync
|
|
||||||
try {
|
|
||||||
await fetch(
|
|
||||||
`https://webhook.site/fc954dfe-8c1e-4efc-a75e-3f9a8917f503?source=backgroundFetch`,
|
|
||||||
);
|
|
||||||
} catch (webhookError) {
|
|
||||||
// Silently ignore webhook setup errors
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the shared heartbeat logic and get result
|
// Execute the shared heartbeat logic and get result
|
||||||
syncResult = await executeHeartbeatSync();
|
syncResult = await executeHeartbeatSync();
|
||||||
backgroundFetchLogger.debug("Heartbeat sync completed", {
|
backgroundFetchLogger.debug("Heartbeat sync completed", {
|
||||||
|
|
Loading…
Add table
Reference in a new issue