upd: announcement dan upload gambar
Deskripsi - fix html tag pada pengumuman - testing upload gambar = blm selesai No Issues
This commit is contained in:
@@ -6,7 +6,8 @@ import { useAuthSession } from "@/providers/AuthProvider";
|
|||||||
import { AntDesign, Entypo, MaterialIcons } from "@expo/vector-icons";
|
import { AntDesign, Entypo, MaterialIcons } from "@expo/vector-icons";
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { SafeAreaView, ScrollView, Text, View } from "react-native";
|
import { Dimensions, SafeAreaView, ScrollView, Text, View } from "react-native";
|
||||||
|
import RenderHTML from 'react-native-render-html';
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -18,10 +19,11 @@ type Props = {
|
|||||||
export default function DetailAnnouncement() {
|
export default function DetailAnnouncement() {
|
||||||
const { id } = useLocalSearchParams<{ id: string }>();
|
const { id } = useLocalSearchParams<{ id: string }>();
|
||||||
const { token, decryptToken } = useAuthSession()
|
const { token, decryptToken } = useAuthSession()
|
||||||
const [data, setData] = useState<Props>()
|
const [data, setData] = useState<Props>({ id: '', title: '', desc: '' })
|
||||||
const [dataMember, setDataMember] = useState<any>({})
|
const [dataMember, setDataMember] = useState<any>({})
|
||||||
const update = useSelector((state: any) => state.announcementUpdate)
|
const update = useSelector((state: any) => state.announcementUpdate)
|
||||||
const entityUser = useSelector((state: any) => state.user)
|
const entityUser = useSelector((state: any) => state.user)
|
||||||
|
const contentWidth = Dimensions.get('window').width
|
||||||
|
|
||||||
async function handleLoad() {
|
async function handleLoad() {
|
||||||
try {
|
try {
|
||||||
@@ -55,9 +57,11 @@ export default function DetailAnnouncement() {
|
|||||||
<MaterialIcons name="campaign" size={30} color="black" style={Styles.mr05} />
|
<MaterialIcons name="campaign" size={30} color="black" style={Styles.mr05} />
|
||||||
<Text style={[Styles.textDefaultSemiBold]}>{data?.title}</Text>
|
<Text style={[Styles.textDefaultSemiBold]}>{data?.title}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={[Styles.rowItemsCenter, Styles.mt10]}>
|
<View style={[Styles.mt10]}>
|
||||||
<AntDesign name="profile" size={30} color="black" style={Styles.mr05} />
|
<RenderHTML
|
||||||
<Text style={[Styles.textDefault]}>{data?.desc}</Text>
|
contentWidth={contentWidth}
|
||||||
|
source={{ html: data?.desc }}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View style={[Styles.wrapPaper, Styles.mv15]}>
|
<View style={[Styles.wrapPaper, Styles.mv15]}>
|
||||||
@@ -71,7 +75,7 @@ export default function DetailAnnouncement() {
|
|||||||
return (
|
return (
|
||||||
<View key={x} style={[Styles.rowItemsCenter]}>
|
<View key={x} style={[Styles.rowItemsCenter]}>
|
||||||
<Entypo name="dot-single" size={24} color="black" />
|
<Entypo name="dot-single" size={24} color="black" />
|
||||||
<Text style={[Styles.textDefault]}>{item.division}</Text>
|
<Text style={[Styles.textDefault]} numberOfLines={1} ellipsizeMode='tail'>{item.division}</Text>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export default function Announcement() {
|
|||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
title={item.title}
|
title={item.title}
|
||||||
desc={item.desc}
|
desc={item.desc.replace(/<[^>]*>?/gm, '')}
|
||||||
rightTopInfo={item.createdAt}
|
rightTopInfo={item.createdAt}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { useDispatch } from "react-redux";
|
|||||||
import * as FileSystem from 'expo-file-system';
|
import * as FileSystem from 'expo-file-system';
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import ReactNativeBlobUtil from 'react-native-blob-util';
|
import ReactNativeBlobUtil from 'react-native-blob-util';
|
||||||
import FormData from 'form-data';
|
import { launchImageLibrary } from "react-native-image-picker";
|
||||||
|
|
||||||
const debug = true
|
const debug = true
|
||||||
|
|
||||||
@@ -151,52 +151,46 @@ const requestPermission = async () => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const base64ToBlob = (base64: string, mimeType: string) => {
|
||||||
|
const byteString = atob(base64);
|
||||||
|
const arrayBuffer = new ArrayBuffer(byteString.length);
|
||||||
|
const int8Array = new Uint8Array(arrayBuffer);
|
||||||
|
|
||||||
|
for (let i = 0; i < byteString.length; i++) {
|
||||||
|
int8Array[i] = byteString.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Blob([int8Array], { type: mimeType });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
async function kirim() {
|
async function kirim() {
|
||||||
const hasPermission = await requestPermission();
|
console.log("clicked");
|
||||||
|
const result = await launchImageLibrary({
|
||||||
if (!hasPermission) {
|
mediaType: "photo",
|
||||||
return;
|
quality: 1,
|
||||||
}
|
includeBase64: true,
|
||||||
|
|
||||||
let result = await ImagePicker.launchImageLibraryAsync({
|
|
||||||
mediaTypes: ImagePicker.MediaTypeOptions.Images,
|
|
||||||
aspect: [4, 3],
|
|
||||||
quality: 0.8,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.canceled) {
|
if (!result.assets || result.assets.length === 0) {
|
||||||
const uri = result.assets[0].uri;
|
console.log("No image selected");
|
||||||
console.log("Selected image URI:", uri);
|
return;
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
const base64Data = await ReactNativeBlobUtil.fs.readFile(uri, 'base64');
|
|
||||||
|
|
||||||
// Convert base64 to Blob
|
|
||||||
const blob = new Blob([base64Data], { type: 'image/jpeg' });
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('file', blob);
|
|
||||||
formData.append('name', 'bg.png');
|
|
||||||
|
|
||||||
|
|
||||||
console.log("Sending FormData with file URI...");
|
|
||||||
|
|
||||||
// Kirim request tanpa header Content-Type
|
|
||||||
const response = await fetch("http://10.0.2.2:3000/api/v2/test", {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData as any,
|
|
||||||
});
|
|
||||||
|
|
||||||
// console.log("Response status:", response.status);
|
|
||||||
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error('Error uploading image:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
const base64 = result.assets[0].base64!;
|
||||||
|
const mimeType = result.assets[0].type!; // e.g., 'image/jpeg'
|
||||||
|
|
||||||
|
const blob = base64ToBlob(base64, mimeType);
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", blob);
|
||||||
|
formData.append("name", "bip.png");
|
||||||
|
const res = await fetch("http://10.0.2.2:3000/api/v2/test", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
console.log(await res.text());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
|
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||||
import Styles from "@/constants/Styles";
|
import Styles from "@/constants/Styles";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Dimensions, Pressable, Text, View } from "react-native";
|
import { Dimensions, Pressable, Text, View } from "react-native";
|
||||||
import LabelStatus from "./labelStatus";
|
|
||||||
import { Feather } from "@expo/vector-icons";
|
|
||||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
title?: string
|
title?: string
|
||||||
@@ -46,7 +44,7 @@ export default function BorderBottomItem({ title, subtitle, icon, desc, onPress,
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
{desc && <Text style={[Styles.textDefault, Styles.mt05, { textAlign: 'justify' }]}>{desc}</Text>}
|
{desc && <Text style={[Styles.textDefault, Styles.mt05, { textAlign: 'justify' }]} numberOfLines={2} ellipsizeMode='tail'>{desc}</Text>}
|
||||||
{
|
{
|
||||||
(leftBottomInfo || rightBottomInfo) &&
|
(leftBottomInfo || rightBottomInfo) &&
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
"react-native-fs": "^2.20.0",
|
"react-native-fs": "^2.20.0",
|
||||||
"react-native-gesture-handler": "~2.20.2",
|
"react-native-gesture-handler": "~2.20.2",
|
||||||
"react-native-gifted-charts": "^1.4.57",
|
"react-native-gifted-charts": "^1.4.57",
|
||||||
|
"react-native-image-picker": "^8.2.1",
|
||||||
"react-native-modal": "^14.0.0-rc.1",
|
"react-native-modal": "^14.0.0-rc.1",
|
||||||
"react-native-reanimated": "~3.16.1",
|
"react-native-reanimated": "~3.16.1",
|
||||||
"react-native-reanimated-carousel": "^4.0.2",
|
"react-native-reanimated-carousel": "^4.0.2",
|
||||||
|
|||||||
Reference in New Issue
Block a user