Compare commits
7 Commits
amalia/26-
...
amalia/04-
| Author | SHA1 | Date | |
|---|---|---|---|
| 608381673f | |||
| 3cc7f76346 | |||
| 868b712fbb | |||
| a53b99b39d | |||
| 25d521f013 | |||
| ef08c821fa | |||
| 7729dc38f8 |
@@ -4,7 +4,7 @@ export default {
|
||||
expo: {
|
||||
name: "Desa+",
|
||||
slug: "mobile-darmasaba",
|
||||
version: "2.0.5", // Versi aplikasi (App Store)
|
||||
version: "2.1.0", // Versi aplikasi (App Store)
|
||||
jsEngine: "jsc",
|
||||
orientation: "portrait",
|
||||
icon: "./assets/images/logo-icon-small.png",
|
||||
@@ -14,7 +14,7 @@ export default {
|
||||
ios: {
|
||||
supportsTablet: true,
|
||||
bundleIdentifier: "mobiledarmasaba.app",
|
||||
buildNumber: "7",
|
||||
buildNumber: "8",
|
||||
infoPlist: {
|
||||
ITSAppUsesNonExemptEncryption: false,
|
||||
CFBundleDisplayName: "Desa+"
|
||||
@@ -23,7 +23,7 @@ export default {
|
||||
},
|
||||
android: {
|
||||
package: "mobiledarmasaba.app",
|
||||
versionCode: 15,
|
||||
versionCode: 16,
|
||||
adaptiveIcon: {
|
||||
foregroundImage: "./assets/images/logo-icon-small.png",
|
||||
backgroundColor: "#ffffff"
|
||||
|
||||
@@ -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<boolean | undefined>(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);
|
||||
@@ -44,7 +46,7 @@ export default function ListSetting() {
|
||||
try {
|
||||
const token = await getToken();
|
||||
if (token) {
|
||||
await apiUnregisteredToken({ user: entities.id, token });
|
||||
await apiUnregisteredToken({ user: entities.id, token, category: "unregister" });
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Error unregistering 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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -740,16 +740,21 @@ 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;
|
||||
};
|
||||
|
||||
export const apiUnregisteredToken = async (data: { user: string, token: string }) => {
|
||||
export const apiUnregisteredToken = async (data: { user: string, token: string, category?: string }) => {
|
||||
const response = await api.put(`/mobile/auth-token`, data)
|
||||
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;
|
||||
|
||||
@@ -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,23 +66,39 @@ 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: currentStatus } = await Notifications.getPermissionsAsync();
|
||||
|
||||
// Jika belum pernah ditentukan (undetermined), baru panggil request
|
||||
if (currentStatus === 'undetermined') {
|
||||
const { status: newStatus } = await Notifications.requestPermissionsAsync();
|
||||
await AsyncStorage.setItem('@notification_permission', newStatus === 'granted' ? 'true' : 'false');
|
||||
return newStatus === 'granted';
|
||||
}
|
||||
|
||||
// Jika sudah pernah ditentukan (granted/denied), update storage sesuai OS
|
||||
// Tapi jika granted, kita cek storage dlu sapa tau user pernah matiin manual di app
|
||||
const osPermission = await checkPermission();
|
||||
const existing = await AsyncStorage.getItem('@notification_permission');
|
||||
|
||||
if (osPermission === false) {
|
||||
await AsyncStorage.setItem('@notification_permission', 'false');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (osPermission === true) {
|
||||
// Jika OS ijinkan, tapi di storage belum ada, set true
|
||||
if (existing === null) {
|
||||
await AsyncStorage.setItem('@notification_permission', 'true');
|
||||
return true;
|
||||
}
|
||||
// Jika OS ijinkan dan di storage ada, ikuti storage (toggle manual user)
|
||||
return existing === 'true';
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (err) {
|
||||
console.warn('Error requesting notification permissions:', err);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user