upd: view file
Deskripsi: - view file pada pengumuman, diskusi divisi dan diskusi umum No Issues
This commit is contained in:
@@ -14,7 +14,7 @@ import { startActivityAsync } from 'expo-intent-launcher';
|
|||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||||
import * as Sharing from 'expo-sharing';
|
import * as Sharing from 'expo-sharing';
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Alert, Dimensions, Platform, Pressable, RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
|
import { Dimensions, Platform, Pressable, RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
|
||||||
import ImageViewing from 'react-native-image-viewing';
|
import ImageViewing from 'react-native-image-viewing';
|
||||||
import * as mime from 'react-native-mime-types';
|
import * as mime from 'react-native-mime-types';
|
||||||
import RenderHTML from 'react-native-render-html';
|
import RenderHTML from 'react-native-render-html';
|
||||||
@@ -68,6 +68,7 @@ export default function DetailAnnouncement() {
|
|||||||
* Opens the image preview modal for the selected image file
|
* Opens the image preview modal for the selected image file
|
||||||
* @param item The file data object containing image information
|
* @param item The file data object containing image information
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function handleChooseFile(item: FileData) {
|
function handleChooseFile(item: FileData) {
|
||||||
setChooseFile(item)
|
setChooseFile(item)
|
||||||
setPreview(true)
|
setPreview(true)
|
||||||
@@ -156,11 +157,13 @@ export default function DetailAnnouncement() {
|
|||||||
}
|
}
|
||||||
} catch (openError) {
|
} catch (openError) {
|
||||||
console.error('Error opening file:', openError);
|
console.error('Error opening file:', openError);
|
||||||
Alert.alert('INFO', 'Tidak ada aplikasi yang dapat membuka file ini');
|
Toast.show({
|
||||||
|
type: 'error',
|
||||||
|
text1: 'Tidak ada aplikasi yang dapat membuka file ini'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error downloading or opening file:', error);
|
console.error('Error downloading or opening file:', error);
|
||||||
Alert.alert('INFO', 'Gagal mengunduh atau membuka file');
|
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
text1: 'Gagal membuka file',
|
text1: 'Gagal membuka file',
|
||||||
|
|||||||
@@ -80,8 +80,6 @@ export default function DetailDiscussionGeneral() {
|
|||||||
})
|
})
|
||||||
const [viewEdit, setViewEdit] = useState(false)
|
const [viewEdit, setViewEdit] = useState(false)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onValueChange = reference.on('value', snapshot => {
|
const onValueChange = reference.on('value', snapshot => {
|
||||||
if (snapshot.val() == null) {
|
if (snapshot.val() == null) {
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||||
import { ConstEnv } from "@/constants/ConstEnv";
|
import { ConstEnv } from "@/constants/ConstEnv";
|
||||||
|
import { isImageFile } from "@/constants/FileExtensions";
|
||||||
import Styles from "@/constants/Styles";
|
import Styles from "@/constants/Styles";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||||
import * as FileSystem from 'expo-file-system';
|
import * as FileSystem from 'expo-file-system';
|
||||||
import { startActivityAsync } from 'expo-intent-launcher';
|
import { startActivityAsync } from 'expo-intent-launcher';
|
||||||
import * as Sharing from 'expo-sharing';
|
import * as Sharing from 'expo-sharing';
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Alert, Dimensions, Platform, Pressable, View } from "react-native";
|
import { Dimensions, Platform, Pressable, View } from "react-native";
|
||||||
import { ScrollView } from "react-native-gesture-handler";
|
import { ScrollView } from "react-native-gesture-handler";
|
||||||
|
import ImageViewing from "react-native-image-viewing";
|
||||||
import * as mime from 'react-native-mime-types';
|
import * as mime from 'react-native-mime-types';
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
import Text from "./Text";
|
import Text from "./Text";
|
||||||
|
|
||||||
|
|
||||||
@@ -33,26 +36,47 @@ type Props = {
|
|||||||
dataFile: { id: string; idStorage: string; name: string; extension: string }[]
|
dataFile: { id: string; idStorage: string; name: string; extension: string }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PropsFile = {
|
||||||
|
id: string;
|
||||||
|
idStorage: string;
|
||||||
|
name: string;
|
||||||
|
extension: string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function BorderBottomItem2({ title, subtitle, icon, desc, onPress, onLongPress, rightTopInfo, borderType, leftBottomInfo, rightBottomInfo, titleWeight, bgColor, width, descEllipsize, textColor, colorPress, titleShowAll, dataFile }: Props) {
|
export default function BorderBottomItem2({ title, subtitle, icon, desc, onPress, onLongPress, rightTopInfo, borderType, leftBottomInfo, rightBottomInfo, titleWeight, bgColor, width, descEllipsize, textColor, colorPress, titleShowAll, dataFile }: Props) {
|
||||||
const lebarDim = Dimensions.get("window").width;
|
const lebarDim = Dimensions.get("window").width;
|
||||||
const lebar = width ? lebarDim * width / 100 : 'auto';
|
const lebar = width ? lebarDim * width / 100 : 'auto';
|
||||||
const textColorFix = textColor ? textColor : 'black';
|
const textColorFix = textColor ? textColor : 'black';
|
||||||
const [isTap, setIsTap] = useState(false);
|
const [isTap, setIsTap] = useState(false);
|
||||||
const [loadingOpen, setLoadingOpen] = useState(false)
|
const [loadingOpen, setLoadingOpen] = useState(false)
|
||||||
|
const [chooseFile, setChooseFile] = useState<PropsFile>()
|
||||||
|
const [preview, setPreview] = useState(false)
|
||||||
|
|
||||||
|
function handleChooseFile(item: PropsFile) {
|
||||||
|
setChooseFile(item)
|
||||||
|
setPreview(true)
|
||||||
|
}
|
||||||
|
|
||||||
const openFile = (item: { idStorage: string; name: string; extension: string }) => {
|
const openFile = async (item: PropsFile) => {
|
||||||
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 {
|
try {
|
||||||
if (Platform.OS == 'android') {
|
setLoadingOpen(true);
|
||||||
|
const remoteUrl = ConstEnv.url_storage + '/files/' + item.idStorage;
|
||||||
|
const fileName = item.name + '.' + item.extension;
|
||||||
|
const localPath = `${FileSystem.documentDirectory}/${fileName}`;
|
||||||
|
const mimeType = mime.lookup(fileName);
|
||||||
|
|
||||||
|
// Download the file
|
||||||
|
const downloadResult = await FileSystem.downloadAsync(remoteUrl, localPath);
|
||||||
|
|
||||||
|
if (downloadResult.status !== 200) {
|
||||||
|
throw new Error(`Download failed with status ${downloadResult.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentURL = await FileSystem.getContentUriAsync(downloadResult.uri);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (Platform.OS === 'android') {
|
||||||
await startActivityAsync(
|
await startActivityAsync(
|
||||||
'android.intent.action.VIEW',
|
'android.intent.action.VIEW',
|
||||||
{
|
{
|
||||||
@@ -61,19 +85,31 @@ export default function BorderBottomItem2({ title, subtitle, icon, desc, onPress
|
|||||||
type: mimeType as string,
|
type: mimeType as string,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else if (Platform.OS == 'ios') {
|
} else if (Platform.OS === 'ios') {
|
||||||
Sharing.shareAsync(localPath);
|
await Sharing.shareAsync(localPath);
|
||||||
|
}
|
||||||
|
} catch (openError) {
|
||||||
|
console.error('Error opening file:', openError);
|
||||||
|
Toast.show({
|
||||||
|
type: 'error',
|
||||||
|
text1: 'Tidak ada aplikasi yang dapat membuka file ini'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Alert.alert('INFO', 'Gagal membuka file, tidak ada aplikasi yang dapat membuka file ini');
|
console.error('Error downloading or opening file:', error);
|
||||||
} finally {
|
Toast.show({
|
||||||
if (Platform.OS == 'android') setLoadingOpen(false)
|
type: 'error',
|
||||||
}
|
text1: 'Gagal membuka file',
|
||||||
|
text2: 'Silakan coba lagi nanti'
|
||||||
});
|
});
|
||||||
|
} finally {
|
||||||
|
setLoadingOpen(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Pressable onLongPress={onLongPress} onPress={onPress}
|
<Pressable onLongPress={onLongPress} onPress={onPress}
|
||||||
onPressIn={() => setIsTap(true)}
|
onPressIn={() => setIsTap(true)}
|
||||||
onPressOut={() => setIsTap(false)}
|
onPressOut={() => setIsTap(false)}
|
||||||
@@ -116,10 +152,17 @@ export default function BorderBottomItem2({ title, subtitle, icon, desc, onPress
|
|||||||
<Pressable
|
<Pressable
|
||||||
key={index}
|
key={index}
|
||||||
style={[Styles.rowItemsCenter, Styles.borderAll, Styles.round10, Styles.ph05, Styles.pv03, Styles.mr05]}
|
style={[Styles.rowItemsCenter, Styles.borderAll, Styles.round10, Styles.ph05, Styles.pv03, Styles.mr05]}
|
||||||
onPress={() => { openFile({ idStorage: item.idStorage, name: item.name, extension: item.extension }) }}
|
onPress={() => {
|
||||||
|
isImageFile(item.extension) ?
|
||||||
|
handleChooseFile(item)
|
||||||
|
: openFile(item)
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Ionicons name="document-text" size={18} color="grey" style={Styles.mr05} />
|
<MaterialCommunityIcons
|
||||||
<Text style={[Styles.textInformation, Styles.cGray, Styles.mb05]}>{item.name}.{item.extension}</Text>
|
name={isImageFile(item.extension) ? "file-image-outline" : "file-document-outline"}
|
||||||
|
size={18}
|
||||||
|
color="grey" />
|
||||||
|
<Text style={[Styles.textInformation, Styles.cGray]}>{item.name}.{item.extension}</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
))}
|
))}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
@@ -145,5 +188,45 @@ export default function BorderBottomItem2({ title, subtitle, icon, desc, onPress
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
</Pressable>
|
</Pressable>
|
||||||
|
|
||||||
|
<ImageViewing
|
||||||
|
images={[{ uri: `${ConstEnv.url_storage}/files/${chooseFile?.idStorage}` }]}
|
||||||
|
imageIndex={0}
|
||||||
|
visible={preview}
|
||||||
|
onRequestClose={() => setPreview(false)}
|
||||||
|
doubleTapToZoomEnabled
|
||||||
|
HeaderComponent={({ imageIndex }) => (
|
||||||
|
<View style={[Styles.headerModalViewImg]}>
|
||||||
|
{/* CLOSE */}
|
||||||
|
<Pressable
|
||||||
|
onPress={() => setPreview(false)}
|
||||||
|
accessibilityRole="button"
|
||||||
|
accessibilityLabel="Close image viewer"
|
||||||
|
>
|
||||||
|
<Text style={{ color: 'white', fontSize: 26 }}>✕</Text>
|
||||||
|
</Pressable>
|
||||||
|
|
||||||
|
{/* MENU */}
|
||||||
|
<Pressable
|
||||||
|
onPress={() => chooseFile && openFile(chooseFile)}
|
||||||
|
accessibilityRole="button"
|
||||||
|
accessibilityLabel="Download or share image"
|
||||||
|
disabled={loadingOpen}
|
||||||
|
>
|
||||||
|
<Text style={{ color: loadingOpen ? 'gray' : 'white', fontSize: 22 }}>⋯</Text>
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
FooterComponent={({ imageIndex }) => (
|
||||||
|
<View style={{
|
||||||
|
paddingBottom: 20,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
alignItems: 'center',
|
||||||
|
}}>
|
||||||
|
<Text style={{ color: 'white', fontSize: 16 }}>{chooseFile?.name}.{chooseFile?.extension}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user