// src/screens/HomeScreen.tsx

import React, { useCallback, useContext, useState, useEffect } from 'react';
import { 
  View, 
  FlatList, 
  Image, 
  TouchableOpacity, 
  StyleSheet, 
  SafeAreaView,
  ActivityIndicator,
  useWindowDimensions,
  RefreshControl,
  ScrollView,
  Alert,
} from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { useNavigation } from '@react-navigation/native';
import { HistoricalFigure } from '../types/types';
import CustomText from '../components/CustomText';
import { Ionicons } from '@expo/vector-icons';
import { useApp } from '../context/AppContext';
import { ChatContext } from '../context/ChatContext';
import { CoinContext } from '../context/CoinContext';
import uuid from 'react-native-uuid'; // Ensure correct import
import AsyncStorage from '@react-native-async-storage/async-storage';

const HomeScreen = () => {
  const navigation = useNavigation();
  const { 
    historicalFigures, 
    loading, 
    refreshing, 
    t, 
    currentTheme, 
    refreshFigures,
    categories,
  } = useApp();
  const { width } = useWindowDimensions();

  const { addChat } = useContext(ChatContext);
  const { coins, showRewardedAd } = useContext(CoinContext);
  const [selectedCategory, setSelectedCategory] = useState<string>('All');
  const [isCreatingChat, setIsCreatingChat] = useState<boolean>(false); // New state
  const [isGlobalLoading, setIsGlobalLoading] = useState<boolean>(false); // Central loading state

  const numColumns = 2;

  useEffect(() => {
    // Manage global loading state based on 'loading' or 'isCreatingChat'
    if (loading || isCreatingChat) {
      setIsGlobalLoading(true);
    } else {
      setIsGlobalLoading(false);
    }
  }, [loading, isCreatingChat]);

  const startChat = useCallback(async (figure: HistoricalFigure) => {
    if (isCreatingChat) return; // Prevent multiple chat creations
    setIsCreatingChat(true); // Set loading state
    try {
      const chatId = uuid.v4().toString();
      console.log('Generated chatId:', chatId); // Logging for debugging

      await addChat({
        id: chatId,
        figure,
        createdAt: Date.now(),
      });

      navigation.navigate('Chat', { chatId, figure });
    } catch (error) {
      console.error('Error starting chat:', error);
      Alert.alert(t('error'), t('errorStartingChat'));
    } finally {
      setIsCreatingChat(false); // Reset loading state
    }
  }, [addChat, navigation, t, isCreatingChat]);

  const renderFigureItem = useCallback(({ item }: { item: HistoricalFigure }) => (
    <TouchableOpacity
      style={[styles.figureItem, { backgroundColor: currentTheme.cardBackground }]}
      onPress={() => startChat(item)}
      disabled={isCreatingChat} // Disable while creating
    >
      <Image source={{ uri: item.image }} style={styles.avatar} />
      <View style={styles.figureTextContainer}>
        <CustomText bold style={[styles.figureName, { color: currentTheme.textColor }]}>
          {item.name}
        </CustomText>
        <CustomText style={[styles.figureCategory, { color: currentTheme.accentColor }]}>
          {item.category}
        </CustomText>
        <CustomText 
          style={[styles.figureDescription, { color: currentTheme.secondaryTextColor }]} 
          numberOfLines={3}
        >
          {item.description}
        </CustomText>
        <CustomText style={[styles.popularity, { color: currentTheme.accentColor }]}>
          {t('popularity')} {item.popularity || 0}
        </CustomText>
      </View>
    </TouchableOpacity>
  ), [currentTheme, startChat, t, isCreatingChat]);

  const keyExtractor = useCallback((item: HistoricalFigure) => item.id, []);

  const onRefresh = useCallback(async () => {
    await refreshFigures(); // This will handle refreshing state in AppContext
  }, [refreshFigures]);

  const filteredFigures = selectedCategory === 'All' 
    ? historicalFigures 
    : historicalFigures.filter(fig => fig.category === selectedCategory);

  const renderCategoryItem = useCallback((category: string, index: number) => {
    const isSelected = category === selectedCategory;
    return (
      <TouchableOpacity
        key={`${category}-${index}`} // Ensure each item has a unique key
        style={[
          styles.categoryButton,
          { 
            backgroundColor: isSelected ? currentTheme.accentColor : 'transparent',
            borderColor: isSelected ? currentTheme.accentColor : currentTheme.secondaryTextColor,
          }
        ]}
        onPress={() => setSelectedCategory(category)}
      >
        <CustomText 
          style={[
            styles.categoryText, 
            { 
              color: isSelected ? currentTheme.backgroundColor : currentTheme.textColor,
            }
          ]}
        >
          {t(category)}
        </CustomText>
      </TouchableOpacity>
    );
  }, [selectedCategory, currentTheme, t]);

  const renderCategoryRows = () => {
    // Display categories in two rows for better organization
    const halfLength = Math.ceil(categories.length / 2);
    return (
      <ScrollView 
        horizontal 
        showsHorizontalScrollIndicator={false}
        contentContainerStyle={styles.categoryRowsContainer}
      >
        <View>
          <View style={styles.categoryRow}>
            {categories.slice(0, halfLength).map((category, index) => renderCategoryItem(category, index))}
          </View>
          <View style={styles.categoryRow}>
            {categories.slice(halfLength).map((category, index) => renderCategoryItem(category, index + halfLength))}
          </View>
        </View>
      </ScrollView>
    );
  };

  return (
    <SafeAreaView style={[styles.safeArea, { backgroundColor: currentTheme.backgroundColor }]}>
      <StatusBar style={currentTheme.statusBarStyle} />
      <View style={styles.container}>
        {/* Header */}
        <View style={styles.header}>
          <CustomText bold style={[styles.title, { color: currentTheme.textColor }]}>
            {t('historicalFigures')}
          </CustomText>
          <View style={styles.headerRight}>
            {/* Coin Display */}
            <TouchableOpacity style={styles.coinContainer} onPress={showRewardedAd}>
              <Ionicons name="flash" size={20} color={currentTheme.accentColor} />
              <CustomText style={[styles.coinText, { color: currentTheme.textColor }]}>
                {coins}
              </CustomText>
            </TouchableOpacity>
            {/* Settings Button */}
            <TouchableOpacity 
              style={styles.settingsButton} 
              onPress={() => navigation.navigate('Settings')}
            >
              <Ionicons name="settings-outline" size={24} color={currentTheme.accentColor} />
            </TouchableOpacity>
          </View>
        </View>

        {/* Category Selector (Two Rows, Scrollable Horizontally) */}
        {renderCategoryRows()}

        {/* Content */}
        {loading ? (
          // Single centralized loading indicator
          <View style={styles.loadingContainer}>
            <ActivityIndicator size="large" color={currentTheme.accentColor} />
          </View>
        ) : filteredFigures.length === 0 ? (
          <View style={styles.emptyContainer}>
            <CustomText style={[styles.emptyText, { color: currentTheme.textColor }]}>
              {t('noFiguresInThisCategory')}
            </CustomText>
          </View>
        ) : (
          <FlatList
            data={filteredFigures}
            renderItem={renderFigureItem}
            keyExtractor={keyExtractor}
            contentContainerStyle={styles.figureList}
            numColumns={numColumns}
            showsVerticalScrollIndicator={false}
            refreshControl={
              <RefreshControl
                refreshing={refreshing}
                onRefresh={onRefresh}
                colors={[currentTheme.accentColor]}
                tintColor={currentTheme.accentColor}
              />
            }
          />
        )}
      </View>

      {/* Global Loading Overlay */}
      {isGlobalLoading && (
        <View style={styles.loadingOverlay}>
          <ActivityIndicator size="large" color={currentTheme.accentColor} />
        </View>
      )}
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  safeArea: {
    flex: 1,
  },
  container: {
    flex: 1,
    paddingHorizontal: 10,
  },
  header: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    paddingVertical: 15,
    paddingHorizontal: 15,
  },
  title: {
    fontFamily: 'Poppins_700Bold',
    fontSize: 24,
  },
  headerRight: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  coinContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    padding: 5,
  },
  coinText: {
    fontFamily: 'Poppins_700Bold',
    fontSize: 18,
    marginLeft: 5,
  },
  settingsButton: {
    padding: 2,
    marginLeft: 15, // Increased spacing for better visual separation
  },
  categoryRowsContainer: {
    paddingHorizontal: 5,
    paddingBottom: 20,
  },
  categoryRow: {
    flexDirection: 'row',
    marginBottom: 6,
  },
  categoryButton: {
    paddingVertical: 8,
    paddingHorizontal: 10, // Increased padding for better touch area
    borderRadius: 16,
    borderWidth: 1,
    marginRight: 5, // Increased margin for better spacing
  },
  categoryText: {
    fontFamily: 'Poppins_600SemiBold',
    fontSize: 14,
  },
  figureList: {
    paddingVertical: 10,
  },
  figureItem: {
    borderRadius: 12,
    elevation: 3,
    flex: 1,
    margin: 5,
    shadowOffset: {
      width: 0,
      height: 1,
    },
    shadowOpacity: 0.22,
    shadowRadius: 2.22,
    shadowColor: '#000',
  },
  avatar: {
    borderTopLeftRadius: 12,
    borderTopRightRadius: 12,
    height: 120, // Uniform height across all images
    width: '100%', // Span the full width of the figure item
    resizeMode: 'cover', // Cover ensures the image fills the space without distortion
  },
  figureTextContainer: {
    padding: 10,
  },
  figureName: {
    fontFamily: 'Poppins_700Bold',
    fontSize: 16,
    textAlign: 'center',
    marginBottom: 4,
  },
  figureCategory: {
    fontFamily: 'Poppins_600SemiBold',
        textAlign: 'center',

    fontSize: 12,
    marginBottom: 4,
  },
  figureDescription: {
    fontFamily: 'Poppins_400Regular',
    fontSize: 14,
    textAlign: 'center',
    marginBottom: 4,
  },
  popularity: {
        textAlign: 'center',

    fontSize: 14,
    fontFamily: 'Poppins_600SemiBold',
  },
  loadingContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  emptyContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  emptyText: {
    fontSize: 16,
    textAlign: 'center',
  },
  loadingOverlay: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'rgba(0, 0, 0, 0.3)', // Semi-transparent background
    zIndex: 1000, // Ensure overlay is on top
  },
});

