chore: fix notif
This commit is contained in:
@@ -1,11 +1,23 @@
|
||||
import React from "react";
|
||||
import { TouchableOpacity, View } from "react-native";
|
||||
import React, { useState, useEffect, useRef, useCallback } from "react";
|
||||
import {
|
||||
TouchableOpacity,
|
||||
View,
|
||||
Text,
|
||||
Modal,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
} from "react-native";
|
||||
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useAuth } from "../auth/AuthContext";
|
||||
import { useTheme } from "../context/ThemeContext";
|
||||
import { logoutAdmin } from "../api/api_admin";
|
||||
import { fontSize, spacing } from "../theme";
|
||||
import {
|
||||
getLivreurNotifications,
|
||||
markLivreurNotificationsRead,
|
||||
} from "../api/api_delivery";
|
||||
import type { LivreurNotification } from "../api/api_delivery";
|
||||
import { fontSize, spacing, borderRadius } from "../theme";
|
||||
import type { DeliveryTabParamList } from "./types";
|
||||
|
||||
import DashboardScreen from "../screens/delivery/DashboardScreen";
|
||||
@@ -14,99 +26,341 @@ import AlertsScreen from "../screens/delivery/AlertsScreen";
|
||||
|
||||
const Tab = createBottomTabNavigator<DeliveryTabParamList>();
|
||||
|
||||
function formatNotifTime(dateStr: string): string {
|
||||
try {
|
||||
const diffMs = Date.now() - new Date(dateStr).getTime();
|
||||
const diffMin = Math.floor(diffMs / 60000);
|
||||
if (diffMin < 1) return "À l'instant";
|
||||
if (diffMin < 60) return `Il y a ${diffMin} min`;
|
||||
const diffH = Math.floor(diffMin / 60);
|
||||
if (diffH < 24) return `Il y a ${diffH}h`;
|
||||
return `Il y a ${Math.floor(diffH / 24)} jours`;
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export default function DeliveryNavigator() {
|
||||
const { logout } = useAuth();
|
||||
const { colors, isDark, toggleTheme } = useTheme();
|
||||
|
||||
const [notifications, setNotifications] = useState<LivreurNotification[]>([]);
|
||||
const [unreadCount, setUnreadCount] = useState(0);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const seenIdsRef = useRef<Set<string>>(new Set());
|
||||
const isFirstLoad = useRef(true);
|
||||
|
||||
const fetchNotifications = useCallback(async () => {
|
||||
const res = await getLivreurNotifications();
|
||||
if (!res.success || !res.notifications) return;
|
||||
setNotifications(res.notifications);
|
||||
|
||||
if (!isFirstLoad.current) {
|
||||
let newUnread = 0;
|
||||
for (const n of res.notifications) {
|
||||
if (!n.read) {
|
||||
const key = `${n.command_id}-${n.type}-${n.created_at}`;
|
||||
if (!seenIdsRef.current.has(key)) {
|
||||
seenIdsRef.current.add(key);
|
||||
newUnread++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newUnread > 0) {
|
||||
setUnreadCount((prev) => prev + newUnread);
|
||||
}
|
||||
} else {
|
||||
for (const n of res.notifications) {
|
||||
const key = `${n.command_id}-${n.type}-${n.created_at}`;
|
||||
seenIdsRef.current.add(key);
|
||||
}
|
||||
setUnreadCount(res.unread_count ?? 0);
|
||||
isFirstLoad.current = false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchNotifications();
|
||||
const interval = setInterval(fetchNotifications, 15000);
|
||||
return () => clearInterval(interval);
|
||||
}, [fetchNotifications]);
|
||||
|
||||
const openModal = async () => {
|
||||
setShowModal(true);
|
||||
if (unreadCount > 0) {
|
||||
await markLivreurNotificationsRead();
|
||||
setUnreadCount(0);
|
||||
setNotifications((prev) => prev.map((n) => ({ ...n, read: true })));
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logoutAdmin();
|
||||
await logout();
|
||||
};
|
||||
|
||||
return (
|
||||
<Tab.Navigator
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: colors.bgSecondary },
|
||||
headerTintColor: colors.textWhite,
|
||||
headerRight: () => (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginRight: spacing.l,
|
||||
}}
|
||||
>
|
||||
<TouchableOpacity
|
||||
onPress={toggleTheme}
|
||||
style={{ marginRight: spacing.m }}
|
||||
<>
|
||||
<Tab.Navigator
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: colors.bgSecondary },
|
||||
headerTintColor: colors.textWhite,
|
||||
headerRight: () => (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginRight: spacing.l,
|
||||
gap: spacing.m,
|
||||
}}
|
||||
>
|
||||
{/* Cloche notifications */}
|
||||
<TouchableOpacity
|
||||
onPress={openModal}
|
||||
style={{ position: "relative" }}
|
||||
>
|
||||
<Ionicons
|
||||
name="notifications-outline"
|
||||
size={24}
|
||||
color={colors.textSecondary}
|
||||
/>
|
||||
{unreadCount > 0 && (
|
||||
<View style={styles.badge}>
|
||||
<Text style={styles.badgeText}>
|
||||
{unreadCount > 9 ? "9+" : unreadCount}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity onPress={toggleTheme}>
|
||||
<Ionicons
|
||||
name={isDark ? "sunny-outline" : "moon-outline"}
|
||||
size={22}
|
||||
color={colors.textSecondary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={handleLogout}>
|
||||
<Ionicons
|
||||
name="log-out-outline"
|
||||
size={24}
|
||||
color={colors.textSecondary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
),
|
||||
tabBarStyle: {
|
||||
backgroundColor: colors.bgSecondary,
|
||||
borderTopColor: colors.border,
|
||||
borderTopWidth: 1,
|
||||
},
|
||||
tabBarActiveTintColor: colors.success,
|
||||
tabBarInactiveTintColor: colors.textMuted,
|
||||
tabBarLabelStyle: { fontSize: fontSize.xs },
|
||||
}}
|
||||
>
|
||||
<Tab.Screen
|
||||
name="Dashboard"
|
||||
component={DashboardScreen}
|
||||
options={{
|
||||
title: "Livraisons",
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<Ionicons
|
||||
name={isDark ? "sunny-outline" : "moon-outline"}
|
||||
size={22}
|
||||
color={colors.textSecondary}
|
||||
name="navigate-outline"
|
||||
size={size}
|
||||
color={color}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={handleLogout}>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Stats"
|
||||
component={StatsScreen}
|
||||
options={{
|
||||
title: "Stats",
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<Ionicons
|
||||
name="log-out-outline"
|
||||
size={24}
|
||||
color={colors.textSecondary}
|
||||
name="stats-chart-outline"
|
||||
size={size}
|
||||
color={color}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Alerts"
|
||||
component={AlertsScreen}
|
||||
options={{
|
||||
title: "Alertes",
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<Ionicons
|
||||
name="alert-circle-outline"
|
||||
size={size}
|
||||
color={color}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
|
||||
{/* Modal notifications */}
|
||||
<Modal
|
||||
visible={showModal}
|
||||
animationType="slide"
|
||||
transparent
|
||||
onRequestClose={() => setShowModal(false)}
|
||||
>
|
||||
<View style={styles.overlay}>
|
||||
<View
|
||||
style={[
|
||||
styles.modalContent,
|
||||
{ backgroundColor: colors.bgPrimary },
|
||||
]}
|
||||
>
|
||||
{/* Header modal */}
|
||||
<View
|
||||
style={[
|
||||
styles.modalHeader,
|
||||
{ borderBottomColor: colors.border },
|
||||
]}
|
||||
>
|
||||
<View style={styles.modalTitleRow}>
|
||||
<Ionicons
|
||||
name="notifications-outline"
|
||||
size={20}
|
||||
color={colors.success}
|
||||
/>
|
||||
<Text
|
||||
style={[
|
||||
styles.modalTitle,
|
||||
{ color: colors.textPrimary },
|
||||
]}
|
||||
>
|
||||
Notifications
|
||||
</Text>
|
||||
</View>
|
||||
<TouchableOpacity onPress={() => setShowModal(false)}>
|
||||
<Ionicons
|
||||
name="close"
|
||||
size={24}
|
||||
color={colors.textSecondary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* Liste */}
|
||||
<ScrollView style={{ flex: 1 }}>
|
||||
{notifications.length === 0 ? (
|
||||
<Text
|
||||
style={[
|
||||
styles.emptyText,
|
||||
{ color: colors.textMuted },
|
||||
]}
|
||||
>
|
||||
Aucune notification
|
||||
</Text>
|
||||
) : (
|
||||
notifications.map((n, i) => (
|
||||
<View
|
||||
key={`${n.command_id}-${n.type}-${n.created_at}-${i}`}
|
||||
style={[
|
||||
styles.notifItem,
|
||||
{
|
||||
borderBottomColor: colors.border,
|
||||
borderLeftColor: n.read
|
||||
? "transparent"
|
||||
: colors.success,
|
||||
backgroundColor: n.read
|
||||
? "transparent"
|
||||
: colors.success + "10",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
styles.notifMessage,
|
||||
{ color: colors.textPrimary },
|
||||
]}
|
||||
>
|
||||
{n.message}
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
styles.notifTime,
|
||||
{ color: colors.textMuted },
|
||||
]}
|
||||
>
|
||||
{formatNotifTime(n.created_at)}
|
||||
</Text>
|
||||
</View>
|
||||
))
|
||||
)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
),
|
||||
tabBarStyle: {
|
||||
backgroundColor: colors.bgSecondary,
|
||||
borderTopColor: colors.border,
|
||||
borderTopWidth: 1,
|
||||
},
|
||||
tabBarActiveTintColor: colors.success,
|
||||
tabBarInactiveTintColor: colors.textMuted,
|
||||
tabBarLabelStyle: { fontSize: fontSize.xs },
|
||||
}}
|
||||
>
|
||||
<Tab.Screen
|
||||
name="Dashboard"
|
||||
component={DashboardScreen}
|
||||
options={{
|
||||
title: "Livraisons",
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<Ionicons
|
||||
name="navigate-outline"
|
||||
size={size}
|
||||
color={color}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Stats"
|
||||
component={StatsScreen}
|
||||
options={{
|
||||
title: "Stats",
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<Ionicons
|
||||
name="stats-chart-outline"
|
||||
size={size}
|
||||
color={color}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="Alerts"
|
||||
component={AlertsScreen}
|
||||
options={{
|
||||
title: "Alertes",
|
||||
tabBarIcon: ({ color, size }) => (
|
||||
<Ionicons
|
||||
name="alert-circle-outline"
|
||||
size={size}
|
||||
color={color}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
</View>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
badge: {
|
||||
position: "absolute",
|
||||
top: -4,
|
||||
right: -4,
|
||||
backgroundColor: "#ef4444",
|
||||
borderRadius: 10,
|
||||
minWidth: 18,
|
||||
height: 18,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
badgeText: {
|
||||
color: "#fff",
|
||||
fontSize: 10,
|
||||
fontWeight: "bold",
|
||||
},
|
||||
overlay: {
|
||||
flex: 1,
|
||||
justifyContent: "flex-end",
|
||||
backgroundColor: "rgba(0,0,0,0.5)",
|
||||
},
|
||||
modalContent: {
|
||||
height: "70%",
|
||||
borderTopLeftRadius: borderRadius.lg,
|
||||
borderTopRightRadius: borderRadius.lg,
|
||||
},
|
||||
modalHeader: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
padding: spacing.l,
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
modalTitleRow: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: spacing.s,
|
||||
},
|
||||
modalTitle: {
|
||||
fontSize: fontSize.lg,
|
||||
fontWeight: "bold",
|
||||
},
|
||||
emptyText: {
|
||||
textAlign: "center",
|
||||
marginTop: spacing.xl,
|
||||
fontSize: fontSize.sm,
|
||||
},
|
||||
notifItem: {
|
||||
padding: spacing.m,
|
||||
borderBottomWidth: 1,
|
||||
borderLeftWidth: 3,
|
||||
},
|
||||
notifMessage: {
|
||||
fontSize: fontSize.sm,
|
||||
marginBottom: spacing.xs,
|
||||
},
|
||||
notifTime: {
|
||||
fontSize: fontSize.xs,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -49,13 +49,6 @@ import TomTomMap, {
|
||||
TomTomMarker,
|
||||
} from "../../components/TomTomMap";
|
||||
import DetailsModal from "../../components/ui/Modal";
|
||||
import {
|
||||
setupDeliveryNotificationChannel,
|
||||
registerForPushNotificationsAsync,
|
||||
sendPushTokenToBackend,
|
||||
addNotificationReceivedListener,
|
||||
} from "../../services/pushNotifications";
|
||||
import { getLivreurNotifications } from "../../api/api_delivery";
|
||||
|
||||
const { width: SCREEN_WIDTH } = Dimensions.get("window");
|
||||
const MAP_HEIGHT = 260;
|
||||
@@ -110,10 +103,7 @@ export default function DashboardScreen() {
|
||||
const pendingRouteAddress = useRef<string | null>(null);
|
||||
const { alert, showError, showSuccess, hideAlert } = useAlert();
|
||||
|
||||
// Notifications
|
||||
const [unreadNotifCount, setUnreadNotifCount] = useState(0);
|
||||
const [detailsDelivery, setDetailsDelivery] = useState<EnrichedDelivery | null>(null);
|
||||
const pushTokenRef = useRef<string | null>(null);
|
||||
|
||||
const STATUS_COLORS: Record<string, string> = useMemo(
|
||||
() => ({
|
||||
@@ -281,37 +271,6 @@ export default function DashboardScreen() {
|
||||
loadData();
|
||||
}, [loadData]);
|
||||
|
||||
// Push notifications: enregistrement + badge
|
||||
useEffect(() => {
|
||||
let notifSub: ReturnType<typeof addNotificationReceivedListener> | null = null;
|
||||
|
||||
const setupPush = async () => {
|
||||
await setupDeliveryNotificationChannel();
|
||||
const token = await registerForPushNotificationsAsync();
|
||||
if (token) {
|
||||
pushTokenRef.current = token;
|
||||
await sendPushTokenToBackend(token);
|
||||
}
|
||||
// Charger le compteur de notifications non lues
|
||||
const res = await getLivreurNotifications();
|
||||
if (res.success) {
|
||||
setUnreadNotifCount(res.unread_count ?? 0);
|
||||
}
|
||||
};
|
||||
|
||||
setupPush();
|
||||
|
||||
// Écouter les nouvelles notifications en foreground
|
||||
notifSub = addNotificationReceivedListener((_notif) => {
|
||||
setUnreadNotifCount((prev) => prev + 1);
|
||||
loadData();
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (notifSub) notifSub.remove();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// Quand GPS devient disponible, rejouer la route en attente
|
||||
useEffect(() => {
|
||||
@@ -1401,25 +1360,6 @@ export default function DashboardScreen() {
|
||||
fontWeight: "700",
|
||||
},
|
||||
|
||||
notifBadgeBar: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
backgroundColor: "#FF6B0015",
|
||||
borderLeftWidth: 3,
|
||||
borderLeftColor: "#FF6B00",
|
||||
paddingHorizontal: spacing.m,
|
||||
paddingVertical: 8,
|
||||
marginHorizontal: spacing.l,
|
||||
marginTop: spacing.s,
|
||||
borderRadius: 6,
|
||||
},
|
||||
notifBadgeText: {
|
||||
color: "#FF6B00",
|
||||
fontSize: fontSize.sm,
|
||||
fontWeight: "600",
|
||||
flex: 1,
|
||||
},
|
||||
fullscreenStatusBadge: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
@@ -1666,16 +1606,6 @@ export default function DashboardScreen() {
|
||||
</ScrollView>
|
||||
</DetailsModal>
|
||||
|
||||
{/* ── Badge notifications ── */}
|
||||
{unreadNotifCount > 0 && (
|
||||
<View style={styles.notifBadgeBar}>
|
||||
<Ionicons name="notifications" size={16} color="#FF6B00" />
|
||||
<Text style={styles.notifBadgeText}>
|
||||
{unreadNotifCount} nouvelle{unreadNotifCount > 1 ? "s" : ""} commande{unreadNotifCount > 1 ? "s" : ""} assignée{unreadNotifCount > 1 ? "s" : ""}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* ── Barre de statut ── */}
|
||||
<View style={styles.statusBar}>
|
||||
<Text style={styles.statusLabel}>Mon statut:</Text>
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
import * as Notifications from "expo-notifications";
|
||||
import * as Device from "expo-device";
|
||||
import Constants from "expo-constants";
|
||||
import { Platform } from "react-native";
|
||||
import apiClient from "../api/client";
|
||||
|
||||
const LIVREUR_API = "https://uber-stup.club/api/v1/livreur";
|
||||
|
||||
// Configuration du comportement des notifications en foreground
|
||||
Notifications.setNotificationHandler({
|
||||
handleNotification: async () => ({
|
||||
shouldPlaySound: true,
|
||||
shouldSetBadge: true,
|
||||
shouldShowBanner: true,
|
||||
shouldShowList: true,
|
||||
}),
|
||||
});
|
||||
|
||||
export async function setupDeliveryNotificationChannel(): Promise<void> {
|
||||
if (Platform.OS === "android") {
|
||||
await Notifications.setNotificationChannelAsync("deliveries", {
|
||||
name: "Livraisons",
|
||||
importance: Notifications.AndroidImportance.MAX,
|
||||
vibrationPattern: [0, 250, 250, 250],
|
||||
lightColor: "#FF6B00",
|
||||
sound: "default",
|
||||
enableVibrate: true,
|
||||
showBadge: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Enregistrer le device pour les push notifications et retourner le token
|
||||
export async function registerForPushNotificationsAsync(): Promise<
|
||||
string | null
|
||||
> {
|
||||
if (!Device.isDevice) {
|
||||
console.log(
|
||||
"⚠️ Push notifications ne fonctionnent pas sur un émulateur",
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const { status: existingStatus } =
|
||||
await Notifications.getPermissionsAsync();
|
||||
let finalStatus = existingStatus;
|
||||
|
||||
if (existingStatus !== "granted") {
|
||||
const { status } = await Notifications.requestPermissionsAsync();
|
||||
finalStatus = status;
|
||||
}
|
||||
|
||||
if (finalStatus !== "granted") {
|
||||
console.log("❌ Permission push notifications refusée");
|
||||
return null;
|
||||
}
|
||||
|
||||
const projectId =
|
||||
Constants.expoConfig?.extra?.eas?.projectId ??
|
||||
Constants.easConfig?.projectId;
|
||||
|
||||
if (!projectId) {
|
||||
console.log("⚠️ projectId non trouvé - push notifications désactivées");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const tokenData = await Notifications.getExpoPushTokenAsync({
|
||||
projectId,
|
||||
});
|
||||
const pushToken = tokenData.data;
|
||||
console.log("📱 [LIVREUR] Push token:", pushToken);
|
||||
return pushToken;
|
||||
} catch (error) {
|
||||
console.error("❌ [LIVREUR] Erreur récupération push token:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Envoyer le push token au backend (endpoint livreur)
|
||||
export async function sendPushTokenToBackend(
|
||||
pushToken: string,
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const { data } = await apiClient.post(`${LIVREUR_API}/push-token`, {
|
||||
push_token: pushToken,
|
||||
});
|
||||
console.log("✅ [LIVREUR] Push token enregistré sur le backend");
|
||||
return data.success === true;
|
||||
} catch (error) {
|
||||
console.error("❌ [LIVREUR] Erreur envoi push token:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Supprimer le push token du backend (au logout)
|
||||
export async function removePushTokenFromBackend(): Promise<void> {
|
||||
try {
|
||||
await apiClient.delete(`${LIVREUR_API}/push-token`);
|
||||
console.log("✅ [LIVREUR] Push token supprimé du backend");
|
||||
} catch (error) {
|
||||
console.error("❌ [LIVREUR] Erreur suppression push token:", error);
|
||||
}
|
||||
}
|
||||
|
||||
export function addNotificationReceivedListener(
|
||||
callback: (notification: Notifications.Notification) => void,
|
||||
): Notifications.EventSubscription {
|
||||
return Notifications.addNotificationReceivedListener(callback);
|
||||
}
|
||||
|
||||
export function addNotificationResponseListener(
|
||||
callback: (response: Notifications.NotificationResponse) => void,
|
||||
): Notifications.EventSubscription {
|
||||
return Notifications.addNotificationResponseReceivedListener(callback);
|
||||
}
|
||||
|
||||
export async function setBadgeCount(count: number): Promise<void> {
|
||||
try {
|
||||
await Notifications.setBadgeCountAsync(count);
|
||||
} catch {
|
||||
// Silencieux
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user