From 4a10655582b517d338b6cd19b7c5e7b3853d18f3 Mon Sep 17 00:00:00 2001 From: amal Date: Tue, 15 Jul 2025 12:08:55 +0800 Subject: [PATCH] upd: pull to refresh Deskripsi: - list banner - lit grouop - list position - list member - list diskusi umum - list pengumuman - list project - list divisi - list tugas divisi - list diskusi divisi - list kalender divisi - list dokumen divisi No Issues --- app/(application)/announcement/index.tsx | 16 +++++- app/(application)/banner/index.tsx | 25 +++++++-- app/(application)/discussion/index.tsx | 16 +++++- .../[id]/(fitur-division)/calendar/index.tsx | 30 ++++++++--- .../(fitur-division)/discussion/index.tsx | 18 ++++++- .../[id]/(fitur-division)/document/index.tsx | 52 ++++++++++++------- .../[id]/(fitur-division)/task/index.tsx | 32 +++++++++--- app/(application)/division/index.tsx | 32 +++++++++--- app/(application)/group/index.tsx | 19 +++++-- app/(application)/member/index.tsx | 17 ++++-- app/(application)/position/index.tsx | 17 +++++- app/(application)/project/index.tsx | 38 ++++++++++---- 12 files changed, 249 insertions(+), 63 deletions(-) diff --git a/app/(application)/announcement/index.tsx b/app/(application)/announcement/index.tsx index b0e624c..80eca00 100644 --- a/app/(application)/announcement/index.tsx +++ b/app/(application)/announcement/index.tsx @@ -8,7 +8,7 @@ import { useAuthSession } from "@/providers/AuthProvider"; import { MaterialIcons } from "@expo/vector-icons"; import { router } from "expo-router"; import { useEffect, useState } from "react"; -import { Text, View, VirtualizedList } from "react-native"; +import { RefreshControl, Text, View, VirtualizedList } from "react-native"; import { useSelector } from "react-redux"; type Props = { @@ -28,6 +28,7 @@ export default function Announcement() { const arrSkeleton = Array.from({ length: 5 }, (_, index) => index) const [page, setPage] = useState(1) const [waiting, setWaiting] = useState(false) + const [refreshing, setRefreshing] = useState(false) async function handleLoad(loading: boolean, thisPage: number) { try { @@ -66,6 +67,13 @@ export default function Announcement() { }, 1000); }; + const handleRefresh = async () => { + setRefreshing(true) + handleLoad(false, 1) + await new Promise(resolve => setTimeout(resolve, 2000)); + setRefreshing(false) + }; + const getItem = (_data: unknown, index: number): Props => ({ id: data[index].id, title: data[index].title, @@ -114,6 +122,12 @@ export default function Announcement() { onEndReached={loadMoreData} onEndReachedThreshold={0.5} showsVerticalScrollIndicator={false} + refreshControl={ + + } /> : Tidak ada pengumuman diff --git a/app/(application)/banner/index.tsx b/app/(application)/banner/index.tsx index ce4f8a1..4459992 100644 --- a/app/(application)/banner/index.tsx +++ b/app/(application)/banner/index.tsx @@ -11,15 +11,16 @@ import { useAuthSession } from "@/providers/AuthProvider" import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons" import { router, Stack } from "expo-router" import { useState } from "react" -import { Image, SafeAreaView, ScrollView, ToastAndroid, View } from "react-native" +import { Image, RefreshControl, SafeAreaView, ScrollView, ToastAndroid, View } from "react-native" import { useDispatch, useSelector } from "react-redux" export default function BannerList() { - const { decryptToken, token } = useAuthSession(); + const { decryptToken, token } = useAuthSession() const [isModal, setModal] = useState(false) const entities = useSelector((state: any) => state.banner) const [dataId, setDataId] = useState('') - const dispatch = useDispatch(); + const dispatch = useDispatch() + const [refreshing, setRefreshing] = useState(false) const handleDeleteEntity = async () => { try { @@ -39,7 +40,16 @@ export default function BannerList() { } finally { setModal(false) } + }; + const handleRefresh = async () => { + setRefreshing(true) + const hasil = await decryptToken(String(token?.current)); + apiGetBanner({ user: hasil }).then((data) => + dispatch(setEntities(data.data)) + ); + await new Promise(resolve => setTimeout(resolve, 2000)); + setRefreshing(false) }; return ( @@ -53,7 +63,14 @@ export default function BannerList() { }} /> - + + } + > {entities.map((index: any, key: number) => ( ('true') const [page, setPage] = useState(1) const [waiting, setWaiting] = useState(false) + const [refreshing, setRefreshing] = useState(false) async function handleLoad(loading: boolean, thisPage: number) { try { @@ -77,6 +78,13 @@ export default function Discussion() { }, 1000); }; + const handleRefresh = async () => { + setRefreshing(true) + handleLoad(false, 1) + await new Promise(resolve => setTimeout(resolve, 2000)); + setRefreshing(false) + }; + const getItem = (_data: unknown, index: number): Props => ({ id: data[index].id, title: data[index].title, @@ -160,6 +168,12 @@ export default function Discussion() { onEndReached={loadMoreData} onEndReachedThreshold={0.5} showsVerticalScrollIndicator={false} + refreshControl={ + + } /> // data.map((item: any, i: number) => { // return ( diff --git a/app/(application)/division/[id]/(fitur-division)/calendar/index.tsx b/app/(application)/division/[id]/(fitur-division)/calendar/index.tsx index 990b994..7e8cb39 100644 --- a/app/(application)/division/[id]/(fitur-division)/calendar/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/calendar/index.tsx @@ -11,7 +11,7 @@ import dayjs from "dayjs"; import { router, Stack, useLocalSearchParams } from "expo-router"; import moment from "moment"; import { useEffect, useState } from "react"; -import { Pressable, SafeAreaView, ScrollView, Text, View } from "react-native"; +import { Pressable, RefreshControl, SafeAreaView, ScrollView, Text, View } from "react-native"; import Datepicker, { CalendarComponents, CalendarDay @@ -32,15 +32,16 @@ type Props = { }; export default function CalendarDivision() { - const [selected, setSelected] = useState(new Date()); - const [data, setData] = useState([]); - const { token, decryptToken } = useAuthSession(); - const { id } = useLocalSearchParams<{ id: string }>(); - const [dataIndicator, setDataIndicator] = useState([]); - const [month, setMonth] = useState(new Date().getMonth()); + const [selected, setSelected] = useState(new Date()) + const [data, setData] = useState([]) + const { token, decryptToken } = useAuthSession() + const { id } = useLocalSearchParams<{ id: string }>() + const [dataIndicator, setDataIndicator] = useState([]) + const [month, setMonth] = useState(new Date().getMonth()) const update = useSelector((state: any) => state.calendarUpdate) const [loading, setLoading] = useState(true) const [loadingBtn, setLoadingBtn] = useState(false) + const [refreshing, setRefreshing] = useState(false) async function handleLoad(loading: boolean) { @@ -90,6 +91,13 @@ export default function CalendarDivision() { handleLoadIndicator(); }, [month, update.data]); + const handleRefresh = async () => { + setRefreshing(true) + handleLoad(false) + await new Promise(resolve => setTimeout(resolve, 2000)); + setRefreshing(false) + }; + const components: CalendarComponents = { Day: (day: CalendarDay) => { const now = String(day.date); @@ -127,7 +135,13 @@ export default function CalendarDivision() { headerRight: () => , }} /> - + + }> ([]) const { token, decryptToken } = useAuthSession() const [search, setSearch] = useState('') - const update = useSelector((state: any) => state.discussionUpdate); + const update = useSelector((state: any) => state.discussionUpdate) const [loading, setLoading] = useState(true) const arrSkeleton = Array.from({ length: 5 }) const [page, setPage] = useState(1) const [waiting, setWaiting] = useState(false) const [status, setStatus] = useState<'true' | 'false'>('true') + const [refreshing, setRefreshing] = useState(false) async function handleLoad(loading: boolean, thisPage: number) { try { @@ -77,6 +78,13 @@ export default function DiscussionDivision() { }, 1000); } + const handleRefresh = async () => { + setRefreshing(true) + handleLoad(false, 1) + await new Promise(resolve => setTimeout(resolve, 2000)); + setRefreshing(false) + }; + const getItem = (_data: unknown, index: number): Props => ({ id: data[index].id, title: data[index].title, @@ -155,6 +163,12 @@ export default function DiscussionDivision() { onEndReached={loadMoreData} onEndReachedThreshold={0.5} showsVerticalScrollIndicator={false} + refreshControl={ + + } /> // data.map((item, index) => ( // (); - const [path, setPath] = useState("home"); - const [data, setData] = useState([]); - const [dataJalur, setDataJalur] = useState([]); - const [dariSelectAll, setDariSelectAll] = useState(false); - const [selectedFiles, setSelectedFiles] = useState([]); - const [selectAll, setSelectAll] = useState(false); - const [shareSelected, setShareSelected] = useState(false); - const [copyAllowed, setCopyAllowed] = useState(true); - const [modalMore, setModalMore] = useState(false); - const [isRename, setRename] = useState(false); + const [isShare, setShare] = useState(false) + const { token, decryptToken } = useAuthSession() + const { id } = useLocalSearchParams<{ id: string }>() + const [path, setPath] = useState("home") + const [data, setData] = useState([]) + const [dataJalur, setDataJalur] = useState([]) + const [dariSelectAll, setDariSelectAll] = useState(false) + const [selectedFiles, setSelectedFiles] = useState([]) + const [selectAll, setSelectAll] = useState(false) + const [shareSelected, setShareSelected] = useState(false) + const [copyAllowed, setCopyAllowed] = useState(true) + const [modalMore, setModalMore] = useState(false) + const [isRename, setRename] = useState(false) const dispatch = useDispatch(); const [loading, setLoading] = useState(true) const arrSkeleton = Array.from({ length: 3 }) - const update = useSelector((state: any) => state.dokumenUpdate); + const update = useSelector((state: any) => state.dokumenUpdate) + const [refreshing, setRefreshing] = useState(false) const [bodyRename, setBodyRename] = useState({ id: "", name: "", @@ -87,9 +89,9 @@ export default function DocumentDivision() { extension: "", }); - async function handleLoad() { + async function handleLoad(loading: boolean) { try { - setLoading(true) + setLoading(loading) const hasil = await decryptToken(String(token?.current)); const response = await apiGetDocument({ user: hasil, @@ -107,7 +109,7 @@ export default function DocumentDivision() { } useEffect(() => { - handleLoad(); + handleLoad(true); }, [path, update]); const handleCheckboxChange = (index: number) => { @@ -297,6 +299,13 @@ export default function DocumentDivision() { }); }; + const handleRefresh = async () => { + setRefreshing(true) + handleLoad(false) + await new Promise(resolve => setTimeout(resolve, 2000)); + setRefreshing(false) + }; + return ( - + + }> { diff --git a/app/(application)/division/[id]/(fitur-division)/task/index.tsx b/app/(application)/division/[id]/(fitur-division)/task/index.tsx index facf093..208ac17 100644 --- a/app/(application)/division/[id]/(fitur-division)/task/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/task/index.tsx @@ -17,7 +17,7 @@ import { } from "@expo/vector-icons"; import { router, useLocalSearchParams } from "expo-router"; import { useEffect, useState } from "react"; -import { Pressable, ScrollView, Text, View, VirtualizedList } from "react-native"; +import { Pressable, RefreshControl, ScrollView, Text, View, VirtualizedList } from "react-native"; import { useSelector } from "react-redux"; type Props = { @@ -30,17 +30,18 @@ type Props = { }; export default function ListTask() { - const { id, status } = useLocalSearchParams<{ id: string; status: string }>(); - const [isList, setList] = useState(false); - const { token, decryptToken } = useAuthSession(); - const [data, setData] = useState([]); - const [search, setSearch] = useState(""); + const { id, status } = useLocalSearchParams<{ id: string; status: string }>() + const [isList, setList] = useState(false) + const { token, decryptToken } = useAuthSession() + const [data, setData] = useState([]) + const [search, setSearch] = useState("") const update = useSelector((state: any) => state.taskUpdate) const [loading, setLoading] = useState(true) const arrSkeleton = Array.from({ length: 3 }) const [statusFix, setStatusFix] = useState<'0' | '1' | '2' | '3'>('0') const [page, setPage] = useState(1) const [waiting, setWaiting] = useState(false) + const [refreshing, setRefreshing] = useState(false) async function handleLoad(loading: boolean, thisPage: number) { try { @@ -85,6 +86,13 @@ export default function ListTask() { }, 1000); } + const handleRefresh = async () => { + setRefreshing(true) + handleLoad(false, 1) + await new Promise(resolve => setTimeout(resolve, 2000)); + setRefreshing(false) + }; + const getItem = (_data: unknown, index: number): Props => ({ id: data[index].id, title: data[index].title, @@ -208,6 +216,12 @@ export default function ListTask() { onEndReached={loadMoreData} onEndReachedThreshold={0.5} showsVerticalScrollIndicator={false} + refreshControl={ + + } /> {/* {data.map((item, index) => ( + } /> {/* {data.map((item, index) => ( (); const [isList, setList] = useState(false); - const entityUser = useSelector((state: any) => state.user); - const { token, decryptToken } = useAuthSession(); - const [search, setSearch] = useState(""); - const [nameGroup, setNameGroup] = useState(""); - const [data, setData] = useState([]); + const entityUser = useSelector((state: any) => state.user) + const { token, decryptToken } = useAuthSession() + const [search, setSearch] = useState("") + const [nameGroup, setNameGroup] = useState("") + const [data, setData] = useState([]) const update = useSelector((state: any) => state.divisionUpdate) const arrSkeleton = Array.from({ length: 3 }, (_, index) => index) const [loading, setLoading] = useState(false) @@ -46,6 +46,7 @@ export default function ListDivision() { const [category, setCategory] = useState<'divisi-saya' | 'semua'>('divisi-saya') const [page, setPage] = useState(1) const [waiting, setWaiting] = useState(false) + const [refreshing, setRefreshing] = useState(false) async function handleLoad(loading: boolean, thisPage: number) { try { @@ -95,6 +96,13 @@ export default function ListDivision() { }, 1000); }; + const handleRefresh = async () => { + setRefreshing(true) + handleLoad(false, 1) + await new Promise(resolve => setTimeout(resolve, 2000)); + setRefreshing(false) + }; + const getItem = (_data: unknown, index: number): Props => ({ id: data[index].id, name: data[index].name, @@ -235,6 +243,12 @@ export default function ListDivision() { onEndReached={loadMoreData} onEndReachedThreshold={0.5} showsVerticalScrollIndicator={false} + refreshControl={ + + } /> ) : ( @@ -264,6 +278,12 @@ export default function ListDivision() { onEndReached={loadMoreData} onEndReachedThreshold={0.5} showsVerticalScrollIndicator={false} + refreshControl={ + + } /> ) diff --git a/app/(application)/group/index.tsx b/app/(application)/group/index.tsx index 9720f53..7a9b41a 100644 --- a/app/(application)/group/index.tsx +++ b/app/(application)/group/index.tsx @@ -14,7 +14,7 @@ import { setUpdateGroup } from "@/lib/groupSlice"; import { useAuthSession } from "@/providers/AuthProvider"; import { AntDesign, Feather, MaterialCommunityIcons } from "@expo/vector-icons"; import { useEffect, useState } from "react"; -import { SafeAreaView, ScrollView, Text, ToastAndroid, View } from "react-native"; +import { RefreshControl, SafeAreaView, ScrollView, Text, ToastAndroid, View } from "react-native"; import { useDispatch, useSelector } from "react-redux"; type Props = { @@ -36,6 +36,7 @@ export default function Index() { const [idChoose, setIdChoose] = useState('') const [activeChoose, setActiveChoose] = useState(true) const [titleChoose, setTitleChoose] = useState('') + const [refreshing, setRefreshing] = useState(false) const dispatch = useDispatch() const update = useSelector((state: any) => state.groupUpdate) @@ -99,12 +100,24 @@ export default function Index() { handleLoad(true) }, [status, search]) - + const handleRefresh = async () => { + setRefreshing(true) + handleLoad(false) + await new Promise(resolve => setTimeout(resolve, 2000)); + setRefreshing(false) + }; return ( - + + } + > ('true') const [page, setPage] = useState(1) const [waiting, setWaiting] = useState(false) + const [refreshing, setRefreshing] = useState(false) async function handleLoad(loading: boolean, thisPage: number) { try { @@ -78,8 +79,12 @@ export default function Index() { handleLoad(true, 1) }, [group, search, status]) - - + const handleRefresh = async () => { + setRefreshing(true) + handleLoad(false, 1) + await new Promise(resolve => setTimeout(resolve, 2000)); + setRefreshing(false) + }; const getItem = (_data: unknown, index: number): Props => ({ id: data[index].id, @@ -155,6 +160,12 @@ export default function Index() { onEndReached={loadMoreData} onEndReachedThreshold={0.5} showsVerticalScrollIndicator={false} + refreshControl={ + + } /> : Tidak ada data diff --git a/app/(application)/position/index.tsx b/app/(application)/position/index.tsx index 8431cb4..a6b0cf4 100644 --- a/app/(application)/position/index.tsx +++ b/app/(application)/position/index.tsx @@ -15,7 +15,7 @@ import { useAuthSession } from "@/providers/AuthProvider"; import { AntDesign, Feather, MaterialCommunityIcons } from "@expo/vector-icons"; import { useLocalSearchParams } from "expo-router"; import { useEffect, useState } from "react"; -import { SafeAreaView, ScrollView, Text, ToastAndroid, View } from "react-native"; +import { RefreshControl, SafeAreaView, ScrollView, Text, ToastAndroid, View } from "react-native"; import { useDispatch, useSelector } from "react-redux"; type Props = { @@ -42,6 +42,7 @@ export default function Index() { const [error, setError] = useState({ name: false, }); + const [refreshing, setRefreshing] = useState(false) const dispatch = useDispatch() const update = useSelector((state: any) => state.positionUpdate) @@ -119,9 +120,21 @@ export default function Index() { handleEdit() } + const handleRefresh = async () => { + setRefreshing(true) + handleLoad(false) + await new Promise(resolve => setTimeout(resolve, 2000)); + setRefreshing(false) + }; + return ( - + + }> (); const [statusFix, setStatusFix] = useState<'0' | '1' | '2' | '3'>('0') const { token, decryptToken } = useAuthSession(); - const entityUser = useSelector((state: any) => state.user); - const [search, setSearch] = useState(""); - const [nameGroup, setNameGroup] = useState(""); - const [data, setData] = useState([]); - const [isList, setList] = useState(false); + const entityUser = useSelector((state: any) => state.user) + const [search, setSearch] = useState("") + const [nameGroup, setNameGroup] = useState("") + const [data, setData] = useState([]) + const [isList, setList] = useState(false) const update = useSelector((state: any) => state.projectUpdate) const [loading, setLoading] = useState(true) const arrSkeleton = Array.from({ length: 3 }, (_, index) => index) const [page, setPage] = useState(1) const [waiting, setWaiting] = useState(false) + const [refreshing, setRefreshing] = useState(false) async function handleLoad(loading: boolean, thisPage: number) { try { @@ -96,7 +97,14 @@ export default function ListProject() { setTimeout(() => { handleLoad(false, page + 1) }, 1000); - }; + } + + const handleRefresh = async () => { + setRefreshing(true) + handleLoad(false, 1) + await new Promise(resolve => setTimeout(resolve, 2000)); + setRefreshing(false) + } const getItem = (_data: unknown, index: number): Props => ({ id: data[index].id, @@ -189,14 +197,14 @@ export default function ListProject() { (entityUser.role == "supadmin" || entityUser.role == "developer") && nameGroup } { - (entityUser.role == 'user' || entityUser.role == 'coadmin') + (entityUser.role == 'user' || entityUser.role == 'coadmin' || entityUser.role == 'cosupadmin') ? (cat == 'null' || cat == 'undefined' || cat == undefined || cat == '' || cat == 'data-saya') ? 'Kegiatan Saya' : 'Semua Kegiatan' : '' } - + { loading ? isList ? @@ -239,6 +247,12 @@ export default function ListProject() { onEndReached={loadMoreData} onEndReachedThreshold={0.5} showsVerticalScrollIndicator={false} + refreshControl={ + + } /> {/* { data.map((item, index) => { @@ -311,6 +325,12 @@ export default function ListProject() { onEndReached={loadMoreData} onEndReachedThreshold={0.5} showsVerticalScrollIndicator={false} + refreshControl={ + + } /> {/* {data.map((item, index) => { return (