Files
mobile-darmasaba/app/(application)/announcement/[id].tsx
amaliadwiy 3fbb230302 req: pengumuman
Deskripsi:
- pengaplikasian api tambah, detail dan edit pengumuman

No Issues
2026-01-14 15:01:32 +08:00

227 lines
9.7 KiB
TypeScript

import HeaderRightAnnouncementDetail from "@/components/announcement/headerAnnouncementDetail";
import BorderBottomItem from "@/components/borderBottomItem";
import ButtonBackHeader from "@/components/buttonBackHeader";
import Skeleton from "@/components/skeleton";
import Text from '@/components/Text';
import { ConstEnv } from "@/constants/ConstEnv";
import Styles from "@/constants/Styles";
import { apiGetAnnouncementOne } from "@/lib/api";
import { useAuthSession } from "@/providers/AuthProvider";
import { Entypo, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons";
import * as FileSystem from 'expo-file-system';
import { startActivityAsync } from 'expo-intent-launcher';
import { router, Stack, useLocalSearchParams } from "expo-router";
import * as Sharing from 'expo-sharing';
import React, { useEffect, useState } from "react";
import { Alert, Dimensions, Platform, RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
import * as mime from 'react-native-mime-types';
import RenderHTML from 'react-native-render-html';
import Toast from "react-native-toast-message";
import { useSelector } from "react-redux";
type Props = {
id: string
title: string
desc: string
}
export default function DetailAnnouncement() {
const { id } = useLocalSearchParams<{ id: string }>();
const { token, decryptToken } = useAuthSession()
const [data, setData] = useState<Props>({ id: '', title: '', desc: '' })
const [dataMember, setDataMember] = useState<any>({})
const [dataFile, setDataFile] = useState<{ id:string; idStorage: string; name: string; extension: string }[]>([])
const update = useSelector((state: any) => state.announcementUpdate)
const entityUser = useSelector((state: any) => state.user)
const contentWidth = Dimensions.get('window').width
const [loading, setLoading] = useState(true)
const arrSkeleton = Array.from({ length: 2 }, (_, index) => index)
const [refreshing, setRefreshing] = useState(false)
const [loadingOpen, setLoadingOpen] = useState(false)
async function handleLoad(loading: boolean) {
try {
setLoading(loading)
const hasil = await decryptToken(String(token?.current))
const response = await apiGetAnnouncementOne({ id: id, user: hasil })
if (response.success) {
setData(response.data)
setDataMember(response.member)
setDataFile(response.file)
} else {
Toast.show({ type: 'small', text1: response.message })
}
} catch (error) {
console.error(error)
Toast.show({ type: 'small', text1: 'Gagal mengambil data' })
} finally {
setLoading(false)
}
}
useEffect(() => {
handleLoad(false)
}, [update])
useEffect(() => {
handleLoad(true)
}, [])
function hasHtmlTags(text: string) {
const htmlRegex = /<[a-z][\s\S]*>/i;
return htmlRegex.test(text);
};
const handleRefresh = async () => {
setRefreshing(true)
handleLoad(false)
await new Promise(resolve => setTimeout(resolve, 2000));
setRefreshing(false)
};
const openFile = (item: { idStorage: string; name: string; extension: string }) => {
if (Platform.OS == 'android') setLoadingOpen(true)
let remoteUrl = ConstEnv.url_storage + '/files/' + item.idStorage;
const fileName = item.name + '.' + item.extension;
let localPath = `${FileSystem.documentDirectory}/${fileName}`;
const mimeType = mime.lookup(fileName)
FileSystem.downloadAsync(remoteUrl, localPath).then(async ({ uri }) => {
const contentURL = await FileSystem.getContentUriAsync(uri);
setLoadingOpen(false)
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 {
if (Platform.OS == 'android') setLoadingOpen(false)
}
});
};
return (
<SafeAreaView>
<Stack.Screen
options={{
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
headerTitle: 'Pengumuman',
headerTitleAlign: 'center',
headerRight: () => entityUser.role != 'user' && entityUser.role != 'coadmin' ? <HeaderRightAnnouncementDetail id={id} /> : <></>,
}}
/>
<ScrollView
showsVerticalScrollIndicator={false}
style={[Styles.h100]}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={() => handleRefresh()}
/>
}
>
<View style={[Styles.p15]}>
<View style={[Styles.wrapPaper]}>
{
loading ?
<View>
<View style={[Styles.rowOnly]}>
<Skeleton width={30} height={30} borderRadius={10} />
<View style={[{ flex: 1 }, Styles.ph05]}>
<Skeleton width={100} widthType="percent" height={30} borderRadius={10} />
</View>
</View>
<Skeleton width={100} widthType="percent" height={10} borderRadius={10} />
<Skeleton width={100} widthType="percent" height={10} borderRadius={10} />
<Skeleton width={100} widthType="percent" height={10} borderRadius={10} />
</View>
:
<>
<View style={[Styles.rowItemsCenter, { alignItems: 'flex-start' }]}>
<MaterialIcons name="campaign" size={30} color="black" style={Styles.mr05} />
<Text style={[Styles.textDefaultSemiBold, Styles.w90]}>{data?.title}</Text>
</View>
<View style={[Styles.mt10]}>
{
hasHtmlTags(data?.desc) ?
<RenderHTML
contentWidth={contentWidth}
source={{ html: data?.desc }}
baseStyle={{ color: 'black' }}
/>
:
<Text>{data?.desc}</Text>
}
</View>
</>
}
</View>
{
dataFile.length > 0 && (
<View style={[Styles.wrapPaper, Styles.mt10]}>
<View style={[Styles.mb05]}>
<Text style={[Styles.textDefaultSemiBold]}>File</Text>
</View>
{dataFile.map((item, index) => (
<BorderBottomItem
key={index}
borderType="bottom"
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
title={item.name}
titleWeight="normal"
onPress={() => { openFile({ idStorage: item.idStorage, name: item.name, extension: item.extension }) }}
/>
))}
</View>
)
}
<View style={[Styles.wrapPaper, Styles.mt10]}>
{
loading ?
arrSkeleton.map((item, index) => {
return (
<View key={index}>
<Skeleton width={30} widthType="percent" height={10} borderRadius={10} />
<Skeleton width={100} widthType="percent" height={10} borderRadius={10} />
<Skeleton width={100} widthType="percent" height={10} borderRadius={10} />
</View>
)
})
:
Object.keys(dataMember).map((v: any, i: any) => {
return (
<View key={i} style={[Styles.mb05]}>
<Text style={[Styles.textDefaultSemiBold]}>{dataMember[v]?.[0].group}</Text>
{
dataMember[v].map((item: any, x: any) => {
return (
<View key={x} style={[Styles.rowItemsCenter, Styles.w90]}>
<Entypo name="dot-single" size={24} color="black" />
<Text style={[Styles.textDefault]} numberOfLines={1} ellipsizeMode='tail'>{item.division}</Text>
</View>
)
})
}
</View>
)
})
}
</View>
</View>
</ScrollView>
</SafeAreaView>
)
}