Compare commits
No commits in common. "cccb49134f55faf5f5d536e91509eba2743f2af8" and "492904cbe8a5ceec35a79610ea59cf5eaed42735" have entirely different histories.
cccb49134f
...
492904cbe8
7 changed files with 88 additions and 143 deletions
|
|
@ -1,75 +1,20 @@
|
|||
import { Platform, Linking, PermissionsAndroid } from "react-native";
|
||||
import { Platform, Linking } from "react-native";
|
||||
import RNImmediatePhoneCall from "react-native-immediate-phone-call";
|
||||
|
||||
export async function phoneCallEmergency() {
|
||||
export function phoneCallEmergency() {
|
||||
const emergencyNumber = "112";
|
||||
|
||||
if (Platform.OS === "ios") {
|
||||
try {
|
||||
// Prefer telprompt on iOS for immediate prompt, fallback to tel
|
||||
await Linking.openURL(`telprompt:${emergencyNumber}`);
|
||||
} catch (err) {
|
||||
console.error("Error opening phone dialer (iOS telprompt):", err);
|
||||
try {
|
||||
await Linking.openURL(`tel:${emergencyNumber}`);
|
||||
} catch (err2) {
|
||||
console.error("Error opening phone dialer (iOS tel fallback):", err2);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Android: request CALL_PHONE upfront and provide deterministic fallback
|
||||
try {
|
||||
const granted = await PermissionsAndroid.request(
|
||||
PermissionsAndroid.PERMISSIONS.CALL_PHONE,
|
||||
);
|
||||
|
||||
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
|
||||
// Try immediate call, but arm a short fallback timer in case the OS/OEM ignores ACTION_CALL
|
||||
let fallbackTriggered = false;
|
||||
|
||||
const triggerFallback = async () => {
|
||||
if (fallbackTriggered) return;
|
||||
fallbackTriggered = true;
|
||||
try {
|
||||
await Linking.openURL(`tel:${emergencyNumber}`);
|
||||
} catch (e) {
|
||||
console.error("Fallback to dialer failed:", e);
|
||||
}
|
||||
};
|
||||
|
||||
// Fire fallback after ~1.2s if nothing happens
|
||||
const timer = setTimeout(triggerFallback, 1200);
|
||||
|
||||
try {
|
||||
RNImmediatePhoneCall.immediatePhoneCall(emergencyNumber);
|
||||
} catch (callErr) {
|
||||
// If native throws synchronously, cancel timer and fallback immediately
|
||||
clearTimeout(timer);
|
||||
await triggerFallback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Give a little extra time; if no fallback was needed, clear the timer
|
||||
setTimeout(() => {
|
||||
if (!fallbackTriggered) clearTimeout(timer);
|
||||
}, 3000);
|
||||
} else {
|
||||
// Permission denied or never-ask-again: open dialer prompt
|
||||
try {
|
||||
await Linking.openURL(`tel:${emergencyNumber}`);
|
||||
} catch (err) {
|
||||
console.error("Permission denied; dialer fallback failed:", err);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("CALL_PHONE permission request failed:", err);
|
||||
// Last resort: open dialer
|
||||
try {
|
||||
await Linking.openURL(`tel:${emergencyNumber}`);
|
||||
} catch (err2) {
|
||||
console.error("Dialer fallback failed after permission error:", err2);
|
||||
}
|
||||
// Use telprompt URL scheme on iOS
|
||||
Linking.openURL(`telprompt:${emergencyNumber}`).catch((err) => {
|
||||
console.error("Error opening phone dialer:", err);
|
||||
// Fallback to regular tel: if telprompt fails
|
||||
Linking.openURL(`tel:${emergencyNumber}`).catch((err) => {
|
||||
console.error("Error opening phone dialer (fallback):", err);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Use RNImmediatePhoneCall on Android
|
||||
RNImmediatePhoneCall.immediatePhoneCall(emergencyNumber);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ export default function AccountManagement({
|
|||
profileData,
|
||||
openAccountModal,
|
||||
waitingSmsType,
|
||||
clearAuthWaitParams,
|
||||
}) {
|
||||
const { colors, custom } = useTheme();
|
||||
const isConnected = isConnectedProfile(profileData);
|
||||
|
|
@ -137,7 +136,6 @@ export default function AccountManagement({
|
|||
modalState={modalState}
|
||||
profileData={profileData}
|
||||
waitingSmsType={waitingSmsType}
|
||||
clearAuthWaitParams={clearAuthWaitParams}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ export default function AccountManagementModal({
|
|||
modalState,
|
||||
profileData,
|
||||
waitingSmsType,
|
||||
clearAuthWaitParams,
|
||||
}) {
|
||||
const styles = useStyles();
|
||||
const [modal, setModal] = modalState;
|
||||
|
|
@ -39,7 +38,6 @@ export default function AccountManagementModal({
|
|||
authMethod={authMethod}
|
||||
setAuthMethod={setAuthMethod}
|
||||
waitingSmsType={waitingSmsType}
|
||||
clearAuthWaitParams={clearAuthWaitParams}
|
||||
/>
|
||||
)}
|
||||
{visible && component === "destroy" && (
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useCallback, useState, useEffect } from "react";
|
||||
|
||||
import { View } from "react-native";
|
||||
import { View, Alert } from "react-native";
|
||||
import LittleLoader from "~/components/LittleLoader";
|
||||
import { Button } from "react-native-paper";
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
|
|
@ -29,7 +29,6 @@ export default function AccountManagementModalConnect({
|
|||
authMethod,
|
||||
setAuthMethod,
|
||||
waitingSmsType,
|
||||
clearAuthWaitParams,
|
||||
}) {
|
||||
const styles = useStyles();
|
||||
const { colors, custom } = useTheme();
|
||||
|
|
@ -71,12 +70,20 @@ export default function AccountManagementModalConnect({
|
|||
};
|
||||
}, [isLoading, loginRequest]);
|
||||
|
||||
const connectUsingPhoneNumber = () => {
|
||||
const connectUsingPhoneNumber = async () => {
|
||||
setIsLoading(true);
|
||||
sendAuthSMS({
|
||||
smsType: "C",
|
||||
body: "Se connecter sur Alerte-Secours:\nCode: [CODE]\n💙", // must don't exceed 160 chars including replaced [CODE]
|
||||
});
|
||||
try {
|
||||
await sendAuthSMS({
|
||||
smsType: "C",
|
||||
body: "Se connecter sur Alerte-Secours:\nCode: [CODE]\n💙", // must don't exceed 160 chars including replaced [CODE]
|
||||
});
|
||||
} catch (e) {
|
||||
setIsLoading(false);
|
||||
Alert.alert(
|
||||
"Échec de l’ouverture des SMS",
|
||||
"Impossible d’ouvrir l’application SMS. Réessayez.",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const smsDisclaimerModalStatePair = useState({ visible: false });
|
||||
|
|
@ -93,39 +100,20 @@ export default function AccountManagementModalConnect({
|
|||
const [loginConfirmRequest] = useMutation(LOGIN_CONFIRM_MUTATION);
|
||||
|
||||
const confirmLoginRequest = useCallback(async () => {
|
||||
try {
|
||||
const deviceUuid = await getDeviceUuid();
|
||||
const {
|
||||
data: {
|
||||
doAuthLoginConfimLoginRequest: { authTokenJwt },
|
||||
},
|
||||
} = await loginConfirmRequest({
|
||||
variables: { loginRequestId: loginRequest.id, deviceUuid },
|
||||
});
|
||||
await authActions.confirmLoginRequest({ authTokenJwt, isConnected });
|
||||
setIsLoading(false);
|
||||
clearAuthWaitParams?.();
|
||||
closeModal();
|
||||
} catch (e) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [
|
||||
loginConfirmRequest,
|
||||
loginRequest?.id,
|
||||
isConnected,
|
||||
clearAuthWaitParams,
|
||||
closeModal,
|
||||
]);
|
||||
const deviceUuid = await getDeviceUuid();
|
||||
const {
|
||||
data: {
|
||||
doAuthLoginConfimLoginRequest: { authTokenJwt },
|
||||
},
|
||||
} = await loginConfirmRequest({
|
||||
variables: { loginRequestId: loginRequest.id, deviceUuid },
|
||||
});
|
||||
await authActions.confirmLoginRequest({ authTokenJwt, isConnected });
|
||||
}, [loginConfirmRequest, loginRequest?.id, isConnected]);
|
||||
|
||||
const rejectLoginRequest = useCallback(async () => {
|
||||
try {
|
||||
await deleteLoginRequest({ variables: { id: loginRequest.id } });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
clearAuthWaitParams?.();
|
||||
closeModal();
|
||||
}
|
||||
}, [deleteLoginRequest, loginRequest?.id, clearAuthWaitParams, closeModal]);
|
||||
await deleteLoginRequest({ variables: { id: loginRequest.id } });
|
||||
}, [deleteLoginRequest, loginRequest]);
|
||||
|
||||
return (
|
||||
<View
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ export default function Form({
|
|||
profileData,
|
||||
openAccountModal,
|
||||
waitingSmsType,
|
||||
clearAuthWaitParams,
|
||||
}) {
|
||||
const { userId } = useSessionState(["userId"]);
|
||||
|
||||
|
|
@ -154,11 +153,7 @@ export default function Form({
|
|||
borderBottomWidth: 1,
|
||||
}}
|
||||
>
|
||||
<PhoneNumbers
|
||||
data={profileData}
|
||||
waitingSmsType={waitingSmsType}
|
||||
clearAuthWaitParams={clearAuthWaitParams}
|
||||
/>
|
||||
<PhoneNumbers data={profileData} waitingSmsType={waitingSmsType} />
|
||||
</View>
|
||||
|
||||
<View
|
||||
|
|
@ -195,7 +190,6 @@ export default function Form({
|
|||
profileData={profileData}
|
||||
openAccountModal={openAccountModal}
|
||||
waitingSmsType={waitingSmsType}
|
||||
clearAuthWaitParams={clearAuthWaitParams}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState, useEffect, useCallback } from "react";
|
||||
|
||||
import { View } from "react-native";
|
||||
import { View, Alert } from "react-native";
|
||||
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
|
||||
|
|
@ -26,11 +26,7 @@ import {
|
|||
|
||||
import useSendAuthSMS from "~/hooks/useSendAuthSMS";
|
||||
|
||||
export default function PhoneNumbersView({
|
||||
data,
|
||||
waitingSmsType,
|
||||
clearAuthWaitParams,
|
||||
}) {
|
||||
export default function PhoneNumbersView({ data, waitingSmsType }) {
|
||||
const [isLoading, setIsLoading] = useState(waitingSmsType === "R" || false);
|
||||
const phoneNumberList = data.selectOneUser.manyPhoneNumber;
|
||||
|
||||
|
|
@ -45,10 +41,18 @@ export default function PhoneNumbersView({
|
|||
|
||||
const registerPhoneNumber = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
sendAuthSMS({
|
||||
smsType: "R",
|
||||
body: "S'enregistrer sur Alerte-Secours:\nCode: [CODE]\n💙", // must don't exceed 160 chars including replaced [CODE]
|
||||
});
|
||||
try {
|
||||
await sendAuthSMS({
|
||||
smsType: "R",
|
||||
body: "S'enregistrer sur Alerte-Secours:\nCode: [CODE]\n💙", // must don't exceed 160 chars including replaced [CODE]
|
||||
});
|
||||
} catch (e) {
|
||||
setIsLoading(false);
|
||||
Alert.alert(
|
||||
"Échec de l’ouverture des SMS",
|
||||
"Impossible d’ouvrir l’application SMS. Réessayez.",
|
||||
);
|
||||
}
|
||||
}, [sendAuthSMS, setIsLoading]);
|
||||
|
||||
// Clear loading state after 3 minutes
|
||||
|
|
@ -73,16 +77,8 @@ export default function PhoneNumbersView({
|
|||
useEffect(() => {
|
||||
if (data.selectOneUser.oneUserLoginRequest) {
|
||||
setIsLoading(false);
|
||||
clearAuthWaitParams?.();
|
||||
}
|
||||
}, [data.selectOneUser.oneUserLoginRequest, clearAuthWaitParams]);
|
||||
|
||||
// Defensive cleanup on unmount to ensure no lingering loader
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setIsLoading(false);
|
||||
};
|
||||
}, []);
|
||||
}, [data.selectOneUser.oneUserLoginRequest]);
|
||||
|
||||
const deletePhoneNumberModalStatePair = useState({ visible: false });
|
||||
const [deletePhoneNumberModalState, setDeletePhoneNumberModalState] =
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect } from "react";
|
||||
|
||||
import { ScrollView, View } from "react-native";
|
||||
import { ScrollView, View, AppState } from "react-native";
|
||||
|
||||
import Loader from "~/components/Loader";
|
||||
import { useSubscription } from "@apollo/client";
|
||||
|
|
@ -12,6 +12,7 @@ import { createLogger } from "~/lib/logger";
|
|||
import { FEATURE_SCOPES } from "~/lib/logger/scopes";
|
||||
|
||||
import withConnectivity from "~/hoc/withConnectivity";
|
||||
import { useFocusEffect } from "@react-navigation/native";
|
||||
|
||||
import Form from "./Form";
|
||||
|
||||
|
|
@ -37,12 +38,38 @@ export default withConnectivity(function Profile({ navigation, route }) {
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [userId]);
|
||||
|
||||
const clearAuthWaitParams = React.useCallback(() => {
|
||||
navigation.setParams({
|
||||
waitingSmsType: undefined,
|
||||
openAccountModal: undefined,
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
restart();
|
||||
}, [restart]),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const sub = AppState.addEventListener("change", (state) => {
|
||||
if (state === "active") {
|
||||
restart();
|
||||
}
|
||||
});
|
||||
}, [navigation]);
|
||||
return () => {
|
||||
sub?.remove?.();
|
||||
};
|
||||
}, [restart]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
route.params?.waitingSmsType &&
|
||||
data?.selectOneUser?.oneUserLoginRequest
|
||||
) {
|
||||
navigation.setParams({
|
||||
waitingSmsType: undefined,
|
||||
openAccountModal: true,
|
||||
});
|
||||
}
|
||||
}, [
|
||||
route.params?.waitingSmsType,
|
||||
data?.selectOneUser?.oneUserLoginRequest,
|
||||
navigation,
|
||||
]);
|
||||
|
||||
if (loading || !data?.selectOneUser) {
|
||||
return <Loader />;
|
||||
|
|
@ -60,7 +87,6 @@ export default withConnectivity(function Profile({ navigation, route }) {
|
|||
profileData={data}
|
||||
openAccountModal={route.params?.openAccountModal}
|
||||
waitingSmsType={route.params?.waitingSmsType}
|
||||
clearAuthWaitParams={clearAuthWaitParams}
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue