diff --git a/app/(application)/discussion/[id].tsx b/app/(application)/discussion/[id].tsx index ea1685f..bd80157 100644 --- a/app/(application)/discussion/[id].tsx +++ b/app/(application)/discussion/[id].tsx @@ -14,10 +14,10 @@ import { ColorsStatus } from "@/constants/ColorsStatus"; import { ConstEnv } from "@/constants/ConstEnv"; import { regexOnlySpacesOrEnter } from "@/constants/OnlySpaceOrEnter"; import Styles from "@/constants/Styles"; -import { apiGetDiscussionGeneralOne, apiSendDiscussionGeneralCommentar } from "@/lib/api"; +import { apiDeleteDiscussionGeneralCommentar, apiGetDiscussionGeneralOne, apiSendDiscussionGeneralCommentar, apiUpdateDiscussionGeneralCommentar } from "@/lib/api"; import { getDB } from "@/lib/firebaseDatabase"; import { useAuthSession } from "@/providers/AuthProvider"; -import { Ionicons, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"; +import { Feather, Ionicons, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"; import { ref } from '@react-native-firebase/database'; import { useHeaderHeight } from '@react-navigation/elements'; import { router, Stack, useLocalSearchParams } from "expo-router"; @@ -49,6 +49,7 @@ type PropsKomentar = { export default function DetailDiscussionGeneral() { const { token, decryptToken } = useAuthSession() const entityUser = useSelector((state: any) => state.user) + const entities = useSelector((state: any) => state.entities) const { id } = useLocalSearchParams<{ id: string }>(); const [data, setData] = useState() const [dataKomentar, setDataKomentar] = useState([]) @@ -67,6 +68,8 @@ export default function DetailDiscussionGeneral() { id: '', comment: '' }) + const [viewEdit, setViewEdit] = useState(false) + useEffect(() => { @@ -138,7 +141,7 @@ export default function DetailDiscussionGeneral() { setKomentar('') updateTrigger() } else { - Toast.show({ type: 'small', text1: 'Gagal menambahkan komentar' }) + Toast.show({ type: 'small', text1: response.message }) } } } catch (error) { @@ -148,11 +151,52 @@ export default function DetailDiscussionGeneral() { } } + async function handleEditKomentar() { + try { + setLoadingSendKomentar(true) + const hasil = await decryptToken(String(token?.current)) + const response = await apiUpdateDiscussionGeneralCommentar({ id: selectKomentar.id, data: { desc: selectKomentar.comment, user: hasil } }) + if (response.success) { + updateTrigger() + } else { + Toast.show({ type: 'small', text1: response.message }) + } + } catch (error) { + console.error(error) + } finally { + setLoadingSendKomentar(false) + handleViewEditKomentar() + } + } + + async function handleDeleteKomentar() { + try { + setLoadingSendKomentar(true) + const hasil = await decryptToken(String(token?.current)) + const response = await apiDeleteDiscussionGeneralCommentar({ id: selectKomentar.id, data: { user: hasil } }) + if (response.success) { + updateTrigger() + } else { + Toast.show({ type: 'small', text1: response.message }) + } + } catch (error) { + console.error(error) + } finally { + setLoadingSendKomentar(false) + setVisible(false) + } + } + function handleMenuKomentar(id: string, comment: string) { setSelectKomentar({ id, comment }) setVisible(true) } + function handleViewEditKomentar() { + setVisible(false) + setViewEdit(!viewEdit) + } + return ( <> } @@ -231,7 +276,7 @@ export default function DetailDiscussionGeneral() { }) }} onLongPress={() => { - handleMenuKomentar(item.id, item.comment) + item.idUser == entities.id && data?.status != 2 && data?.isActive && handleMenuKomentar(item.id, item.comment) }} /> ) @@ -248,40 +293,78 @@ export default function DetailDiscussionGeneral() { Styles.contentItemCenter, Styles.w100, { backgroundColor: "#f4f4f4" }, + viewEdit && Styles.borderTop ]}> { - data?.status != 2 && data?.isActive && ((entityUser.role != "user" && entityUser.role != "coadmin") || memberDiscussion) - ? - { - (!loadingSendKomentar && komentar != '' && !regexOnlySpacesOrEnter.test(komentar) && data?.status === 1 && data?.isActive && (memberDiscussion || (entityUser.role != "user" && entityUser.role != "coadmin"))) - && handleKomentar() - }} - style={[ - Platform.OS == 'android' && Styles.mb12, - ]} - > - + viewEdit ? + <> + + + + Edit Komentar + + handleViewEditKomentar()}> + - } - /> - : - - - { - data?.status == 2 ? "Diskusi telah ditutup" : data?.isActive == false ? "Diskusi telah diarsipkan" : "Hanya anggota diskusi yang dapat memberikan komentar" + + setSelectKomentar({ ...selectKomentar, comment: val })} + value={selectKomentar.comment} + multiline + focus={viewEdit} + itemRight={ + { + (!loadingSendKomentar && selectKomentar.comment != '' && !regexOnlySpacesOrEnter.test(selectKomentar.comment) && data?.status === 1 && data?.isActive && (memberDiscussion || (entityUser.role != "user" && entityUser.role != "coadmin"))) + && handleEditKomentar() + }} + style={[ + Platform.OS == 'android' && Styles.mb12, + ]} + > + + } - - + /> + + : + data?.status != 2 && data?.isActive && ((entityUser.role != "user" && entityUser.role != "coadmin") || memberDiscussion) + ? + { + (!loadingSendKomentar && komentar != '' && !regexOnlySpacesOrEnter.test(komentar) && data?.status === 1 && data?.isActive && (memberDiscussion || (entityUser.role != "user" && entityUser.role != "coadmin"))) + && handleKomentar() + }} + style={[ + Platform.OS == 'android' && Styles.mb12, + ]} + > + + + } + /> + : + + + { + data?.status == 2 ? "Diskusi telah ditutup" : data?.isActive == false ? "Diskusi telah diarsipkan" : "Hanya anggota diskusi yang dapat memberikan komentar" + } + + } @@ -293,9 +376,7 @@ export default function DetailDiscussionGeneral() { } title="Edit" - onPress={() => { - setVisible(false) - }} + onPress={() => { handleViewEditKomentar() }} /> } @@ -304,7 +385,9 @@ export default function DetailDiscussionGeneral() { AlertKonfirmasi({ title: 'Konfirmasi', desc: 'Apakah anda yakin ingin menghapus komentar?', - onPress: () => { setVisible(false) } + onPress: () => { + handleDeleteKomentar() + } }) }} /> diff --git a/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx b/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx index 764c855..1f02b58 100644 --- a/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx @@ -1,9 +1,12 @@ +import AlertKonfirmasi from "@/components/alertKonfirmasi"; import BorderBottomItem from "@/components/borderBottomItem"; import ButtonBackHeader from "@/components/buttonBackHeader"; import HeaderRightDiscussionDetail from "@/components/discussion/headerDiscussionDetail"; +import DrawerBottom from "@/components/drawerBottom"; import ImageUser from "@/components/imageNew"; import { InputForm } from "@/components/inputForm"; import LabelStatus from "@/components/labelStatus"; +import MenuItemRow from "@/components/menuItemRow"; import Skeleton from "@/components/skeleton"; import SkeletonContent from "@/components/skeletonContent"; import Text from "@/components/Text"; @@ -11,18 +14,21 @@ import { ConstEnv } from "@/constants/ConstEnv"; import { regexOnlySpacesOrEnter } from "@/constants/OnlySpaceOrEnter"; import Styles from "@/constants/Styles"; import { + apiDeleteDiscussionCommentar, + apiEditDiscussionCommentar, apiGetDiscussionOne, apiGetDivisionOneFeature, apiSendDiscussionCommentar, } from "@/lib/api"; import { getDB } from "@/lib/firebaseDatabase"; import { useAuthSession } from "@/providers/AuthProvider"; -import { Ionicons, MaterialIcons } from "@expo/vector-icons"; +import { Feather, Ionicons, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"; import { ref } from "@react-native-firebase/database"; import { useHeaderHeight } from '@react-navigation/elements'; import { router, Stack, useLocalSearchParams } from "expo-router"; import { useEffect, useState } from "react"; import { KeyboardAvoidingView, Platform, Pressable, RefreshControl, ScrollView, View } from "react-native"; +import Toast from "react-native-toast-message"; import { useSelector } from "react-redux"; type Props = { @@ -44,6 +50,9 @@ type PropsComment = { createdAt: string; username: string; img: string; + idUser: string; + isEdited: boolean; + updatedAt: string; }; export default function DiscussionDetail() { @@ -65,6 +74,14 @@ export default function DiscussionDetail() { const [refreshing, setRefreshing] = useState(false) const headerHeight = useHeaderHeight(); const [detailMore, setDetailMore] = useState([]) + const entities = useSelector((state: any) => state.entities) + const [isVisible, setVisible] = useState(false) + const [selectKomentar, setSelectKomentar] = useState({ + id: '', + comment: '' + }) + const [viewEdit, setViewEdit] = useState(false) + useEffect(() => { @@ -172,6 +189,59 @@ export default function DiscussionDetail() { } } + async function handleEditKomentar() { + try { + setLoadingSend(true); + const hasil = await decryptToken(String(token?.current)); + const response = await apiEditDiscussionCommentar({ + id: selectKomentar.id, + data: { comment: selectKomentar.comment, user: hasil }, + }); + if (response.success) { + updateTrigger() + } else { + Toast.show({ type: 'small', text1: response.message }) + } + } catch (error) { + console.error(error); + } finally { + setLoadingSend(false); + handleViewEditKomentar() + } + } + + async function handleDeleteKomentar() { + try { + setLoadingSend(true); + const hasil = await decryptToken(String(token?.current)); + const response = await apiDeleteDiscussionCommentar({ + id: selectKomentar.id, + data: { user: hasil }, + }); + if (response.success) { + updateTrigger() + } else { + Toast.show({ type: 'small', text1: response.message }) + } + } catch (error) { + console.error(error); + } finally { + setLoadingSend(false) + setVisible(false) + } + } + + function handleMenuKomentar(id: string, comment: string) { + setSelectKomentar({ id, comment }) + setVisible(true) + } + + + function handleViewEditKomentar() { + setVisible(false) + setViewEdit(!viewEdit) + } + const handleRefresh = async () => { setRefreshing(true) @@ -268,6 +338,7 @@ export default function DiscussionDetail() { { setDetailMore((prev: any) => { @@ -287,6 +359,9 @@ export default function DiscussionDetail() { } }) }} + onLongPress={() => { + item.idUser == entities.id && data?.status != 2 && data?.isActive && handleMenuKomentar(item.id, item.comment) + }} /> )) } @@ -303,73 +378,145 @@ export default function DiscussionDetail() { Styles.contentItemCenter, Styles.w100, { backgroundColor: "#f4f4f4" }, + viewEdit && Styles.borderTop ]} > { - data?.status != 2 && data?.isActive && ((entityUser.role != "user" && entityUser.role != "coadmin") || - isMemberDivision) - ? - { - komentar != "" && - !regexOnlySpacesOrEnter.test(komentar) && - !loadingSend && - data?.status != 2 && - data?.isActive && - (((entityUser.role == "user" || - entityUser.role == "coadmin") && - isMemberDivision) || - entityUser.role == "admin" || - entityUser.role == "supadmin" || - entityUser.role == "developer" || - entityUser.role == "cosupadmin") && - handleKomentar(); - }} - style={[ - Platform.OS == 'android' && Styles.mb12, - ]} - > - + viewEdit ? + <> + + + + Edit Komentar + + handleViewEditKomentar()}> + - } - /> - : - - - { - data?.status == 2 ? "Diskusi telah ditutup" : data?.isActive == false ? "Diskusi telah diarsipkan" : "Hanya anggota divisi yang dapat memberikan komentar" + + setSelectKomentar({ ...selectKomentar, comment: val })} + value={selectKomentar.comment} + itemRight={ + { + selectKomentar.comment != "" && + !regexOnlySpacesOrEnter.test(selectKomentar.comment) && + !loadingSend && + data?.status != 2 && + data?.isActive && + (((entityUser.role == "user" || + entityUser.role == "coadmin") && + isMemberDivision) || + entityUser.role == "admin" || + entityUser.role == "supadmin" || + entityUser.role == "developer" || + entityUser.role == "cosupadmin") && + handleEditKomentar(); + }} + style={[ + Platform.OS == 'android' && Styles.mb12, + ]} + > + + } - - + /> + + : + data?.status != 2 && data?.isActive && ((entityUser.role != "user" && entityUser.role != "coadmin") || + isMemberDivision) + ? + { + komentar != "" && + !regexOnlySpacesOrEnter.test(komentar) && + !loadingSend && + data?.status != 2 && + data?.isActive && + (((entityUser.role == "user" || + entityUser.role == "coadmin") && + isMemberDivision) || + entityUser.role == "admin" || + entityUser.role == "supadmin" || + entityUser.role == "developer" || + entityUser.role == "cosupadmin") && + handleKomentar(); + }} + style={[ + Platform.OS == 'android' && Styles.mb12, + ]} + > + + + } + /> + : + + + { + data?.status == 2 ? "Diskusi telah ditutup" : data?.isActive == false ? "Diskusi telah diarsipkan" : "Hanya anggota divisi yang dapat memberikan komentar" + } + + } - + + + } + title="Edit" + onPress={() => { handleViewEditKomentar() }} + /> + } + title="Hapus" + onPress={() => { + AlertKonfirmasi({ + title: 'Konfirmasi', + desc: 'Apakah anda yakin ingin menghapus komentar?', + onPress: () => { + handleDeleteKomentar() + } + }) + }} + /> + + ); } diff --git a/components/borderBottomItem.tsx b/components/borderBottomItem.tsx index 439900b..509b116 100644 --- a/components/borderBottomItem.tsx +++ b/components/borderBottomItem.tsx @@ -19,16 +19,28 @@ type Props = { bgColor?: 'white' | 'transparent' width?: number descEllipsize?: boolean - textColor?: string + textColor?: string, + colorPress?: boolean } -export default function BorderBottomItem({ title, subtitle, icon, desc, onPress, onLongPress, rightTopInfo, borderType, leftBottomInfo, rightBottomInfo, titleWeight, bgColor, width, descEllipsize, textColor }: Props) { +export default function BorderBottomItem({ title, subtitle, icon, desc, onPress, onLongPress, rightTopInfo, borderType, leftBottomInfo, rightBottomInfo, titleWeight, bgColor, width, descEllipsize, textColor, colorPress }: Props) { const lebarDim = Dimensions.get("window").width; const lebar = width ? lebarDim * width / 100 : 'auto'; const textColorFix = textColor ? textColor : 'black'; return ( - + [ + borderType == 'bottom' + ? Styles.wrapItemBorderBottom + : borderType == 'all' + ? Styles.wrapItemBorderAll + : Styles.wrapItemBorderNone, + bgColor && bgColor == 'white' && ColorsStatus.white, + // efek warna saat ditekan (sementara) + pressed && colorPress && ColorsStatus.pressedGray, + ]} + > {icon} diff --git a/components/inputForm.tsx b/components/inputForm.tsx index 71cba17..e7f0b42 100644 --- a/components/inputForm.tsx +++ b/components/inputForm.tsx @@ -20,10 +20,11 @@ type Props = { disable?: boolean multiline?: boolean mb?: boolean + focus?: boolean }; -export function InputForm({ label, value, placeholder, onChange, info, disable, error, errorText, required, itemLeft, itemRight, type, round, width, bg, multiline, mb = true }: Props) { +export function InputForm({ label, value, placeholder, onChange, info, disable, error, errorText, required, itemLeft, itemRight, type, round, width, bg, multiline, mb = true, focus }: Props) { const lebar = Dimensions.get("window").width; if (itemLeft != undefined || itemRight != undefined) { diff --git a/constants/ColorsStatus.ts b/constants/ColorsStatus.ts index a8f4a17..3a4b49d 100644 --- a/constants/ColorsStatus.ts +++ b/constants/ColorsStatus.ts @@ -31,5 +31,8 @@ export const ColorsStatus = { }, lightRed: { backgroundColor: '#ffcdcd' + }, + pressedGray: { + backgroundColor: '#e4e4e4' } } \ No newline at end of file diff --git a/lib/api.ts b/lib/api.ts index 958485c..7a92ff1 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -182,6 +182,16 @@ export const apiSendDiscussionGeneralCommentar = async ({ id, data }: { id: stri return response.data; }; +export const apiDeleteDiscussionGeneralCommentar = async ({ id, data }: { id: string, data: { user: string } }) => { + const response = await api.delete(`/mobile/discussion-general/${id}/comment`, { data }) + return response.data; +}; + +export const apiUpdateDiscussionGeneralCommentar = async ({ id, data }: { id: string, data: { desc: string, user: string } }) => { + const response = await api.put(`/mobile/discussion-general/${id}/comment`, data) + return response.data; +}; + export const apiDeleteMemberDiscussionGeneral = async (data: { user: string, idUser: string }, id: string) => { await api.delete(`mobile/discussion-general/${id}/member`, { data }).then(response => { @@ -445,6 +455,16 @@ export const apiSendDiscussionCommentar = async ({ data, id }: { data: { user: s return response.data; }; +export const apiEditDiscussionCommentar = async ({ data, id }: { data: { user: string, comment: string }, id: string }) => { + const response = await api.put(`/mobile/discussion/${id}/comment`, data) + return response.data; +}; + +export const apiDeleteDiscussionCommentar = async ({ data, id }: { data: { user: string }, id: string }) => { + const response = await api.delete(`/mobile/discussion/${id}/comment`, { data }) + return response.data; +}; + export const apiEditDiscussion = async ({ data, id }: { data: { user: string, desc: string }, id: string }) => { const response = await api.post(`/mobile/discussion/${id}`, data) return response.data;