Files
mobile-darmasaba/app/(application)/banner/index.tsx
amaliadwiy ccf8ee1caf upd: caching data
Deskripsi:
- update caching pada fitur utama -yg fitur divisi belom
2026-04-20 14:23:14 +08:00

253 lines
9.4 KiB
TypeScript

import styles from "@/components/AppHeader"
import AppHeader from "@/components/AppHeader"
import HeaderRightBannerList from "@/components/banner/headerBannerList"
import BorderBottomItem from "@/components/borderBottomItem"
import DrawerBottom from "@/components/drawerBottom"
import MenuItemRow from "@/components/menuItemRow"
import ModalConfirmation from "@/components/ModalConfirmation"
import ModalLoading from "@/components/modalLoading"
import Skeleton from "@/components/skeleton"
import Text from "@/components/Text"
import { ConstEnv } from "@/constants/ConstEnv"
import Styles from "@/constants/Styles"
import { apiDeleteBanner, apiGetBanner } from "@/lib/api"
import { setEntities } from "@/lib/bannerSlice"
import { useAuthSession } from "@/providers/AuthProvider"
import { useTheme } from "@/providers/ThemeProvider"
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons"
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
import * as FileSystem from 'expo-file-system'
import { startActivityAsync } from 'expo-intent-launcher'
import { router, Stack } from "expo-router"
import * as Sharing from 'expo-sharing'
import { useEffect, useState } from "react"
import { Alert, Image, Platform, RefreshControl, SafeAreaView, ScrollView, View } from "react-native"
import ImageViewing from 'react-native-image-viewing'
import * as mime from 'react-native-mime-types'
import Toast from "react-native-toast-message"
import { useDispatch, useSelector } from "react-redux"
type Props = {
id: string
title: string
extension: string
image: string
}
export default function BannerList() {
const { decryptToken, token } = useAuthSession()
const { colors } = useTheme()
const [isModal, setModal] = useState(false)
const entities = useSelector((state: any) => state.banner)
const [dataId, setDataId] = useState('')
const [selectFile, setSelectFile] = useState<Props | null>(null)
const dispatch = useDispatch()
const [refreshing, setRefreshing] = useState(false)
const [loadingOpen, setLoadingOpen] = useState(false)
const [viewImg, setViewImg] = useState(false)
const [showDeleteModal, setShowDeleteModal] = useState(false)
const queryClient = useQueryClient()
// 1. Fetching logic with useQuery
const { data: bannersRes, isLoading } = useQuery({
queryKey: ['banners'],
queryFn: async () => {
const hasil = await decryptToken(String(token?.current))
const response = await apiGetBanner({ user: hasil })
return response.data || []
},
enabled: !!token?.current,
staleTime: 0,
})
// Sync results with Redux
useEffect(() => {
if (bannersRes) {
dispatch(setEntities(bannersRes))
}
}, [bannersRes, dispatch])
// 2. Deletion logic with useMutation
const deleteMutation = useMutation({
mutationFn: async (id: string) => {
const hasil = await decryptToken(String(token?.current))
return await apiDeleteBanner({ user: hasil }, id)
},
onSuccess: () => {
Toast.show({ type: 'small', text1: 'Berhasil menghapus data' })
queryClient.invalidateQueries({ queryKey: ['banners'] })
},
onError: (error: any) => {
const message = error?.response?.data?.message || "Gagal menghapus data"
Toast.show({ type: 'small', text1: message })
}
})
const handleDeleteEntity = () => {
deleteMutation.mutate(dataId)
setModal(false)
};
const handleRefresh = async () => {
setRefreshing(true)
await queryClient.invalidateQueries({ queryKey: ['banners'] })
setRefreshing(false)
};
const openFile = () => {
setModal(false)
setLoadingOpen(true)
let remoteUrl = ConstEnv.url_storage + '/files/' + selectFile?.image;
const fileName = selectFile?.title + '.' + selectFile?.extension;
let localPath = `${FileSystem.documentDirectory}/${fileName}`;
const mimeType = mime.lookup(fileName)
FileSystem.downloadAsync(remoteUrl, localPath).then(async ({ uri }) => {
const contentURL = await FileSystem.getContentUriAsync(uri);
try {
if (Platform.OS == 'android') {
await startActivityAsync(
'android.intent.action.VIEW',
{
data: contentURL,
flags: 1,
type: mimeType as string,
}
);
} else if (Platform.OS == 'ios') {
Sharing.shareAsync(localPath);
}
} catch (error) {
Alert.alert('INFO', 'Gagal membuka file, tidak ada aplikasi yang dapat membuka file ini');
} finally {
setLoadingOpen(false)
}
});
};
return (
<SafeAreaView style={[Styles.flex1, { backgroundColor: colors.background }]}>
<Stack.Screen
options={{
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
headerTitle: 'Banner',
headerTitleAlign: 'center',
// headerRight: () => <HeaderRightBannerList />
header: () => (
<AppHeader
title="Banner"
showBack={true}
onPressLeft={() => router.back()}
right={
<HeaderRightBannerList />
}
/>
)
}}
/>
<ModalLoading isVisible={loadingOpen} setVisible={setLoadingOpen} />
<ScrollView
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={handleRefresh}
tintColor={colors.icon}
/>
}
style={[Styles.h100, { backgroundColor: colors.background }]}
>
<View style={[Styles.p15, Styles.mb100]}>
{
isLoading ? (
<>
<Skeleton width={100} height={150} borderRadius={10} widthType="percent" />
<Skeleton width={100} height={150} borderRadius={10} widthType="percent" />
<Skeleton width={100} height={150} borderRadius={10} widthType="percent" />
</>
) :
entities.length > 0 ?
entities.map((index: any, key: number) => (
<BorderBottomItem
key={key}
onPress={() => {
setDataId(index.id)
setSelectFile(index)
setModal(true)
}}
borderType="all"
icon={
<Image
source={{ uri: `${ConstEnv.url_storage}/files/${index.image}` }}
style={[Styles.imgListBanner]}
/>
}
title={index.title}
/>
))
:
<View style={[Styles.p15, Styles.mb100]}>
<Text style={[Styles.textDefault, Styles.textCenter]}>Tidak ada data</Text>
</View>
}
</View>
</ScrollView>
<DrawerBottom animation="slide" isVisible={isModal} setVisible={() => setModal(false)} title="Menu">
<View style={Styles.rowItemsCenter}>
<MenuItemRow
icon={<MaterialCommunityIcons name="pencil-outline" color={colors.text} size={25} />}
title="Edit"
onPress={() => {
setModal(false)
router.push(`/banner/${dataId}`)
}}
/>
<MenuItemRow
icon={<MaterialCommunityIcons name="file-eye" color={colors.text} size={25} />}
title="Lihat"
onPress={() => {
setModal(false)
setTimeout(() => {
setViewImg(true);
}, 1000);
// openFile()
}}
/>
<MenuItemRow
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
title="Hapus"
onPress={() => {
setModal(false)
setTimeout(() => {
setShowDeleteModal(true)
}, 600)
}}
/>
</View>
</DrawerBottom>
<ImageViewing
images={[{ uri: `${ConstEnv.url_storage}/files/${selectFile?.image}` }]}
imageIndex={0}
visible={viewImg}
onRequestClose={() => setViewImg(false)}
doubleTapToZoomEnabled
/>
<ModalConfirmation
visible={showDeleteModal}
title="Konfirmasi"
message="Apakah anda yakin ingin menghapus data?"
onConfirm={() => {
setShowDeleteModal(false)
handleDeleteEntity()
}}
onCancel={() => setShowDeleteModal(false)}
confirmText="Hapus"
cancelText="Batal"
/>
</SafeAreaView>
)
}