chore: add push notif

This commit is contained in:
2026-03-14 18:28:02 +01:00
parent 0ae1529353
commit b1a41c4a16
5 changed files with 145 additions and 3 deletions
+11 -3
View File
@@ -25,9 +25,8 @@
"predictiveBackGestureEnabled": false, "predictiveBackGestureEnabled": false,
"package": "com.uberstup.clientpanel", "package": "com.uberstup.clientpanel",
"versionCode": 1, "versionCode": 1,
"permissions": [ "permissions": ["android.permission.VIBRATE"],
"android.permission.VIBRATE" "googleServicesFile": "./google-services.json"
]
}, },
"web": { "web": {
"favicon": "./assets/icon.png" "favicon": "./assets/icon.png"
@@ -35,6 +34,15 @@
"plugins": [ "plugins": [
"expo-font", "expo-font",
"expo-router", "expo-router",
[
"expo-notifications",
{
"icon": "./assets/icon.png",
"color": "#ffffff",
"androidMode": "default",
"androidCollapsedTitle": "#{unread_notifications} nouvelles notifications"
}
],
[ [
"expo-build-properties", "expo-build-properties",
{ {
+29
View File
@@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "607244438046",
"project_id": "frontend-client-f3354",
"storage_bucket": "frontend-client-f3354.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:607244438046:android:ce55d750fd0f1b98ac3c0f",
"android_client_info": {
"package_name": "com.uberstup.clientpanel"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyA2-og1AoVmwXXY4KTX-J-bt6E5cjiQvmM"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
+16
View File
@@ -777,6 +777,22 @@ export const getPublicSettings = async (): Promise<PublicSettings> => {
} }
}; };
export const registerPushToken = async (token: string): Promise<void> => {
try {
await apiClient.post(`${V1}/push-token`, { token });
} catch {
// silencieux
}
};
export const unregisterPushToken = async (): Promise<void> => {
try {
await apiClient.delete(`${V1}/push-token`);
} catch {
// silencieux
}
};
export const calculateOrderTotal = (order: any): number => { export const calculateOrderTotal = (order: any): number => {
if (typeof order.total === "number" && order.total > 0) return order.total; if (typeof order.total === "number" && order.total > 0) return order.total;
if (typeof order.total_prix === "number" && order.total_prix > 0) if (typeof order.total_prix === "number" && order.total_prix > 0)
@@ -7,9 +7,11 @@ import React, {
useCallback, useCallback,
type ReactNode, type ReactNode,
} from "react"; } from "react";
import * as Notifications from "expo-notifications";
import { getClientNotifications, markNotificationsRead } from "../api/api"; import { getClientNotifications, markNotificationsRead } from "../api/api";
import type { ClientNotification } from "../api/api"; import type { ClientNotification } from "../api/api";
import { getToken } from "../auth/tokenStorage"; import { getToken } from "../auth/tokenStorage";
import { registerForPushNotifications } from "../services/pushNotifications";
interface ToastData { interface ToastData {
message: string; message: string;
@@ -128,6 +130,30 @@ export function NotificationProvider({ children }: { children: ReactNode }) {
} }
}, []); }, []);
// Enregistrement push token + listeners (pattern doc Expo)
useEffect(() => {
registerForPushNotifications().catch(console.error);
const notificationListener = Notifications.addNotificationReceivedListener((notif) => {
const body = notif.request.content.body ?? "";
const data = notif.request.content.data as any;
showToast(body, getToastType(data?.type ?? ""));
fetchNotifications();
});
const responseListener = Notifications.addNotificationResponseReceivedListener((response) => {
const data = response.notification.request.content.data as any;
if (data?.command_id) {
setNavigateToOrder(data.command_id);
}
});
return () => {
notificationListener.remove();
responseListener.remove();
};
}, [fetchNotifications, showToast]);
// Polling toutes les 15 secondes // Polling toutes les 15 secondes
useEffect(() => { useEffect(() => {
fetchNotifications(); fetchNotifications();
+63
View File
@@ -0,0 +1,63 @@
import * as Notifications from "expo-notifications";
import * as Device from "expo-device";
import Constants from "expo-constants";
import { Platform, Alert } from "react-native";
import { registerPushToken } from "../api/api";
// Comportement des notifs reçues en foreground
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldPlaySound: true,
shouldSetBadge: true,
shouldShowBanner: true,
shouldShowList: true,
}),
});
function handleRegistrationError(errorMessage: string) {
console.error("[PUSH]", errorMessage);
Alert.alert("Push Notification Error", errorMessage);
}
export async function registerForPushNotifications(): Promise<string | undefined> {
if (Platform.OS === "android") {
await Notifications.setNotificationChannelAsync("default", {
name: "default",
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C",
});
}
if (Device.isDevice) {
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") {
handleRegistrationError("Permission not granted to get push token for push notification!");
return;
}
const projectId =
Constants?.expoConfig?.extra?.eas?.projectId ??
Constants?.easConfig?.projectId;
if (!projectId) {
handleRegistrationError("Project ID not found");
return;
}
try {
const pushTokenString = (
await Notifications.getExpoPushTokenAsync({ projectId })
).data;
console.log("[PUSH] Token obtenu:", pushTokenString);
await registerPushToken(pushTokenString);
return pushTokenString;
} catch (e: unknown) {
handleRegistrationError(`${e}`);
}
} else {
handleRegistrationError("Must use physical device for push notifications");
}
}