diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 217a907..744f73a 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -1,13 +1,48 @@ -import { Stack } from 'expo-router'; +import { Tabs } from 'expo-router'; +import { TouchableOpacity } from 'react-native'; +import { Ionicons } from '@expo/vector-icons'; +import { useRouter } from 'expo-router'; +import { useTheme } from '../themeContext'; import React from 'react'; -export default function AppLayout() { +export default function TabLayout() { + const router = useRouter(); + const { isDarkMode } = useTheme(); + return ( - - - + tabBarActiveTintColor: '#6B21A8', + tabBarInactiveTintColor: isDarkMode ? '#9CA3AF' : '#6B7280', + tabBarStyle: { + backgroundColor: isDarkMode ? '#1F2937' : '#FFFFFF', + borderTopColor: isDarkMode ? '#374151' : '#E5E7EB', + }, + headerStyle: { + backgroundColor: isDarkMode ? '#121212' : '#FFFFFF', + }, + headerTintColor: isDarkMode ? '#F3F4F6' : '#1F2937', + headerRight: () => ( + router.push('/settings')} + style={{ marginRight: 15 }} + > + + + ), + }} + > + , + }} + /> + ); } diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 2508ba5..fff77d0 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -12,6 +12,8 @@ import * as SecureStore from 'expo-secure-store'; import Constants from 'expo-constants'; import { useUser } from "../userContext"; import { AppState } from "react-native"; +import { useTheme } from "../themeContext"; +import ThemeToggle from "../../components/ThemeToggle"; const gov_logo = require('@/assets/images/gov_logo.png'); const billion_readers = require('@/assets/images/billion_readers.png'); @@ -55,6 +57,7 @@ const VideoList = () => { const router = useRouter(); const db = useSQLiteContext(); const { role, setRole } = useUser(); + const { isDarkMode } = useTheme(); const [open, setOpen] = useState(false); const [language, setLanguage] = useState("en"); const [loading, setLoading] = useState(true); @@ -186,8 +189,7 @@ const VideoList = () => { }; return ( - - + @@ -200,11 +202,34 @@ const VideoList = () => { if (levelOpen) setLevelOpen(false); }} setValue={handleLanguageChange} - containerStyle={{ maxWidth: 100, paddingVertical: 0, paddingHorizontal: 0, flex: 1.6 }} - style={{ height: 40, minHeight: 30 }} - textStyle={{ fontSize: 11 }} - arrowIconStyle={{ marginHorizontal: -5 }} + style={{ + borderColor: isDarkMode ? '#374151' : '#E5E7EB', + backgroundColor: isDarkMode ? '#1F2937' : '#FFFFFF', + width: 112, + height: 40, + zIndex: 20 + }} + textStyle={{ + color: isDarkMode ? '#F3F4F6' : '#1F2937', + }} + dropDownContainerStyle={{ + borderColor: isDarkMode ? '#374151' : '#E5E7EB', + backgroundColor: isDarkMode ? '#1F2937' : '#FFFFFF', + }} + theme={isDarkMode ? "DARK" : "LIGHT"} /> + + + + + + + + + Watch and Learn + + { if (open) setOpen(false); }} setValue={handleLevelChange} - containerStyle={{ maxWidth: 100, paddingVertical: 0, flex: 2, paddingHorizontal: 0 }} - style={{ height: 40, minHeight: 30 }} - textStyle={{ fontSize: 11 }} - arrowIconStyle={{ marginHorizontal: -5 }} + style={{ + borderColor: isDarkMode ? '#374151' : '#E5E7EB', + backgroundColor: isDarkMode ? '#1F2937' : '#FFFFFF', + width: 112, + height: 40, + zIndex: 10 + }} + textStyle={{ + color: isDarkMode ? '#F3F4F6' : '#1F2937', + }} + dropDownContainerStyle={{ + borderColor: isDarkMode ? '#374151' : '#E5E7EB', + backgroundColor: isDarkMode ? '#1F2937' : '#FFFFFF', + }} + theme={isDarkMode ? "DARK" : "LIGHT"} /> - router.push(`/login`)} delayLongPress={5000}> - - - { - loading ? ( - item.toString()} - renderItem={() => ( - - - - - - - - )} - /> - ) : ( + level === "all" || item.level === level)} + data={level === "all" ? videoDetails : videoDetails.filter(item => item.level === level)} keyExtractor={(item) => item.id} - renderItem={({ item }) => ( - - handleVideoPress(item)} - > - - - - {/* Video Details along with pdf and translation option */} - - - {videoLanguages[item.id] === "en" ? item.english_title : item.punjabi_title} - - - toggleVideoLanguage(item.id)} - className="bg-white p-2.5 rounded-full"> - - - handlePdfPress(item)} - className="bg-white p-2.5 rounded-full"> - - + numColumns={2} + showsVerticalScrollIndicator={false} + columnWrapperStyle={{ justifyContent: "space-between", marginBottom: 20 }} + renderItem={({ item }) => { + const isEnglish = videoLanguages[item.id] === "en"; + return ( + + + handleVideoPress(item)}> + + + + + + {isEnglish ? item.english_title : item.punjabi_title} + + + + toggleVideoLanguage(item.id)} + className="bg-purple-600 p-2 rounded-md" + > + + + + handlePdfPress(item)} + className="bg-purple-600 p-2 rounded-md" + > + + + - - )} + ); + }} /> - )} ); }; diff --git a/app/_layout.tsx b/app/_layout.tsx index 822f672..873df8e 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -11,14 +11,39 @@ import * as SplashScreen from 'expo-splash-screen'; import { SQLiteProvider } from 'expo-sqlite'; import { initializeDatabase } from './database/database'; import { UserProvider } from './userContext'; -import { downloadVideo,clearDownloadedVideos } from "./video/videoDownlaoder"; +import { ThemeProvider, useTheme } from './themeContext'; +import { downloadVideo, clearDownloadedVideos } from "./video/videoDownlaoder"; import { ProgressBar } from 'react-native-paper'; +import React from 'react'; // Prevent auto-hide at the start - SplashScreen.preventAutoHideAsync(); +// Main Layout component that applies theme +const ThemedLayout = () => { + const { isDarkMode } = useTheme(); + + return ( + + + + + + + + + ); +}; + export default function RootLayout() { const VIDEO_LIST = [ { id: '1_en', url: 'https://storage.googleapis.com/bird-planet-read/Videos/English/A_Cloud_of_Trash_English.mp4' }, @@ -94,20 +119,20 @@ export default function RootLayout() { preloadAssets(); }, []); - //download the videos on the first installation in the next installation check if they exists or not - useEffect(() => { - (async () => { - // Download all videos - // await clearDownloadedVideos(); // only for testing purposes don't use it in production - let completed = 0; - for (const video of VIDEO_LIST) { - await downloadVideo(video.id, video.url); - completed++; - setDownloadProgress(completed); - } - setVideoAssetsLoaded(true); - })(); - }, []); + //download the videos on the first installation in the next installation check if they exists or not + useEffect(() => { + (async () => { + // Download all videos + // await clearDownloadedVideos(); // only for testing purposes don't use it in production + let completed = 0; + for (const video of VIDEO_LIST) { + await downloadVideo(video.id, video.url); + completed++; + setDownloadProgress(completed); + } + setVideoAssetsLoaded(true); + })(); + }, []); const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }], @@ -116,16 +141,16 @@ export default function RootLayout() { return ( - <> - { isLoading && ( - - - )} - - { !isLoading && !videoAssetsLoaded && ( - //show a popup of number of videos downloading and stuff.... a progress bar modal + + <> + { isLoading && ( + + + )} + + { !isLoading && !videoAssetsLoaded && ( @@ -133,38 +158,29 @@ export default function RootLayout() { Downloading Videos... - {/* Show number of videos downloaded out of total */} {downloadProgress} videos downloaded out of {VIDEO_LIST.length} - {/* Progress Bar */} - {/* Optional: Estimated time or animated loading */} Please wait... - )} - - { !isLoading && videoAssetsLoaded && ( - - - - - - - - - - - - - )} - - ) - + )} + + { !isLoading && videoAssetsLoaded && ( + + + + + + + )} + + + ); } \ No newline at end of file diff --git a/app/dashboard/_layout.tsx b/app/dashboard/_layout.tsx index ad6c8fc..4c27b2e 100644 --- a/app/dashboard/_layout.tsx +++ b/app/dashboard/_layout.tsx @@ -1,8 +1,22 @@ import { Stack } from "expo-router"; +import { useTheme } from "../themeContext"; +import React from "react"; export default function Layout() { + const { isDarkMode } = useTheme(); + return ( - + ); diff --git a/app/dashboard/index.tsx b/app/dashboard/index.tsx index 1639aef..ee67d52 100644 --- a/app/dashboard/index.tsx +++ b/app/dashboard/index.tsx @@ -24,6 +24,7 @@ import DateTimePicker from '@react-native-community/datetimepicker'; import { DateTimePickerEvent } from "@react-native-community/datetimepicker"; import { Modal } from "react-native"; import { create } from "react-test-renderer"; +import { useTheme } from "../themeContext"; // Define type for analytics data @@ -46,6 +47,7 @@ type AnalyticsData = { const AnalyticsDashboard = () => { const db = useSQLiteContext(); + const { isDarkMode } = useTheme(); const [analyticsData, setAnalyticsData] = useState([]); const [userId, setUserId] = useState(null); @@ -324,10 +326,10 @@ const AnalyticsDashboard = () => { return ( - + Analytics @@ -339,10 +341,10 @@ const AnalyticsDashboard = () => { cover={0.8} /> - + {totalViews} - Views + Views @@ -353,60 +355,55 @@ const AnalyticsDashboard = () => { cover={0.8} /> - + {totalTime} - Watch Time + Watch Time setEditModalVisible(true)} -> - Edit Username : {username} - - - - - EXPORT - + className={`${isDarkMode ? 'bg-primary-dark' : 'bg-purple-700'} p-2 rounded mt-4`} + onPress={() => setEditModalVisible(true)} + > + Edit Username : {username} + - - {/* SyncToCloud component taking half width */} - - - - - {/* Delete button taking half width */} - - DELETE USER DATA + EXPORT - - + + {/* SyncToCloud component taking half width */} + + + + + {/* Delete button taking half width */} + + + DELETE USER DATA + + + {/* Level, Language and Date Dropdowns */} - Filter - - + Filter + {/* Level Dropdown */} @@ -420,22 +417,26 @@ const AnalyticsDashboard = () => { placeholder="Select Level" style={{ borderWidth: 1, - borderColor: "#d5d5d9", - backgroundColor: "#ECE6F0", + borderColor: isDarkMode ? "#374151" : "#d5d5d9", + backgroundColor: isDarkMode ? "#1F2937" : "#ECE6F0", borderRadius: 0, paddingHorizontal: 5, minHeight: 35, }} + textStyle={{ + color: isDarkMode ? "#F3F4F6" : "#000000", + }} dropDownContainerStyle={{ - backgroundColor: "#ECE6F0", - borderColor: "#d5d5d9", + backgroundColor: isDarkMode ? "#1F2937" : "#ECE6F0", + borderColor: isDarkMode ? "#374151" : "#d5d5d9", zIndex: 1000, borderRadius: 0, }} + theme={isDarkMode ? "DARK" : "LIGHT"} /> - {/* Language Dropdown */} - { placeholder="Select Lang" style={{ borderWidth: 1, - borderColor: "#d5d5d9", - backgroundColor: "#ECE6F0", + borderColor: isDarkMode ? "#374151" : "#d5d5d9", + backgroundColor: isDarkMode ? "#1F2937" : "#ECE6F0", borderRadius: 0, paddingHorizontal: 5, minHeight: 35, }} + textStyle={{ + color: isDarkMode ? "#F3F4F6" : "#000000", + }} dropDownContainerStyle={{ - backgroundColor: "#ECE6F0", - borderColor: "#d5d5d9", + backgroundColor: isDarkMode ? "#1F2937" : "#ECE6F0", + borderColor: isDarkMode ? "#374151" : "#d5d5d9", zIndex: 1000, borderRadius: 0, }} + theme={isDarkMode ? "DARK" : "LIGHT"} /> - - {/* Date Range Filter Section */} - - {/* Start Date Picker */} - setShowStartDatePicker(true)} - style={{ - borderWidth: 1, - borderColor: "#d5d5d9", - backgroundColor: "#ECE6F0", - padding: 8, - flexDirection: 'row', - alignItems: 'center', - flex: 1, - }} - > - - {startDate ? formatDate(startDate) : "Start Date"} - - - {/* End Date Picker */} + {/* Date Range Filter Section */} + + {/* Start Date Picker */} + setShowStartDatePicker(true)} + style={{ + borderWidth: 1, + borderColor: isDarkMode ? "#374151" : "#d5d5d9", + backgroundColor: isDarkMode ? "#1F2937" : "#ECE6F0", + padding: 8, + flexDirection: 'row', + alignItems: 'center', + flex: 1, + }} + > + + + {startDate ? formatDate(startDate) : "Start Date"} + + + + {/* End Date Picker */} + setShowEndDatePicker(true)} + style={{ + borderWidth: 1, + borderColor: isDarkMode ? "#374151" : "#d5d5d9", + backgroundColor: isDarkMode ? "#1F2937" : "#ECE6F0", + padding: 8, + flexDirection: 'row', + alignItems: 'center', + flex: 1, + }} + > + + + {endDate ? formatDate(endDate) : "End Date"} + + + + {/* Reset Button */} + {(startDate || endDate) && ( setShowEndDatePicker(true)} + onPress={resetDateFilters} style={{ borderWidth: 1, - borderColor: "#d5d5d9", - backgroundColor: "#ECE6F0", + borderColor: isDarkMode ? "#374151" : "#d5d5d9", + backgroundColor: isDarkMode ? "#4C1D95" : "#7e22ce", padding: 8, - flexDirection: 'row', alignItems: 'center', - flex: 1, + justifyContent: 'center', }} > - - {endDate ? formatDate(endDate) : "End Date"} + - - {/* Reset Button */} - {(startDate || endDate) && ( - - - - )} - - - - {/* Date Pickers (hidden by default) */} - {showStartDatePicker && ( - - )} - - {showEndDatePicker && ( - - )} - + )} + + {searchQuery.length > 0 && ( setSearchQuery("")}> - + )} - + - Videos + Videos - + Sort By { alignSelf: "center", marginBottom: 0, }} - textStyle={{ fontSize: 12 }} + textStyle={{ + fontSize: 12, + color: isDarkMode ? "#F3F4F6" : "#000000", + }} arrowIconStyle={{ marginHorizontal: -5 }} modalAnimationType="slide" placeholder={"Select"} style={{ borderWidth: 1, - borderColor: "#d5d5d9", - backgroundColor: "#ECE6F0", + borderColor: isDarkMode ? "#374151" : "#d5d5d9", + backgroundColor: isDarkMode ? "#1F2937" : "#ECE6F0", borderRadius: 0, paddingHorizontal: 5, minHeight: 35, zIndex: 100, }} dropDownContainerStyle={{ - backgroundColor: "#ECE6F0", + backgroundColor: isDarkMode ? "#1F2937" : "#ECE6F0", borderWidth: 1, - borderColor: "#d5d5d9", + borderColor: isDarkMode ? "#374151" : "#d5d5d9", borderRadius: 0, gap: 10, }} + theme={isDarkMode ? "DARK" : "LIGHT"} /> @@ -609,8 +602,8 @@ const AnalyticsDashboard = () => { data={filteredData} keyExtractor={(_, index) => index.toString()} renderItem={({ item }) => ( - - + + { {/* Video Details */} - + {item.language === "en" ? item.english_title : item.punjabi_title} - + Level: {item.level} - + Watch Time: {item.total_time_day} s - + Total Views: {item.total_views_day} - + Watched in:{" "} {item.language === "en" ? "English" : "Punjabi"} - + Last Watched: {item.date} @@ -658,58 +651,59 @@ const AnalyticsDashboard = () => { setEditModalVisible(false)} -> - - - <> - Edit Username from {username} to: - - - warning: - - setEditModalVisible(false)} - > - Cancel - - { - if (newUsername.trim()) { - setUsername(newUsername); - deleteData(); - editUserName(db, userId!, newUsername) - .then(() => { - setEditSuccess(true); - - setTimeout(() => { - setEditModalVisible(false); - setEditSuccess(false); - }, 1500); - }); - setNewUsername(""); - } - }} - > - Save - + animationType="slide" + transparent={true} + visible={editModalVisible} + onRequestClose={() => setEditModalVisible(false)} + > + + + <> + + Edit Username from {username} to: + + + + warning: + + setEditModalVisible(false)} + > + Cancel + + { + if (newUsername.trim()) { + setUsername(newUsername); + deleteData(); + editUserName(db, userId!, newUsername) + .then(() => { + setEditSuccess(true); + + setTimeout(() => { + setEditModalVisible(false); + setEditSuccess(false); + }, 1500); + }); + setNewUsername(""); + } + }} + > + Save + + + - - - - - - + + ); }; diff --git a/app/login/_layout.tsx b/app/login/_layout.tsx index ad6c8fc..4c65739 100644 --- a/app/login/_layout.tsx +++ b/app/login/_layout.tsx @@ -1,8 +1,22 @@ import { Stack } from "expo-router"; +import { useTheme } from "../themeContext"; +import React from "react"; -export default function Layout() { +export default function Layout() { + const { isDarkMode } = useTheme(); + return ( - + ); diff --git a/app/login/index.tsx b/app/login/index.tsx index 7da00f6..100fb19 100644 --- a/app/login/index.tsx +++ b/app/login/index.tsx @@ -5,13 +5,13 @@ import { Image } from 'react-native'; import { useState } from 'react'; import { useRouter } from 'expo-router'; import { Alert } from 'react-native'; +import { useTheme } from '../themeContext'; const index = () => { const id = process.env.EXPO_PUBLIC_ADMIN_ID const pass = process.env.EXPO_PUBLIC_ADMIN_PASSWORD + const { isDarkMode } = useTheme(); - console.log("Admin ID: ", id); - console.log("Admin Password: ", pass); const gov_logo = require('@/assets/images/billion_readers.png'); const [adminId, setAdminId] = useState(''); const [password, setPassword] = useState(''); @@ -26,7 +26,7 @@ const index = () => { }; return ( - + @@ -35,7 +35,8 @@ const index = () => { placeholder="Admin ID" value={adminId} onChangeText={setAdminId} - className="w-full bg-white p-3 mt-4 rounded-md" + className={`w-full p-3 mt-4 rounded-md ${isDarkMode ? 'bg-gray-800 text-text-dark border-gray-700 border' : 'bg-white text-black'}`} + placeholderTextColor={isDarkMode ? "#9CA3AF" : "#6B7280"} /> { value={password} onChangeText={setPassword} secureTextEntry - className="w-full bg-white p-3 mt-4 rounded-md" + className={`w-full p-3 mt-4 rounded-md ${isDarkMode ? 'bg-gray-800 text-text-dark border-gray-700 border' : 'bg-white text-black'}`} + placeholderTextColor={isDarkMode ? "#9CA3AF" : "#6B7280"} /> - + LOGIN diff --git a/app/pdf/[id].tsx b/app/pdf/[id].tsx index 59de0b7..fd43c4e 100644 --- a/app/pdf/[id].tsx +++ b/app/pdf/[id].tsx @@ -5,11 +5,13 @@ import * as FileSystem from 'expo-file-system'; import { Asset } from 'expo-asset'; import { useLocalSearchParams, useRouter } from 'expo-router'; import { videoDetails } from '../../assets/details'; +import { useTheme } from '../themeContext'; const PdfViewer = () => { const [pdfUri, setPdfUri] = useState(null); const [loading, setLoading] = useState(true); const router = useRouter(); + const { isDarkMode } = useTheme(); const { id, language } = useLocalSearchParams<{ id?: string; language?: string }>(); const video = videoDetails.find((v) => v.id === id); @@ -55,24 +57,24 @@ const PdfViewer = () => { }, []); return ( - + {/* Header with Back Button and Title */} - - {/* router.push("/")} className="p-2"> - - */} - - {title || 'PDF Viewer'} - - - + + router.push("/")} className="p-2"> + + + + {title || 'PDF Viewer'} + + {loading ? ( - + ) : pdfUri ? ( + ); diff --git a/app/settings.tsx b/app/settings.tsx new file mode 100644 index 0000000..54f7e99 --- /dev/null +++ b/app/settings.tsx @@ -0,0 +1,92 @@ +import React from 'react'; +import { View, Text, ScrollView, TouchableOpacity } from 'react-native'; +import { Stack, useRouter } from 'expo-router'; +import { Ionicons } from '@expo/vector-icons'; +import { useTheme } from './themeContext'; +import ThemeToggle from '../components/ThemeToggle'; + +const SettingsScreen = () => { + const router = useRouter(); + const { isDarkMode } = useTheme(); + + return ( + + ( + router.back()} + className="ml-4" + > + + + ), + }} + /> + + + + + Appearance + + + Customize the app's appearance + + + + + Theme + + + + + + + You can choose between Light, Dark, or System theme. The System option will follow your device's theme settings. + + + + + + + About + + + Information about this app + + + + + Version + + + 1.0.0 + + + + + + Made with + + + ❤️ in India + + + + + + ); +}; + +export default SettingsScreen; \ No newline at end of file diff --git a/app/themeContext.tsx b/app/themeContext.tsx new file mode 100644 index 0000000..a4d7eac --- /dev/null +++ b/app/themeContext.tsx @@ -0,0 +1,82 @@ +import React, { createContext, useState, useEffect, useContext, ReactNode } from 'react'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { useColorScheme } from 'react-native'; + +type ThemeType = 'light' | 'dark' | 'system'; + +interface ThemeContextType { + theme: ThemeType; + isDarkMode: boolean; + setTheme: (theme: ThemeType) => void; + toggleTheme: () => void; +} + +const ThemeContext = createContext(undefined); + +export const ThemeProvider = ({ children }: { children: ReactNode }) => { + const systemColorScheme = useColorScheme(); + const [theme, setThemeState] = useState('system'); + + // Computed property to determine if dark mode is active + const isDarkMode = theme === 'system' + ? systemColorScheme === 'dark' + : theme === 'dark'; + + // Load saved theme preference on initial render + useEffect(() => { + const loadTheme = async () => { + try { + const savedTheme = await AsyncStorage.getItem('theme'); + if (savedTheme) { + setThemeState(savedTheme as ThemeType); + } + } catch (error) { + console.error('Failed to load theme preference:', error); + } + }; + + loadTheme(); + }, []); + + // Save theme preference whenever it changes + useEffect(() => { + const saveTheme = async () => { + try { + await AsyncStorage.setItem('theme', theme); + } catch (error) { + console.error('Failed to save theme preference:', error); + } + }; + + saveTheme(); + }, [theme]); + + // Function to update theme + const setTheme = (newTheme: ThemeType) => { + setThemeState(newTheme); + }; + + // Convenience function to toggle between light and dark + const toggleTheme = () => { + if (theme === 'system') { + setThemeState(systemColorScheme === 'dark' ? 'light' : 'dark'); + } else { + setThemeState(theme === 'dark' ? 'light' : 'dark'); + } + }; + + return ( + + {children} + + ); +}; + +// Custom hook to use the ThemeContext +export const useTheme = () => { + const context = useContext(ThemeContext); + if (!context) { + throw new Error('useTheme must be used within a ThemeProvider'); + } + return context; +}; \ No newline at end of file diff --git a/app/video/[id].tsx b/app/video/[id].tsx index 6d3fb1a..5a3eab6 100644 --- a/app/video/[id].tsx +++ b/app/video/[id].tsx @@ -1,22 +1,21 @@ import { useLocalSearchParams } from "expo-router"; import { useVideoPlayer, VideoView } from "expo-video"; -import { StyleSheet, View, Text, TouchableOpacity, Image,TouchableWithoutFeedback } from "react-native"; +import { StyleSheet, View, Text, TouchableOpacity, Image, TouchableWithoutFeedback } from "react-native"; import { videoDetails } from "@/assets/details"; import { useEffect, useState } from "react"; import * as ScreenOrientation from "expo-screen-orientation"; import { useRouter } from "expo-router"; import { useKeepAwake } from 'expo-keep-awake'; import { useSQLiteContext } from "expo-sqlite"; -import { getVideoAnalyticsByUser,getUsers } from "../database/database"; +import { getVideoAnalyticsByUser, getUsers } from "../database/database"; import { getVideoUri } from "./videoDownlaoder"; import { BackHandler } from "react-native"; // for handling back button press on android - - -//Here back issue is solved but controls by default they are showing...... +import { useTheme } from "../themeContext"; export default function VideoScreen() { useKeepAwake(); const router = useRouter(); + const { isDarkMode } = useTheme(); const { id, language } = useLocalSearchParams<{ id?: string; language?: string }>(); const [originalOrientation, setOriginalOrientation] = useState(); const back = require('@/assets/images/back.png'); @@ -45,21 +44,12 @@ export default function VideoScreen() { fetchVideoUri(); }, []); + const handlePress = () => { setShowControls(true); setTimeout(() => setShowControls(false), 3000); // Hide controls after 3 seconds }; - // useEffect(() => { - // if (video) { - // setVideoSource(language === "pa" ? video.url_punjabi : video.url_en); - // } - // }, [video, language]); - - - - - // Move player up here and modify const player = useVideoPlayer( videoSource || '', @@ -85,29 +75,6 @@ export default function VideoScreen() { return () => backHandler.remove(); // Cleanup when unmounting }, [player, originalOrientation, router]); // Add dependencies - - // Remove the conditional return here - // if (!video || !fileUri) { - // return ( - // - // - - // const player = useVideoPlayer( - // language === "pa" ? fileUri : fileUri, - // async (player) => { - // player.loop = false; - - // // Store the original orientation before changing - // const currentOrientation = await ScreenOrientation.getOrientationAsync(); - // setOriginalOrientation(currentOrientation); - - // // Change to landscape mode automatically - // await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE); - - // await player.play(); - // } - // ); - // Restore original orientation when exiting useEffect(() => { return () => { @@ -117,7 +84,6 @@ export default function VideoScreen() { }; }, [originalOrientation]); - const updateVideoAnalytics = async (watchedTime: number) => { try { const users = await getUsers(db); @@ -128,7 +94,6 @@ export default function VideoScreen() { const today = new Date().toISOString().split("T")[0]; // Get YYYY-MM-DD format const lastWatchedTimestamp = new Date().toISOString(); // Get full timestamp - // Check if the analytics entry exists for this user, video, language, and date const existingRecords = await db.getAllAsync( "SELECT * FROM video_analytics WHERE user_id = ? AND video_id = ? AND language = ? AND date = ?", @@ -160,7 +125,6 @@ export default function VideoScreen() { } }; - const returnBackToHome = async () => { const watchedTime = Math.floor(player.currentTime); console.log(`Watched Till: ${watchedTime} seconds`); @@ -178,13 +142,12 @@ export default function VideoScreen() { }; return ( - - + - + + ); diff --git a/components/ThemeToggle.tsx b/components/ThemeToggle.tsx new file mode 100644 index 0000000..0a5ba69 --- /dev/null +++ b/components/ThemeToggle.tsx @@ -0,0 +1,118 @@ +import React from 'react'; +import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; +import { Ionicons } from '@expo/vector-icons'; +import { useTheme } from '../app/themeContext'; + +interface ThemeToggleProps { + showLabel?: boolean; + size?: number; + containerStyle?: object; +} + +const ThemeToggle: React.FC = ({ + showLabel = true, + size = 24, + containerStyle = {} +}) => { + const { theme, setTheme, isDarkMode } = useTheme(); + + // Function to determine the icon based on current theme + const getThemeIcon = () => { + switch (theme) { + case 'light': + return 'sunny'; + case 'dark': + return 'moon'; + case 'system': + return isDarkMode ? 'moon' : 'sunny'; + default: + return 'sunny'; + } + }; + + // Cycle through themes: light -> dark -> system -> light + const cycleTheme = () => { + if (theme === 'light') { + setTheme('dark'); + } else if (theme === 'dark') { + setTheme('system'); + } else { + setTheme('light'); + } + }; + + // Get the theme label + const getThemeLabel = () => { + switch (theme) { + case 'light': + return 'Light'; + case 'dark': + return 'Dark'; + case 'system': + return 'System'; + default: + return 'Light'; + } + }; + + return ( + + + + {showLabel && ( + + {getThemeLabel()} + + )} + + + ); +}; + +const styles = StyleSheet.create({ + container: { + alignItems: 'center', + }, + button: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 8, + paddingHorizontal: 12, + borderRadius: 8, + gap: 6, + }, + buttonLight: { + backgroundColor: '#F9FAFB', + borderWidth: 1, + borderColor: '#E5E7EB', + }, + buttonDark: { + backgroundColor: '#1F2937', + borderWidth: 1, + borderColor: '#374151', + }, + label: { + fontWeight: '500', + fontSize: 14, + }, + labelLight: { + color: '#1F2937', + }, + labelDark: { + color: '#F3F4F6', + }, +}); + +export default ThemeToggle; \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index 38f7798..b9ed08b 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,10 +1,35 @@ /** @type {import('tailwindcss').Config} */ module.exports = { // NOTE: Update this to include the paths to all of your component files. - content: ["./app/**/*.{js,jsx,ts,tsx}"], + content: ["./app/**/*.{js,jsx,ts,tsx}", "./components/**/*.{js,jsx,ts,tsx}"], + darkMode: "class", presets: [require("nativewind/preset")], theme: { - extend: {}, + extend: { + colors: { + primary: { + DEFAULT: "#6B21A8", // purple-800 + dark: "#4C1D95", // purple-900 + light: "#8B5CF6", // purple-500 + }, + background: { + light: "#FFFFFF", + dark: "#121212", + }, + text: { + light: "#1F2937", // gray-800 + dark: "#F3F4F6", // gray-100 + }, + surface: { + light: "#F9FAFB", // gray-50 + dark: "#1F2937", // gray-800 + }, + border: { + light: "#E5E7EB", // gray-200 + dark: "#374151", // gray-700 + }, + }, + }, }, plugins: [], } \ No newline at end of file