export default HomeScreen; buradaki tüm loading activityindicator leri kaldırıyorsun yerine bu paketi kullanarak güzel bir şey yapıyorsun skip to:contentpackage searchsign in
❤
Pro
Teams
Pricing
Documentation
npm
Search packages
Search
expo-shimmer-placeholder
3.0.0 • Public • Published 2 years ago
Expo Shimmer Placeholder
  

Placeholder for both IOS and Android


Get Started
Fork of https://www.npmjs.com/package/react-native-shimmer-loading to only require expo-linear-gradient as react-native-linear-gradient was causing issues

Installation
npm i expo-shimmer-placeholder --save

or

yarn add expo-shimmer-placeholder

Usage
Simple
import { createShimmerPlaceHolder } from 'expo-shimmer-placeholder'
import { LinearGradient } from 'expo-linear-gradient'
const ShimmerPlaceHolder = createShimmerPlaceHolder(LinearGradient)

<ShimmerPlaceHolder />
<ShimmerPlaceHolder visible={isFetched}>
  <Text>
    Wow, awesome here.
  </Text>
</ShimmerPlaceHolder>
Connect more components


More Detail see this

Props
Prop	Description	Type	Default
LinearGradient	Linear Gradient components ('expo-linear-gradient')	Component	undefined
visible	Visible child components	boolean	false
style	Container Style	Style	{backgroundColor: '#ebebeb',overflow: 'hidden'}
shimmerStyle	Shimmer Style only	Style	{}
contentStyle	Content Style when visible	Style	{}
location	Locations of shimmer	number[]	[0.3, 0.5, 0.7]
width	Width of row	number	200
duration	Duration of shimmer over a row	number	1000
height	Height of row	number	15
shimmerWidthPercent	Percent of shimmer width	number	1.0
isReversed	Reverse direction of animation	boolean	false
stopAutoRun	Stop running shimmer animation at beginning	boolean	false
isInteraction	Defines whether or not the shimmer animation creates an interaction handle on the InteractionManager	boolean	true
shimmerColors	Colors of the shimmer.	string[]	['#ebebeb', '#c5c5c5', '#ebebeb']
containerProps	Props passed to the outermost View	ViewProps	undefined
shimmerContainerProps	Props passed to the View which contains the loading animation	ViewProps	undefined
childrenContainerProps	Props passed to the View which contains the children	ViewProps	undefined
Methods
Method	Description	Type
getAnimated	get Animated of Placeholder	Animated
Helpers
createShimmerPlaceHolder

Contribute
Welcome help me to build this awesome lib.

License
MIT

Readme
Keywords
loadingplaceholderreact-nativeshimmerexpo
Package Sidebar
Install
npm i expo-shimmer-placeholder

Repository
github.com/kdthomas2121/expo-shimmer-placeholder

Homepage
github.com/kdthomas2121/expo-shimmer-placeholder

Weekly Downloads
253

Version
3.0.0

License
MIT

Unpacked Size
12.4 kB

Total Files
5

Issues
0

Pull Requests
0

Last publish
2 years ago

Collaborators
kdthom21
Try on RunKit
Report malware
Footer
Support
Help
Advisories
Status
Contact npm
Company
About
Blog
Press
Terms & Policies
Policies
Terms of Use
Code of Conduct
Privacy