Compare commits
8 Commits
amalia/24-
...
amalia/03-
| Author | SHA1 | Date | |
|---|---|---|---|
| 868b712fbb | |||
| a53b99b39d | |||
| 25d521f013 | |||
| aee0823cb1 | |||
| 2a0e1f4c1f | |||
| ef08c821fa | |||
| fd5d582092 | |||
| 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"
|
||||
|
||||
@@ -252,7 +252,7 @@ export default function DetailAnnouncement() {
|
||||
{dataFile.map((item, index) => (
|
||||
<BorderBottomItem
|
||||
key={`${item.id}-${index}`}
|
||||
borderType="bottom"
|
||||
borderType={index === dataFile.length - 1 ? 'none' : 'bottom'}
|
||||
icon={<MaterialCommunityIcons
|
||||
name={isImageFile(item.extension) ? "file-image-outline" : "file-document-outline"}
|
||||
size={25}
|
||||
|
||||
@@ -78,7 +78,6 @@ export default function CreateAnnouncement() {
|
||||
async function handleCreate() {
|
||||
try {
|
||||
setLoading(true)
|
||||
console.log('jalan')
|
||||
const hasil = await decryptToken(String(token?.current))
|
||||
const fd = new FormData()
|
||||
|
||||
@@ -91,7 +90,7 @@ export default function CreateAnnouncement() {
|
||||
}
|
||||
|
||||
fd.append("data", JSON.stringify(
|
||||
{ user: 'apaya', groups: divisionMember, ...dataForm }
|
||||
{ user: hasil, groups: divisionMember, ...dataForm }
|
||||
))
|
||||
|
||||
const response = await apiCreateAnnouncement(fd)
|
||||
|
||||
@@ -241,7 +241,6 @@ export default function ListDivision() {
|
||||
</View>
|
||||
}
|
||||
title={item.name}
|
||||
titleWeight="normal"
|
||||
/>
|
||||
)
|
||||
}}
|
||||
|
||||
@@ -319,6 +319,7 @@ export default function ListProject() {
|
||||
content="page"
|
||||
title={item.title}
|
||||
headerColor="primary"
|
||||
titleTail={2}
|
||||
>
|
||||
<ProgressBar value={item.progress} category="list" />
|
||||
<View style={[Styles.rowSpaceBetween]}>
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Styles from '@/constants/Styles';
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { useRouter } from 'expo-router';
|
||||
import { Platform, Text, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import ButtonBackHeader from './buttonBackHeader';
|
||||
@@ -15,13 +14,12 @@ type Props = {
|
||||
|
||||
export default function AppHeader({ title, right, showBack = true, onPressLeft, left }: Props) {
|
||||
const insets = useSafeAreaInsets();
|
||||
const router = useRouter();
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<View style={[Styles.headerContainer, Platform.OS === 'ios' ? Styles.pb05 : Styles.pb13, { backgroundColor: colors.header, paddingTop: Platform.OS === 'ios' ? insets.top : 10 }]}>
|
||||
<View style={Styles.headerApp}>
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
<View style={[Styles.rowItemsCenter, Styles.flex1]}>
|
||||
{showBack ? (
|
||||
<ButtonBackHeader onPress={onPressLeft} />
|
||||
) :
|
||||
@@ -30,7 +28,9 @@ export default function AppHeader({ title, right, showBack = true, onPressLeft,
|
||||
<View style={Styles.headerSide} />
|
||||
)}
|
||||
|
||||
<Text style={[Styles.headerTitle, Styles.ml05]}>{title}</Text>
|
||||
<Text style={[Styles.headerTitle, Styles.ml05, Styles.flex1, Styles.mr10]} numberOfLines={1} ellipsizeMode="tail">
|
||||
{title ? title.charAt(0).toUpperCase() + title.slice(1) : ""}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={Styles.headerSide}>{right}</View>
|
||||
</View>
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function BorderBottomItem({ title, subtitle, icon, desc, onPress,
|
||||
{icon}
|
||||
<View style={[Styles.rowSpaceBetween, Styles.flex1]}>
|
||||
<View style={[Styles.ml10, Styles.flex1, Styles.mr10]}>
|
||||
<Text style={[titleWeight == 'normal' ? Styles.textDefault : Styles.textDefaultSemiBold, { color: textColorFix }]} numberOfLines={titleShowAll ? 0 : 1} ellipsizeMode='tail'>{title}</Text>
|
||||
<Text style={[titleWeight == 'normal' ? Styles.textDefault : Styles.textDefaultSemiBold, { color: textColorFix }]} numberOfLines={titleShowAll ? 0 : 1} ellipsizeMode='tail'>{title ? title.charAt(0).toUpperCase() + title.slice(1) : ""}</Text>
|
||||
{
|
||||
subtitle &&
|
||||
typeof subtitle == "string"
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function DiscussionItem({ title, user, date, onPress }: Props) {
|
||||
<View style={[Styles.rowItemsCenter, Styles.mb10]}>
|
||||
<Ionicons name="chatbox-ellipses-outline" size={22} color={colors.text} style={Styles.mr10} />
|
||||
<View style={[{ flex: 1 }]}>
|
||||
<Text style={{ fontWeight: 'bold' }} numberOfLines={1} ellipsizeMode="tail">{title}</Text>
|
||||
<Text style={{ fontWeight: 'bold' }} numberOfLines={1} ellipsizeMode="tail">{title?.charAt(0).toUpperCase() + title?.slice(1)}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={[Styles.rowSpaceBetween]}>
|
||||
|
||||
@@ -18,7 +18,7 @@ type Props = {
|
||||
export default function ItemFile({ category, checked, dateTime, title, onChecked, onPress, canChecked }: Props) {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<View style={[Styles.wrapItemBorderBottom, { borderColor: colors.background }]}>
|
||||
<View style={[Styles.wrapItemBorderBottom, { borderColor: colors.icon + '20' }]}>
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
<Pressable onPress={onPress}>
|
||||
{
|
||||
@@ -56,8 +56,8 @@ export default function ItemFile({ category, checked, dateTime, title, onChecked
|
||||
<Pressable onPress={onChecked}>
|
||||
{
|
||||
checked
|
||||
? <MaterialCommunityIcons name="checkbox-marked-circle" size={25} color={colors.text} />
|
||||
: <MaterialCommunityIcons name="checkbox-blank-circle-outline" size={25} color={colors.icon} />
|
||||
? <MaterialCommunityIcons name="checkbox-marked-circle" size={25} color={colors.icon} />
|
||||
: <MaterialCommunityIcons name="checkbox-blank-circle-outline" size={25} color={colors.icon + '90'} />
|
||||
}
|
||||
|
||||
</Pressable>
|
||||
|
||||
@@ -63,7 +63,7 @@ export default function DivisionHome({ refreshing }: { refreshing: boolean }) {
|
||||
<Pressable style={[Styles.wrapPaper, Styles.mb05, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]} key={index} onPress={() => { router.push(`/division/${item.id}`) }}>
|
||||
<View style={[Styles.rowSpaceBetween, { alignItems: 'center' }]}>
|
||||
<View>
|
||||
<Text style={[Styles.textDefaultSemiBold]}>{item.name}</Text>
|
||||
<Text style={[Styles.textDefaultSemiBold]}>{item.name?.charAt(0).toUpperCase() + item.name?.slice(1)}</Text>
|
||||
<Text style={[Styles.textDefault]}>{item.jumlah} Kegiatan</Text>
|
||||
</View>
|
||||
<Feather name="chevron-right" size={20} color={colors.text} />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetDataHome } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { router } from "expo-router";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Dimensions, View } from "react-native";
|
||||
@@ -10,7 +11,6 @@ import PaperGridContent from "../paperGridContent";
|
||||
import ProgressBar from "../progressBar";
|
||||
import Skeleton from "../skeleton";
|
||||
import Text from "../Text";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
|
||||
type Props = {
|
||||
id: string
|
||||
@@ -52,7 +52,7 @@ export default function ProjectHome({ refreshing }: { refreshing: boolean }) {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View style={[Styles.mb15]}>
|
||||
<View style={[Styles.mb05]}>
|
||||
<Text style={[Styles.textDefaultSemiBold, Styles.mb10]}>Kegiatan Terupdate</Text>
|
||||
{
|
||||
loading ? (<Skeleton width={100} height={150} borderRadius={10} widthType="percent" />)
|
||||
@@ -62,7 +62,7 @@ export default function ProjectHome({ refreshing }: { refreshing: boolean }) {
|
||||
ref={ref}
|
||||
style={{ width: "100%" }}
|
||||
width={width * 0.8}
|
||||
height={235}
|
||||
height={220}
|
||||
data={data}
|
||||
loop={false}
|
||||
autoPlay={false}
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function PaperGridContent({ content, children, title, headerColor
|
||||
const bgSource = activeTheme === 'light' ? bgLight : bgDark;
|
||||
return (
|
||||
<Pressable onPress={onPress}>
|
||||
<View style={[content == 'carousel' ? Styles.wrapGridCaraousel : Styles.wrapGridContent]}>
|
||||
<View style={[content == 'carousel' ? Styles.wrapGridCaraousel : Styles.wrapGridContent, { backgroundColor: colors.card }]}>
|
||||
{
|
||||
headerColor == 'warning' ? (
|
||||
<View style={[Styles.headerPaperGrid, ColorsStatus.warning]}>
|
||||
@@ -35,13 +35,12 @@ export default function PaperGridContent({ content, children, title, headerColor
|
||||
imageStyle={{ borderTopLeftRadius: 5, borderTopRightRadius: 5 }}
|
||||
style={[Styles.headerPaperGrid, { backgroundColor: colors.primary }]}
|
||||
>
|
||||
<Text numberOfLines={titleTail ? titleTail : undefined} style={[Styles.textSubtitle, Styles.cWhite, { textAlign: 'center' }]}>{title}</Text>
|
||||
<Text numberOfLines={titleTail ? titleTail : undefined} style={[Styles.textSubtitle, Styles.cWhite, { textAlign: 'center' }]}>{title.charAt(0).toUpperCase() + title.slice(1)}</Text>
|
||||
</ImageBackground>
|
||||
)
|
||||
}
|
||||
<View style={[
|
||||
contentPosition && contentPosition == 'top' ? Styles.contentPaperGrid2 : Styles.contentPaperGrid,
|
||||
{ backgroundColor: colors.card },
|
||||
height ? { height: height } : {}
|
||||
]}>
|
||||
{children}
|
||||
|
||||
@@ -392,10 +392,10 @@ const Styles = StyleSheet.create({
|
||||
},
|
||||
wrapGridContent: {
|
||||
shadowColor: '#171717',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 10,
|
||||
elevation: 5,
|
||||
shadowRadius: 5,
|
||||
elevation: 2,
|
||||
borderRadius: 5,
|
||||
marginBottom: 15
|
||||
},
|
||||
@@ -403,12 +403,13 @@ const Styles = StyleSheet.create({
|
||||
width: '95%',
|
||||
height: 200,
|
||||
shadowColor: '#171717',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 5,
|
||||
shadowRadius: 5,
|
||||
elevation: 2,
|
||||
borderRadius: 5,
|
||||
marginLeft: 5
|
||||
marginLeft: 5,
|
||||
display: 'flex',
|
||||
},
|
||||
headerPaperGrid: {
|
||||
paddingVertical: 25,
|
||||
@@ -418,15 +419,13 @@ const Styles = StyleSheet.create({
|
||||
borderTopEndRadius: 5
|
||||
},
|
||||
contentPaperGrid: {
|
||||
backgroundColor: 'white',
|
||||
height: 150,
|
||||
height: 125,
|
||||
borderBottomEndRadius: 5,
|
||||
borderBottomStartRadius: 5,
|
||||
paddingHorizontal: 20,
|
||||
justifyContent: 'space-evenly'
|
||||
},
|
||||
contentPaperGrid2: {
|
||||
backgroundColor: 'white',
|
||||
height: 100,
|
||||
borderBottomEndRadius: 5,
|
||||
borderBottomStartRadius: 5,
|
||||
@@ -455,8 +454,8 @@ const Styles = StyleSheet.create({
|
||||
shadowColor: '#171717',
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 5,
|
||||
shadowRadius: 5,
|
||||
elevation: 2,
|
||||
},
|
||||
noShadow: {
|
||||
shadowColor: 'transparent',
|
||||
@@ -469,8 +468,8 @@ const Styles = StyleSheet.create({
|
||||
shadowColor: '#171717',
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 5,
|
||||
shadowRadius: 5,
|
||||
elevation: 2,
|
||||
},
|
||||
contentItemCenter: {
|
||||
justifyContent: 'center',
|
||||
@@ -749,10 +748,10 @@ const Styles = StyleSheet.create({
|
||||
},
|
||||
wrapHomeCarousel: {
|
||||
shadowColor: '#171717',
|
||||
shadowOffset: { width: 0, height: 5 },
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 5,
|
||||
elevation: 50,
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.15,
|
||||
shadowRadius: 8,
|
||||
elevation: 5,
|
||||
},
|
||||
modalOverlay: {
|
||||
flex: 1,
|
||||
@@ -764,7 +763,7 @@ const Styles = StyleSheet.create({
|
||||
width: '80%',
|
||||
borderRadius: 14,
|
||||
overflow: 'hidden',
|
||||
elevation: 5,
|
||||
elevation: 2,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.25,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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