Component
Add: - upload button : masih percobaan Utils: Add: - file validasi untuk upload file Pakage Add: - expo-document-picker - expo-file-system ## No Issue
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
@@ -11,6 +12,8 @@ import { Ionicons } from "@expo/vector-icons";
|
|||||||
import { router, Stack } from "expo-router";
|
import { router, Stack } from "expo-router";
|
||||||
import EventDetailScreen from "./double-scroll";
|
import EventDetailScreen from "./double-scroll";
|
||||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||||
|
import CustomUploadButton from "./upload-button";
|
||||||
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
|
|
||||||
const { width } = Dimensions.get("window");
|
const { width } = Dimensions.get("window");
|
||||||
|
|
||||||
@@ -117,14 +120,38 @@ const CustomTabNavigator = () => {
|
|||||||
|
|
||||||
const ActiveComponent = getActiveComponent();
|
const ActiveComponent = getActiveComponent();
|
||||||
|
|
||||||
|
const handleImageUpload = (file: any) => {
|
||||||
|
console.log("Gambar dipilih:", file);
|
||||||
|
// Upload ke server
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePdfOrPngUpload = (file: any) => {
|
||||||
|
console.log("PDF atau PNG dipilih:", file);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<SafeAreaView edges={["bottom"]} style={styles.container}>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
options={{
|
options={{
|
||||||
title: "Custom Tab Navigator",
|
title: "Custom Tab Navigator",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<EventDetailScreen />
|
<EventDetailScreen />
|
||||||
|
|
||||||
|
<CustomUploadButton
|
||||||
|
allowedExtensions={["jpeg", "png"]}
|
||||||
|
buttonTitle="Unggah Gambar (JPEG/PNG)"
|
||||||
|
onFileSelected={handleImageUpload}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Hanya PDF atau PNG */}
|
||||||
|
<CustomUploadButton
|
||||||
|
allowedExtensions={["pdf"]}
|
||||||
|
buttonTitle="Unggah PDF atau PNG"
|
||||||
|
onFileSelected={handlePdfOrPngUpload}
|
||||||
|
/>
|
||||||
|
</SafeAreaView>
|
||||||
</>
|
</>
|
||||||
// <View style={styles.container}>
|
// <View style={styles.container}>
|
||||||
// {/* Content Area */}
|
// {/* Content Area */}
|
||||||
|
|||||||
99
app/(application)/coba/upload-button.tsx
Normal file
99
app/(application)/coba/upload-button.tsx
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// components/CustomUploadButton.tsx
|
||||||
|
import React from 'react';
|
||||||
|
import { Button, Alert, View, StyleSheet } from 'react-native';
|
||||||
|
import * as DocumentPicker from 'expo-document-picker';
|
||||||
|
import { isValidFileType, getMimeType } from '../../../utils/fileValidation';
|
||||||
|
|
||||||
|
interface UploadButtonProps {
|
||||||
|
allowedExtensions: string[];
|
||||||
|
buttonTitle?: string;
|
||||||
|
onFileSelected?: (file: {
|
||||||
|
uri: string;
|
||||||
|
name: string;
|
||||||
|
size: number | null;
|
||||||
|
mimeType: string;
|
||||||
|
}) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomUploadButton: React.FC<UploadButtonProps> = ({
|
||||||
|
allowedExtensions,
|
||||||
|
buttonTitle = 'Pilih File',
|
||||||
|
onFileSelected,
|
||||||
|
}) => {
|
||||||
|
const handlePickFile = async () => {
|
||||||
|
try {
|
||||||
|
// Coba filter dengan MIME type jika memungkinkan
|
||||||
|
const typeFilter = getMimeTypeFilter(allowedExtensions);
|
||||||
|
|
||||||
|
const result = await DocumentPicker.getDocumentAsync({
|
||||||
|
type: typeFilter, // Ini membantu memfilter di UI pemilih
|
||||||
|
copyToCacheDirectory: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.canceled) {
|
||||||
|
Alert.alert('Dibatalkan', 'Tidak ada file yang dipilih.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = result.assets[0];
|
||||||
|
const { uri, name, size } = file;
|
||||||
|
|
||||||
|
// Validasi ekstensi secara manual (cadangan jika MIME tidak akurat)
|
||||||
|
if (!isValidFileType(name, allowedExtensions)) {
|
||||||
|
Alert.alert(
|
||||||
|
'Format Tidak Didukung',
|
||||||
|
`Hanya file dengan ekstensi berikut yang diperbolehkan: ${allowedExtensions.join(', ')}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mimeType = getMimeType(name);
|
||||||
|
|
||||||
|
// Kirim data file ke komponen induk
|
||||||
|
if (onFileSelected) {
|
||||||
|
onFileSelected({ uri, name, size: size || null, mimeType });
|
||||||
|
}
|
||||||
|
|
||||||
|
Alert.alert('Berhasil', `File ${name} berhasil dipilih!`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error picking file:', error);
|
||||||
|
Alert.alert('Error', 'Terjadi kesalahan saat memilih file.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Button title={buttonTitle} onPress={handlePickFile} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fungsi bantu untuk menghasilkan MIME type filter dari ekstensi
|
||||||
|
const getMimeTypeFilter = (extensions: string[]): string => {
|
||||||
|
const mimeTypes: string[] = [];
|
||||||
|
extensions.forEach((ext) => {
|
||||||
|
switch (ext.toLowerCase()) {
|
||||||
|
case 'jpg':
|
||||||
|
case 'jpeg':
|
||||||
|
if (!mimeTypes.includes('image/jpeg')) mimeTypes.push('image/jpeg');
|
||||||
|
break;
|
||||||
|
case 'png':
|
||||||
|
if (!mimeTypes.includes('image/png')) mimeTypes.push('image/png');
|
||||||
|
break;
|
||||||
|
case 'pdf':
|
||||||
|
if (!mimeTypes.includes('application/pdf')) mimeTypes.push('application/pdf');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mimeTypes.push('*/*'); // fallback
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return mimeTypes.length > 0 ? mimeTypes.join(',') : '*/*';
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
marginVertical: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default CustomUploadButton;
|
||||||
4
bun.lock
4
bun.lock
@@ -18,6 +18,8 @@
|
|||||||
"expo-camera": "~16.1.10",
|
"expo-camera": "~16.1.10",
|
||||||
"expo-clipboard": "~7.1.5",
|
"expo-clipboard": "~7.1.5",
|
||||||
"expo-constants": "~17.1.7",
|
"expo-constants": "~17.1.7",
|
||||||
|
"expo-document-picker": "~13.1.6",
|
||||||
|
"expo-file-system": "~18.1.11",
|
||||||
"expo-font": "~13.3.2",
|
"expo-font": "~13.3.2",
|
||||||
"expo-haptics": "~14.1.4",
|
"expo-haptics": "~14.1.4",
|
||||||
"expo-image": "~2.3.2",
|
"expo-image": "~2.3.2",
|
||||||
@@ -833,6 +835,8 @@
|
|||||||
|
|
||||||
"expo-constants": ["expo-constants@17.1.7", "", { "dependencies": { "@expo/config": "~11.0.12", "@expo/env": "~1.0.7" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-byBjGsJ6T6FrLlhOBxw4EaiMXrZEn/MlUYIj/JAd+FS7ll5X/S4qVRbIimSJtdW47hXMq0zxPfJX6njtA56hHA=="],
|
"expo-constants": ["expo-constants@17.1.7", "", { "dependencies": { "@expo/config": "~11.0.12", "@expo/env": "~1.0.7" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-byBjGsJ6T6FrLlhOBxw4EaiMXrZEn/MlUYIj/JAd+FS7ll5X/S4qVRbIimSJtdW47hXMq0zxPfJX6njtA56hHA=="],
|
||||||
|
|
||||||
|
"expo-document-picker": ["expo-document-picker@13.1.6", "", { "peerDependencies": { "expo": "*" } }, "sha512-8FTQPDOkyCvFN/i4xyqzH7ELW4AsB6B3XBZQjn1FEdqpozo6rpNJRr7sWFU/93WrLgA9FJEKpKbyr6XxczK6BA=="],
|
||||||
|
|
||||||
"expo-file-system": ["expo-file-system@18.1.11", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-HJw/m0nVOKeqeRjPjGdvm+zBi5/NxcdPf8M8P3G2JFvH5Z8vBWqVDic2O58jnT1OFEy0XXzoH9UqFu7cHg9DTQ=="],
|
"expo-file-system": ["expo-file-system@18.1.11", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-HJw/m0nVOKeqeRjPjGdvm+zBi5/NxcdPf8M8P3G2JFvH5Z8vBWqVDic2O58jnT1OFEy0XXzoH9UqFu7cHg9DTQ=="],
|
||||||
|
|
||||||
"expo-font": ["expo-font@13.3.2", "", { "dependencies": { "fontfaceobserver": "^2.1.0" }, "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-wUlMdpqURmQ/CNKK/+BIHkDA5nGjMqNlYmW0pJFXY/KE/OG80Qcavdu2sHsL4efAIiNGvYdBS10WztuQYU4X0A=="],
|
"expo-font": ["expo-font@13.3.2", "", { "dependencies": { "fontfaceobserver": "^2.1.0" }, "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-wUlMdpqURmQ/CNKK/+BIHkDA5nGjMqNlYmW0pJFXY/KE/OG80Qcavdu2sHsL4efAIiNGvYdBS10WztuQYU4X0A=="],
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
"expo-camera": "~16.1.10",
|
"expo-camera": "~16.1.10",
|
||||||
"expo-clipboard": "~7.1.5",
|
"expo-clipboard": "~7.1.5",
|
||||||
"expo-constants": "~17.1.7",
|
"expo-constants": "~17.1.7",
|
||||||
|
"expo-document-picker": "~13.1.6",
|
||||||
|
"expo-file-system": "~18.1.11",
|
||||||
"expo-font": "~13.3.2",
|
"expo-font": "~13.3.2",
|
||||||
"expo-haptics": "~14.1.4",
|
"expo-haptics": "~14.1.4",
|
||||||
"expo-image": "~2.3.2",
|
"expo-image": "~2.3.2",
|
||||||
|
|||||||
34
utils/fileValidation.ts
Normal file
34
utils/fileValidation.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// utils/fileValidation.ts
|
||||||
|
const ALLOWED_TYPES: Record<string, string[]> = {
|
||||||
|
image: ["jpeg", "jpg", "png"],
|
||||||
|
document: ["pdf", "png"],
|
||||||
|
pdf: ["pdf"],
|
||||||
|
png: ["png"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isValidFileType = (
|
||||||
|
fileName: string,
|
||||||
|
allowedExtensions: string[]
|
||||||
|
): boolean => {
|
||||||
|
const extension = fileName.split(".").pop()?.toLowerCase();
|
||||||
|
return !!extension && allowedExtensions.includes(extension);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper: Dapatkan MIME type berdasarkan ekstensi (opsional)
|
||||||
|
export const getMimeType = (fileName: string): string => {
|
||||||
|
const ext = fileName.split(".").pop()?.toLowerCase();
|
||||||
|
switch (ext) {
|
||||||
|
case "jpg":
|
||||||
|
return "image/jpg";
|
||||||
|
case "jpeg":
|
||||||
|
return "image/jpeg";
|
||||||
|
case "png":
|
||||||
|
return "image/png";
|
||||||
|
case "pdf":
|
||||||
|
return "application/pdf";
|
||||||
|
default:
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ALLOWED_TYPES;
|
||||||
Reference in New Issue
Block a user