From 868b712fbb3daf1175b76a2eb75239a75cabdef4 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Tue, 3 Mar 2026 16:44:02 +0800 Subject: [PATCH] upd: notifikasi Deskripsi: - belom selesai notifikasi No Issues --- app/(application)/setting/index.tsx | 73 +++++++++++++++++------------ lib/api.ts | 7 ++- lib/useNotification.ts | 39 ++++++--------- providers/AuthProvider.tsx | 15 +++--- 4 files changed, 71 insertions(+), 63 deletions(-) diff --git a/app/(application)/setting/index.tsx b/app/(application)/setting/index.tsx index 8090761..5f769aa 100644 --- a/app/(application)/setting/index.tsx +++ b/app/(application)/setting/index.tsx @@ -3,13 +3,14 @@ import Text from "@/components/Text"; import ButtonSetting from "@/components/buttonSetting"; import DrawerBottom from "@/components/drawerBottom"; import Styles from "@/constants/Styles"; -import { apiRegisteredToken, apiUnregisteredToken } from "@/lib/api"; -import { checkPermission, getToken, openSettings, requestPermission } from "@/lib/useNotification"; +import { apiGetCheckToken, apiRegisteredToken, apiUnregisteredToken } from "@/lib/api"; +import { checkPermission, getToken, openSettings } from "@/lib/useNotification"; import { useAuthSession } from "@/providers/AuthProvider"; import { useTheme } from "@/providers/ThemeProvider"; import { Feather, Ionicons } from "@expo/vector-icons"; +import AsyncStorage from "@react-native-async-storage/async-storage"; import { router } from "expo-router"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { AppState, AppStateStatus, Pressable, View } from "react-native"; import { useSelector } from "react-redux"; @@ -28,12 +29,13 @@ export default function ListSetting() { const [showLogoutModal, setShowLogoutModal] = useState(false) const [showThemeModal, setShowThemeModal] = useState(false) + const prevOsPermission = useRef(undefined); const registerToken = async () => { try { const token = await getToken(); if (token) { - await apiRegisteredToken({ user: entities.id, token }); + await apiRegisteredToken({ user: entities.id, token, category: "register" }); } } catch (error) { console.warn('Error registering token:', error); @@ -52,15 +54,31 @@ export default function ListSetting() { }; const checkNotif = useCallback(async () => { - const status = await checkPermission(); - setIsNotificationEnabled((prev) => { - if (prev === false && status === true) { - registerToken(); - } else if (prev === true && status === false) { - unregisterToken(); + const osPermission = await checkPermission(); + + // Jika dari tidak diijinkan sistem kemudian diijinkan (setelah balik dari pengaturan device) + if (prevOsPermission.current === false && osPermission === true) { + await registerToken(); + } + prevOsPermission.current = osPermission; + + if (!osPermission) { + setIsNotificationEnabled(false); + return; + } + + try { + const token = await getToken(); + if (token) { + const response = await apiGetCheckToken({ user: entities.id, token }); + setIsNotificationEnabled(!!response.data); + } else { + setIsNotificationEnabled(false); } - return !!status; - }); + } catch (error) { + console.warn('Error checking token status:', error); + setIsNotificationEnabled(false); + } }, [entities.id]); useEffect(() => { @@ -78,10 +96,12 @@ export default function ListSetting() { }, [checkNotif]); const handleToggleNotif = async () => { - if (isNotificationEnabled) { + const osPermission = await checkPermission(); + + if (!osPermission) { setModalConfig({ - title: "Matikan Notifikasi?", - message: "Anda akan diarahkan ke pengaturan sistem untuk mematikan notifikasi.", + title: "Aktifkan Notifikasi?", + message: "Izin notifikasi tidak diberikan. Buka pengaturan sistem untuk mengaktifkannya?", confirmText: "Buka Pengaturan", onConfirm: () => { setModalVisible(false); @@ -90,22 +110,17 @@ export default function ListSetting() { }); setModalVisible(true); } else { - const granted = await requestPermission(); - if (granted) { - setIsNotificationEnabled(true); - registerToken(); + // OS Permission is granted, perform in-app toggle + const targetState = !isNotificationEnabled; + if (targetState) { + await AsyncStorage.setItem('@notification_permission', "true"); + await registerToken(); } else { - setModalConfig({ - title: "Aktifkan Notifikasi?", - message: "Izin notifikasi tidak diberikan. Buka pengaturan sistem untuk mengaktifkannya?", - confirmText: "Buka Pengaturan", - onConfirm: () => { - setModalVisible(false); - openSettings(); - } - }); - setModalVisible(true); + await AsyncStorage.setItem('@notification_permission', "false"); + await unregisterToken(); } + // UI will be updated by checkNotif (triggered by state change or manually here) + setIsNotificationEnabled(targetState); } }; diff --git a/lib/api.ts b/lib/api.ts index ad33ea3..81624f6 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -740,7 +740,7 @@ export const apiShareDocument = async (data: { dataDivision: any[], dataItem: an return response.data; }; -export const apiRegisteredToken = async (data: { user: string, token: string }) => { +export const apiRegisteredToken = async (data: { user: string, token: string, category?: string }) => { const response = await api.post(`/mobile/auth-token`, data) return response.data; }; @@ -750,6 +750,11 @@ export const apiUnregisteredToken = async (data: { user: string, token: string } return response.data; }; +export const apiGetCheckToken = async (data: { user: string, token: string }) => { + const response = await api.post(`mobile/auth-token/check`, data); + return response.data; +}; + export const apiGetNotification = async ({ user, page }: { user: string, page?: number }) => { const response = await api.get(`mobile/home/notification?user=${user}&page=${page}`); return response.data; diff --git a/lib/useNotification.ts b/lib/useNotification.ts index 7a9ac7c..f0c6e80 100644 --- a/lib/useNotification.ts +++ b/lib/useNotification.ts @@ -1,3 +1,5 @@ +import { ConstEnv } from '@/constants/ConstEnv'; +import AsyncStorage from '@react-native-async-storage/async-storage'; import { getApp, getApps, initializeApp } from '@react-native-firebase/app'; import { getMessaging, @@ -6,8 +8,7 @@ import { } from '@react-native-firebase/messaging'; import * as Notifications from 'expo-notifications'; import { useEffect } from 'react'; -import { Linking, PermissionsAndroid, Platform } from 'react-native'; -import { ConstEnv } from '@/constants/ConstEnv'; +import { Linking, Platform } from 'react-native'; const RNfirebaseConfig = { apiKey: ConstEnv.firebase.apiKey, @@ -39,13 +40,15 @@ const initializeFirebase = async () => { export const checkPermission = async () => { try { - if (Platform.OS === 'android') { - return await PermissionsAndroid.check( - PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS - ); - } else if (Platform.OS === 'ios') { - const { status } = await Notifications.getPermissionsAsync(); - return status === 'granted'; + // Cek status permission sekarang + const { status } = await Notifications.getPermissionsAsync(); + + if (status === 'granted') { + return true; + } + + if (status === 'denied') { + return false; } } catch (err) { console.warn('Error checking notification permissions:', err); @@ -63,21 +66,9 @@ export const openSettings = () => { export const requestPermission = async () => { try { - if (Platform.OS === 'android') { - const cek = await PermissionsAndroid.check( - PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS - ); - if (!cek) { - const granted = await PermissionsAndroid.request( - PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS - ); - return granted === PermissionsAndroid.RESULTS.GRANTED; - } - return true; - } else if (Platform.OS === 'ios') { - const { status } = await Notifications.requestPermissionsAsync(); - return status === 'granted'; - } + const { status: newStatus } = await Notifications.requestPermissionsAsync(); + await AsyncStorage.setItem('@notification_permission', newStatus === 'granted' ? "true" : "false"); + return newStatus === 'granted'; } catch (err) { console.warn('Error requesting notification permissions:', err); } diff --git a/providers/AuthProvider.tsx b/providers/AuthProvider.tsx index ac1f23c..e9d4c02 100644 --- a/providers/AuthProvider.tsx +++ b/providers/AuthProvider.tsx @@ -1,6 +1,6 @@ import { ConstEnv } from '@/constants/ConstEnv'; import { apiRegisteredToken, apiUnregisteredToken } from '@/lib/api'; -import { getToken, requestPermission } from '@/lib/useNotification'; +import { getToken } from '@/lib/useNotification'; import AsyncStorage from '@react-native-async-storage/async-storage'; import CryptoES from "crypto-es"; import { router } from "expo-router"; @@ -52,16 +52,12 @@ export default function AuthProvider({ children }: { children: ReactNode }): Rea const signIn = useCallback(async (token: string) => { const hasil = await decryptToken(String(token)) - const permission = await requestPermission() - if (permission) { + // const permission = await requestPermission() + const permissionStorage = await AsyncStorage.getItem('@notification_permission') + if (permissionStorage === "true") { + const tokenDevice = await getToken() try { - // if (Platform.OS === 'android') { - const tokenDevice = await getToken() const register = await apiRegisteredToken({ user: hasil, token: String(tokenDevice) }) - // }else{ - // const tokenDevice = await getToken() - // const register = await apiRegisteredToken({ user: hasil, token: String(tokenDevice) }) - // } } catch (error) { console.error(error) } finally { @@ -71,6 +67,7 @@ export default function AuthProvider({ children }: { children: ReactNode }): Rea return true } } else { + const register = await apiRegisteredToken({ user: hasil, token: "" }) await AsyncStorage.setItem('@token', token); tokenRef.current = token; router.replace('/home')