From 360ac5807ca53269d9f823ee671024d3341faa60 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Thu, 24 Jul 2025 11:53:47 +0800 Subject: [PATCH] Component Add: - Contoh Flatlist untuk tampilan yang membutuhkan load data # No Issue --- app/(application)/coba/double-scroll.tsx | 258 +++++++++++++++++++++++ app/(application)/coba/index.tsx | 75 ++++--- screens/Authentication/LoginView.tsx | 1 + 3 files changed, 301 insertions(+), 33 deletions(-) create mode 100644 app/(application)/coba/double-scroll.tsx diff --git a/app/(application)/coba/double-scroll.tsx b/app/(application)/coba/double-scroll.tsx new file mode 100644 index 0000000..3600384 --- /dev/null +++ b/app/(application)/coba/double-scroll.tsx @@ -0,0 +1,258 @@ +// File: src/screens/EventDetailScreen.tsx + +import LeftButtonCustom from "@/components/Button/BackButton"; +import { MainColor } from "@/constants/color-palet"; +import React, { useState, useEffect } from "react"; +import { + FlatList, + View, + Text, + Image, + StyleSheet, + ActivityIndicator, +} from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; + +// === TYPES === +type Participant = { + id: number; + name: string; + avatar: string; +}; + +type EventDetail = { + id: number; + title: string; + description: string; + date: string; + location: string; + organizer: string; +}; + +// === KOMPONEN UTAMA === +const EventDetailScreen: React.FC = () => { + const [participants, setParticipants] = useState([]); + const [loading, setLoading] = useState(true); + const [loadingMore, setLoadingMore] = useState(false); + + // Data event + const event: EventDetail = { + id: 1, + title: "Workshop React Native & Expo", + description: + "Pelatihan intensif pengembangan aplikasi mobile menggunakan React Native, Expo, dan TypeScript. Cocok untuk developer tingkat menengah.", + date: "Sabtu, 5 April 2025 | 09:00 - 16:00", + location: "Gedung Teknologi, Jakarta Selatan", + organizer: "DevCommunity Indonesia", + }; + + // Simulasi API: generate data dummy + const generateParticipants = ( + startId: number, + count: number + ): Participant[] => { + return Array.from({ length: count }, (_, i) => { + const id = startId + i; + return { + id, + name: `Peserta ${id}`, + avatar: `https://i.pravatar.cc/150?img=${(id % 70) + 1}`, // 70 gambar unik + }; + }); + }; + + // Load data awal + useEffect(() => { + const loadInitial = () => { + setTimeout(() => { + const initialData = generateParticipants(1, 20); // 20 peserta pertama + setParticipants(initialData); + setLoading(false); + }, 800); + }; + + loadInitial(); + }, []); + + // Load lebih banyak peserta saat scroll ke bawah + const loadMore = () => { + if (loadingMore || participants.length >= 200) return; // Batas 200 peserta + + setLoadingMore(true); + setTimeout(() => { + const nextId = participants.length + 1; + const newData = generateParticipants(nextId, 10); // Tambah 10 peserta + setParticipants((prev) => [...prev, ...newData]); + setLoadingMore(false); + }, 1000); + }; + + // Render footer: loading indicator + const renderFooter = () => { + if (!loadingMore) return null; + return ( + + + Memuat peserta berikutnya... + + ); + }; + + // Render header: detail event + info jumlah peserta + const renderHeader = () => ( + <> + + + {event.title} + 📅 {event.date} + 📍 {event.location} + 👤 {event.organizer} + {event.description} + + + + + Daftar Peserta ({participants.length}) + + + + {/* Sub-header tambahan jika perlu */} + {participants.length === 0 ? ( + Belum ada peserta yang terdaftar. + ) : null} + + ); + + // Loading awal + if (loading) { + return ( + + + + ); + } + + return ( + <> + item.id.toString()} + ListHeaderComponent={renderHeader} + ListFooterComponent={renderFooter} + onEndReached={loadMore} + onEndReachedThreshold={0.5} + renderItem={({ item }) => ( + + + {item.name} + + )} + initialNumToRender={10} + maxToRenderPerBatch={5} + windowSize={7} + showsVerticalScrollIndicator={false} + ListEmptyComponent={ + Tidak ada peserta. + } + /> + + + + ); +}; + +export default EventDetailScreen; + +// === STYLES === +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: "#f8f9fa", + }, + loader: { + marginTop: 20, + }, + headerContainer: { + padding: 16, + backgroundColor: "#ffffff", + borderBottomWidth: 1, + borderBottomColor: "#e0e0e0", + }, + title: { + fontSize: 24, + fontWeight: "bold", + color: "#1d1d1d", + marginBottom: 8, + }, + info: { + fontSize: 16, + color: "#444", + marginBottom: 4, + }, + description: { + fontSize: 14, + color: "#666", + lineHeight: 20, + marginTop: 12, + }, + divider: { + height: 1, + backgroundColor: "#e0e0e0", + marginVertical: 16, + }, + sectionTitle: { + fontSize: 18, + fontWeight: "600", + color: "#1d1d1d", + marginBottom: 12, + }, + participantItem: { + flexDirection: "row", + alignItems: "center", + paddingHorizontal: 16, + paddingVertical: 12, + backgroundColor: "#fff", + borderBottomWidth: 1, + borderBottomColor: "#f0f0f0", + }, + avatar: { + width: 40, + height: 40, + borderRadius: 20, + marginRight: 12, + }, + participantName: { + fontSize: 16, + color: "#333", + }, + footer: { + flexDirection: "row", + justifyContent: "center", + alignItems: "center", + padding: 16, + backgroundColor: "#f8f9fa", + }, + loadingText: { + color: "#555", + fontSize: 14, + }, + empty: { + textAlign: "center", + color: "#999", + fontStyle: "italic", + padding: 16, + backgroundColor: "#fff", + fontSize: 14, + }, + subHeader: { + paddingHorizontal: 16, + paddingVertical: 8, + backgroundColor: "#f1f1f1", + fontSize: 14, + color: "#666", + fontStyle: "italic", + }, +}); diff --git a/app/(application)/coba/index.tsx b/app/(application)/coba/index.tsx index 7e1b43b..65e2c9e 100644 --- a/app/(application)/coba/index.tsx +++ b/app/(application)/coba/index.tsx @@ -8,7 +8,9 @@ import { ScrollView, } from "react-native"; import { Ionicons } from "@expo/vector-icons"; -import { router } from "expo-router"; +import { router, Stack } from "expo-router"; +import EventDetailScreen from "./double-scroll"; +import LeftButtonCustom from "@/components/Button/BackButton"; const { width } = Dimensions.get("window"); @@ -16,7 +18,9 @@ const { width } = Dimensions.get("window"); const HomeScreen = () => ( Selamat Datang! - Ini adalah halaman utama aplikasi Anda + + Ini adalah halaman utama aplikasi Anda + ); const SearchScreen = () => ( @@ -65,9 +69,7 @@ const CustomTab = ({ icon, label, isActive, onPress }: any) => ( // Main Custom Tab Navigator const CustomTabNavigator = () => { - const [activeTab, setActiveTab] = React.useState( - 'home' - ); + const [activeTab, setActiveTab] = React.useState("home"); const [showHome, setShowHome] = React.useState(true); const tabs = [ @@ -97,13 +99,12 @@ const CustomTabNavigator = () => { }, ]; - // Function untuk handle tab press const handleTabPress = (tabId: string) => { setActiveTab(tabId); setShowHome(false); // Hide home when any tab is pressed }; - + // Determine which component to show const getActiveComponent = () => { if (showHome || activeTab === "home") { @@ -111,38 +112,46 @@ const CustomTabNavigator = () => { } // const selectedTab = tabs.find((tab) => tab.id === activeTab); // return selectedTab ? selectedTab.component : HomeScreen; - return HomeScreen + return HomeScreen; }; const ActiveComponent = getActiveComponent(); return ( - - {/* Content Area */} - - - - - + <> + + + + // + // {/* Content Area */} + // + // + // + // + // - {/* Custom Tab Bar */} - - - {tabs.map((e) => ( - { - handleTabPress(e.id); - router.push(e.path as any); - }} - /> - ))} - - - + // {/* Custom Tab Bar */} + // + // + // {tabs.map((e) => ( + // { + // handleTabPress(e.id); + // router.push(e.path as any); + // }} + // /> + // ))} + // + // + // ); }; diff --git a/screens/Authentication/LoginView.tsx b/screens/Authentication/LoginView.tsx index aac55c3..641006d 100644 --- a/screens/Authentication/LoginView.tsx +++ b/screens/Authentication/LoginView.tsx @@ -37,6 +37,7 @@ export default function LoginView() { // router.navigate(`/(application)/(user)/portofolio/${id}`) // router.navigate(`/(application)/(image)/preview-image/${id}`); // router.replace("/(application)/(user)/event/(tabs)"); + // router.replace("/(application)/coba"); } return (