diff --git a/app/(application)/_layout.tsx b/app/(application)/_layout.tsx
index 57cced1..5d77e94 100644
--- a/app/(application)/_layout.tsx
+++ b/app/(application)/_layout.tsx
@@ -112,6 +112,18 @@ export default function RootLayout() {
)
}} />
+ { router.back() }} />,
+ title: 'Pengaturan',
+ headerTitleAlign: 'center',
+ // headerRight: () =>
+ header: () => (
+ router.back()}
+ />
+ )
+ }} />
{ router.back() }} />,
title: 'Anggota',
diff --git a/app/(application)/edit-profile.tsx b/app/(application)/edit-profile.tsx
index 6b32dda..3157aa4 100644
--- a/app/(application)/edit-profile.tsx
+++ b/app/(application)/edit-profile.tsx
@@ -1,4 +1,4 @@
-import ButtonBackHeader from "@/components/buttonBackHeader";
+import AppHeader from "@/components/AppHeader";
import ButtonSaveHeader from "@/components/buttonSaveHeader";
import { InputForm } from "@/components/inputForm";
import ModalSelect from "@/components/modalSelect";
@@ -219,24 +219,40 @@ export default function EditProfile() {
(
- {
- router.back();
- }}
- />
- ),
+ // headerLeft: () => (
+ // {
+ // router.back();
+ // }}
+ // />
+ // ),
headerTitle: "Edit Profile",
headerTitleAlign: "center",
- headerRight: () => (
- {
- handleEdit()
- }}
+ header: () => (
+ router.back()}
+ right={
+ {
+ handleEdit()
+ }}
+ />
+ }
/>
- ),
+ )
+ // headerRight: () => (
+ // {
+ // handleEdit()
+ // }}
+ // />
+ // ),
}}
/>
state.entities)
const [error, setError] = useState(false)
const [preview, setPreview] = useState(false)
- const [showThemeModal, setShowThemeModal] = useState(false)
- const [showLogoutModal, setShowLogoutModal] = useState(false)
-
- const ThemeOption = ({ label, value, icon }: { label: string, value: 'light' | 'dark' | 'system', icon: string }) => (
- {
- setTheme(value);
- setShowThemeModal(false);
- }}
- >
-
-
- {label}
-
- {theme === value && }
-
- );
return (
@@ -54,9 +33,9 @@ export default function Profile() {
onPressLeft={() => router.back()}
right={
}
+ item={}
onPress={() => {
- setShowLogoutModal(true)
+ router.push('/setting')
}}
/>
}
@@ -81,31 +60,8 @@ export default function Profile() {
{entities.role}
-
- Tampilan
-
-
- setShowThemeModal(true)}
- style={[Styles.wrapItemBorderAll, { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderColor: colors.icon + '40', backgroundColor: colors.background }]}
- >
-
-
- Tema Aplikasi
-
-
-
- {theme === 'light' ? 'Terang' : theme === 'dark' ? 'Gelap' : 'Sistem'}
-
-
-
-
-
-
+
Informasi
- {
- entities.idUserRole != "developer" && { router.push('/edit-profile') }} style={[Styles.textLink]}>Edit
- }
{/* Note: ItemDetailMember might need updates to support dynamic colors if it uses default text colors */}
@@ -118,29 +74,6 @@ export default function Profile() {
- setShowThemeModal(false)}
- >
- setShowThemeModal(false)}>
-
-
- Pilih Tema
- setShowThemeModal(false)}>
-
-
-
-
-
-
-
-
-
-
-
-
setPreview(false)}
doubleTapToZoomEnabled
/>
-
- {
- setShowLogoutModal(false)
- signOut()
- }}
- onCancel={() => setShowLogoutModal(false)}
- confirmText="Keluar"
- cancelText="Batal"
- />
)
}
\ No newline at end of file
diff --git a/app/(application)/setting/index.tsx b/app/(application)/setting/index.tsx
new file mode 100644
index 0000000..0a25d8e
--- /dev/null
+++ b/app/(application)/setting/index.tsx
@@ -0,0 +1,186 @@
+import ModalConfirmation from "@/components/ModalConfirmation";
+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 { useAuthSession } from "@/providers/AuthProvider";
+import { useTheme } from "@/providers/ThemeProvider";
+import { Feather, Ionicons } from "@expo/vector-icons";
+import { router } from "expo-router";
+import { useCallback, useEffect, useState } from "react";
+import { AppState, AppStateStatus, Pressable, View } from "react-native";
+import { useSelector } from "react-redux";
+
+export default function ListSetting() {
+ const { theme, setTheme, colors } = useTheme()
+ const { signOut } = useAuthSession()
+ const [isNotificationEnabled, setIsNotificationEnabled] = useState(null);
+ const entities = useSelector((state: any) => state.entities)
+ const [modalVisible, setModalVisible] = useState(false);
+ const [modalConfig, setModalConfig] = useState({
+ title: '',
+ message: '',
+ confirmText: 'Buka Pengaturan',
+ onConfirm: () => { }
+ });
+
+ const [showLogoutModal, setShowLogoutModal] = useState(false)
+ const [showThemeModal, setShowThemeModal] = useState(false)
+
+ const registerToken = async () => {
+ try {
+ const token = await getToken();
+ if (token) {
+ await apiRegisteredToken({ user: entities.id, token });
+ }
+ } catch (error) {
+ console.warn('Error registering token:', error);
+ }
+ };
+
+ const unregisterToken = async () => {
+ try {
+ const token = await getToken();
+ if (token) {
+ await apiUnregisteredToken({ user: entities.id, token });
+ }
+ } catch (error) {
+ console.warn('Error unregistering token:', error);
+ }
+ };
+
+ const checkNotif = useCallback(async () => {
+ const status = await checkPermission();
+ setIsNotificationEnabled((prev) => {
+ if (prev === false && status === true) {
+ registerToken();
+ } else if (prev === true && status === false) {
+ unregisterToken();
+ }
+ return !!status;
+ });
+ }, [entities.id]);
+
+ useEffect(() => {
+ checkNotif();
+
+ const subscription = AppState.addEventListener('change', (nextAppState: AppStateStatus) => {
+ if (nextAppState === 'active') {
+ checkNotif();
+ }
+ });
+
+ return () => {
+ subscription.remove();
+ };
+ }, [checkNotif]);
+
+ const handleToggleNotif = async () => {
+ if (isNotificationEnabled) {
+ setModalConfig({
+ title: "Matikan Notifikasi?",
+ message: "Anda akan diarahkan ke pengaturan sistem untuk mematikan notifikasi.",
+ confirmText: "Buka Pengaturan",
+ onConfirm: () => {
+ setModalVisible(false);
+ openSettings();
+ }
+ });
+ setModalVisible(true);
+ } else {
+ const granted = await requestPermission();
+ if (granted) {
+ setIsNotificationEnabled(true);
+ 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);
+ }
+ }
+ };
+
+ const ThemeOption = ({ label, value, icon }: { label: string, value: 'light' | 'dark' | 'system', icon: string }) => (
+ {
+ setTheme(value);
+ }}
+ >
+
+
+ {label}
+
+ {theme === value && }
+
+ );
+
+ return (
+
+
+ {
+ entities.idUserRole != "developer" &&
+ }
+ onPress={() => { router.push('/edit-profile') }}
+ />
+ }
+ }
+ onPress={() => setShowThemeModal(true)}
+ value={theme === 'light' ? 'Terang' : theme === 'dark' ? 'Gelap' : 'Sistem'}
+ />
+ }
+ onPress={handleToggleNotif}
+ value={isNotificationEnabled === null ? 'Memuat...' : isNotificationEnabled ? 'Aktif' : 'Nonaktif'}
+ />
+ }
+ onPress={() => setShowLogoutModal(true)}
+ borderBottom={false}
+ />
+
+
+ setModalVisible(false)}
+ />
+
+ {
+ setShowLogoutModal(false)
+ signOut()
+ }}
+ onCancel={() => setShowLogoutModal(false)}
+ confirmText="Keluar"
+ cancelText="Batal"
+ />
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/components/buttonSetting.tsx b/components/buttonSetting.tsx
new file mode 100644
index 0000000..419b227
--- /dev/null
+++ b/components/buttonSetting.tsx
@@ -0,0 +1,33 @@
+import Styles from "@/constants/Styles";
+import { useTheme } from "@/providers/ThemeProvider";
+import { ReactNode } from "react";
+import { Pressable, View } from "react-native";
+import Text from "./Text";
+
+type Props = {
+ title: string
+ onPress?: () => void,
+ icon?: ReactNode,
+ borderBottom?: boolean
+ value?: string
+}
+
+export default function ButtonSetting({ title, onPress, icon, borderBottom = true, value }: Props) {
+ const { colors } = useTheme();
+ return (
+
+
+
+ {icon}
+ {title}
+
+ {value && {value}}
+
+
+ )
+
+}
\ No newline at end of file
diff --git a/lib/useNotification.ts b/lib/useNotification.ts
index 4af6e44..7a9ac7c 100644
--- a/lib/useNotification.ts
+++ b/lib/useNotification.ts
@@ -6,8 +6,7 @@ import {
} from '@react-native-firebase/messaging';
import * as Notifications from 'expo-notifications';
import { useEffect } from 'react';
-import { PermissionsAndroid, Platform } from 'react-native';
-
+import { Linking, PermissionsAndroid, Platform } from 'react-native';
import { ConstEnv } from '@/constants/ConstEnv';
const RNfirebaseConfig = {
@@ -38,6 +37,30 @@ 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';
+ }
+ } catch (err) {
+ console.warn('Error checking notification permissions:', err);
+ return false;
+ }
+};
+
+export const openSettings = () => {
+ if (Platform.OS === 'ios') {
+ Linking.openURL('app-settings:');
+ } else {
+ Linking.openSettings();
+ }
+};
+
export const requestPermission = async () => {
try {
if (Platform.OS === 'android') {