From be236b64df3913dcc37c560a62964e6b301b6bf0 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sun, 20 Jul 2025 19:28:56 +0200 Subject: [PATCH] Dark mode --- xtablo-expo/app/(app)/(tabs)/index.tsx | 159 ++++++-- xtablo-expo/app/(app)/(tabs)/planning.tsx | 373 ++++++++++++------ xtablo-expo/app/(app)/(tabs)/settings.tsx | 102 +++-- xtablo-expo/app/(app)/(tabs)/tablos.tsx | 118 ++++-- xtablo-expo/app/(app)/channel/[cid].tsx | 62 ++- xtablo-expo/app/(app)/user/profile.tsx | 244 ++++++------ xtablo-expo/app/login.tsx | 52 ++- xtablo-expo/app/signup.tsx | 40 +- xtablo-expo/components/LoadingView.tsx | 27 +- .../components/SwipeableChannelPreview.tsx | 40 +- xtablo-expo/providers/ChatProvider.tsx | 103 ++++- 11 files changed, 940 insertions(+), 380 deletions(-) diff --git a/xtablo-expo/app/(app)/(tabs)/index.tsx b/xtablo-expo/app/(app)/(tabs)/index.tsx index 127c17b..1bb802f 100644 --- a/xtablo-expo/app/(app)/(tabs)/index.tsx +++ b/xtablo-expo/app/(app)/(tabs)/index.tsx @@ -16,15 +16,24 @@ import { useTablosList } from "@/hooks/tablos"; import { ColorMap } from "@/constants/colors"; import { UserTablo } from "@/types/tablos.types"; import { SwipeableChannelPreview } from "@/components/SwipeableChannelPreview"; +import { useThemeColor } from "@/hooks/useThemeColor"; +import { useColorScheme } from "@/hooks/useColorScheme"; // Custom Avatar Component for Channel List // Custom Title Component for bigger channel names const CustomChannelTitle = ({ channel }: { channel: Channel }) => { const channelName = channel?.data?.name || channel?.id || "Channel"; + const textColor = useThemeColor( + { light: "#1f2937", dark: "#f9fafb" }, + "text" + ); return ( - + {channelName} ); @@ -45,6 +54,54 @@ const CustomChannelPreview = ( export default function HomeScreen() { const user = useUser(); const { data: tablos, isLoading } = useTablosList(); + const colorScheme = useColorScheme(); + + // Theme-aware colors + const backgroundColor = useThemeColor( + { light: "#f8fafc", dark: "#111827" }, + "background" + ); + const textColor = useThemeColor( + { light: "#1f2937", dark: "#f9fafb" }, + "text" + ); + const subtitleColor = useThemeColor( + { light: "#6b7280", dark: "#9ca3af" }, + "text" + ); + const cardBackgroundColor = useThemeColor( + { light: "#ffffff", dark: "#1f2937" }, + "background" + ); + const borderColor = colorScheme === "dark" ? "#374151" : "#e5e7eb"; + const placeholderColor = useThemeColor( + { light: "#9ca3af", dark: "#6b7280" }, + "text" + ); + const iconColor = useThemeColor( + { light: "#6b7280", dark: "#9ca3af" }, + "text" + ); + const emptyTextColor = useThemeColor( + { light: "#4b5563", dark: "#d1d5db" }, + "text" + ); + const emptyIconColor = useThemeColor( + { light: "#d1d5db", dark: "#6b7280" }, + "text" + ); + + // Theme-aware gradient colors + const gradientColors: [string, string, string] = + colorScheme === "dark" + ? ["#1f2937", "#374151", "#4b5563"] + : ["#1e3a8a", "#3b82f6", "#60a5fa"]; + + const loadingColors = { + container: colorScheme === "dark" ? "#1f2937" : "#f8fafc", + item: colorScheme === "dark" ? "#374151" : "#e5e7eb", + itemSecondary: colorScheme === "dark" ? "#4b5563" : "#f3f4f6", + }; // const animations = useSharedValue>({}); @@ -179,7 +236,9 @@ export default function HomeScreen() { /> {/* Status indicator (online/active) */} - + ); }; @@ -289,12 +348,15 @@ export default function HomeScreen() { if (isLoading) { return ( - - + + {/* Loading Header */} {/* Loading Content */} - + {/* Loading Skeleton Items */} {[1, 2, 3, 4, 5].map((item) => ( - + - - + + ))} @@ -333,12 +415,15 @@ export default function HomeScreen() { } return ( - - + + {/* Beautiful Header */} {/* Channel List with animated search */} - + {/* */} ( - + {/* {debouncedSearchQuery ? "Recherche en cours..." : "Chargement..."} */} @@ -420,14 +507,18 @@ export default function HomeScreen() { // Show empty state when no results EmptyStateIndicator={() => ( - - + + {/* {debouncedSearchQuery ? "Aucun résultat" : "Aucune conversation"} */} Aucune conversation - + {/* {debouncedSearchQuery ? `Aucune conversation trouvée pour "${debouncedSearchQuery}"` : "Vous n'avez pas encore de conversations"} */} @@ -444,7 +535,7 @@ export default function HomeScreen() { const styles = StyleSheet.create({ container: { flex: 1, - backgroundColor: "#f8fafc", + // backgroundColor is set dynamically }, headerGradient: { paddingTop: 50, @@ -575,7 +666,7 @@ const styles = StyleSheet.create({ }, channelListContainer: { flex: 1, - backgroundColor: "#f8fafc", + // backgroundColor is set dynamically marginTop: -10, borderTopLeftRadius: 10, borderTopRightRadius: 10, @@ -630,7 +721,7 @@ const styles = StyleSheet.create({ borderRadius: 8, backgroundColor: "#10b981", borderWidth: 3, - borderColor: "white", + // borderColor is set dynamically to match background shadowColor: "#000", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, @@ -664,26 +755,26 @@ const styles = StyleSheet.create({ customChannelTitle: { fontSize: 18, fontWeight: "bold", - color: "#1f2937", + // color is set dynamically }, // Search Header Styles searchHeaderContainer: { - backgroundColor: "#f8fafc", + // backgroundColor is set dynamically borderBottomWidth: 1, - borderBottomColor: "#e5e7eb", + // borderBottomColor is set dynamically overflow: "hidden", paddingHorizontal: 20, }, searchInputContainer: { flexDirection: "row", alignItems: "center", - backgroundColor: "white", + // backgroundColor is set dynamically borderRadius: 8, paddingHorizontal: 12, paddingVertical: 8, borderWidth: 1, - borderColor: "#e5e7eb", + // borderColor is set dynamically marginTop: 15, }, searchIcon: { @@ -692,7 +783,7 @@ const styles = StyleSheet.create({ searchInput: { flex: 1, fontSize: 16, - color: "#374151", + // color is set dynamically paddingVertical: 0, fontWeight: "500", }, @@ -705,7 +796,7 @@ const styles = StyleSheet.create({ }, searchInfoText: { fontSize: 14, - color: "#6b7280", + // color is set dynamically }, searchLoadingContainer: { paddingVertical: 20, @@ -713,7 +804,7 @@ const styles = StyleSheet.create({ }, searchLoadingText: { fontSize: 16, - color: "#6b7280", + // color is set dynamically }, emptySearchContainer: { paddingVertical: 40, @@ -721,12 +812,12 @@ const styles = StyleSheet.create({ }, emptySearchTitle: { fontSize: 20, - color: "#4b5563", + // color is set dynamically marginTop: 10, }, emptySearchMessage: { fontSize: 16, - color: "#6b7280", + // color is set dynamically marginTop: 5, textAlign: "center", paddingHorizontal: 20, @@ -735,7 +826,7 @@ const styles = StyleSheet.create({ // Loading Skeleton Styles loadingContentContainer: { flex: 1, - backgroundColor: "#f8fafc", + // backgroundColor is set dynamically marginTop: -10, borderTopLeftRadius: 10, borderTopRightRadius: 10, @@ -752,7 +843,7 @@ const styles = StyleSheet.create({ width: 56, height: 56, borderRadius: 16, - backgroundColor: "#e5e7eb", + // backgroundColor is set dynamically marginRight: 12, }, loadingTextContainer: { @@ -760,14 +851,14 @@ const styles = StyleSheet.create({ }, loadingTitle: { height: 20, - backgroundColor: "#e5e7eb", + // backgroundColor is set dynamically borderRadius: 4, marginBottom: 8, width: "70%", }, loadingSubtitle: { height: 16, - backgroundColor: "#f3f4f6", + // backgroundColor is set dynamically borderRadius: 4, width: "50%", }, diff --git a/xtablo-expo/app/(app)/(tabs)/planning.tsx b/xtablo-expo/app/(app)/(tabs)/planning.tsx index dea8769..3c0abff 100644 --- a/xtablo-expo/app/(app)/(tabs)/planning.tsx +++ b/xtablo-expo/app/(app)/(tabs)/planning.tsx @@ -31,6 +31,8 @@ import { EventAndTablo, EventInsert } from "@/types/events.types"; import { useTablosList } from "@/hooks/tablos"; import { UserTablo } from "@/types/tablos.types"; import { ColorMap } from "@/constants/colors"; +import { useThemeColor } from "@/hooks/useThemeColor"; +import { useColorScheme } from "@/hooks/useColorScheme"; type ViewMode = "month" | "week"; @@ -74,6 +76,33 @@ export default function PlanningScreen() { const { data: events } = useEventsByTablo(null); const { data: tablos } = useTablosList(); const createEvent = useCreateEvent(); + const colorScheme = useColorScheme(); + + // Theme-aware colors + const backgroundColor = useThemeColor( + { light: "#f8fafc", dark: "#111827" }, + "background" + ); + const cardBackgroundColor = useThemeColor( + { light: "#ffffff", dark: "#1f2937" }, + "background" + ); + const textColor = useThemeColor( + { light: "#1f2937", dark: "#f9fafb" }, + "text" + ); + const subtitleColor = useThemeColor( + { light: "#6b7280", dark: "#9ca3af" }, + "text" + ); + const borderColor = colorScheme === "dark" ? "#374151" : "#e5e7eb"; + const modalOverlayColor = + colorScheme === "dark" ? "rgba(0, 0, 0, 0.8)" : "rgba(0, 0, 0, 0.5)"; + const inactiveDayColor = colorScheme === "dark" ? "#4b5563" : "#d1d5db"; + const emptyIconColor = colorScheme === "dark" ? "#6b7280" : "#d1d5db"; + const viewModeToggleColor = colorScheme === "dark" ? "#374151" : "#f3f4f6"; + const weekHeaderBorderColor = colorScheme === "dark" ? "#374151" : "#e5e7eb"; + const selectedOptionBgColor = colorScheme === "dark" ? "#1e3a8a" : "#eff6ff"; const filteredEvents: EventAndTablo[] = (selectedTablo === null @@ -176,7 +205,7 @@ export default function PlanningScreen() { const renderTabloOption = ({ item }: { item: UserTablo }) => ( selectTablo(item)} > @@ -189,7 +218,9 @@ export default function PlanningScreen() { ]} /> - {item.name} + + {item.name} + {selectedTablo?.id === item.id && } @@ -197,22 +228,31 @@ export default function PlanningScreen() { ); const renderEvent = ({ item }: { item: EventAndTablo }) => ( - + - {item.title} + + {item.title} + - - + + {item.start_time?.substring(0, 5)} - - {item.tablo_name} + + + {item.tablo_name} + @@ -233,7 +273,8 @@ export default function PlanningScreen() { ))} {dayEvents.length > 6 && ( - - + + +{dayEvents.length - 6} @@ -318,14 +369,16 @@ export default function PlanningScreen() { return ( - + {selectedDate.toLocaleDateString("fr-FR", { weekday: "long", day: "numeric", month: "long", })} - + {selectedDayEvents.length} événement {selectedDayEvents.length > 1 ? "s" : ""} @@ -342,9 +395,11 @@ export default function PlanningScreen() { /> ) : ( - - Aucun événement - + + + Aucun événement + + Vous n'avez aucun événement prévu pour cette date. @@ -358,12 +413,17 @@ export default function PlanningScreen() { const todayEvents = getEventsForDate(selectedDate); return ( - + {/* Header */} - Planning - + + Planning + + {viewMode === "month" ? months[currentMonth.getMonth()] + " " + @@ -386,7 +446,12 @@ export default function PlanningScreen() { {/* View Mode Toggle */} - + @@ -416,11 +482,12 @@ export default function PlanningScreen() { > @@ -433,13 +500,20 @@ export default function PlanningScreen() { {/* Tablo Selector */} setShowTabloSelector(true)} > - +
- Tablo actuel + + Tablo actuel + - + {selectedTablo?.name ?? "Tous les tablos"} - + {/* Calendar/Week View */} - + @@ -473,7 +552,7 @@ export default function PlanningScreen() { > - + {viewMode === "month" ? `${ months[currentMonth.getMonth()] @@ -495,9 +574,17 @@ export default function PlanningScreen() { {viewMode === "month" ? ( <> - + {daysOfWeek.map((day, i) => ( - + {day} ))} @@ -524,7 +611,7 @@ export default function PlanningScreen() { - + Événements du jour ({todayEvents.length}) @@ -539,9 +626,11 @@ export default function PlanningScreen() { /> ) : ( - - Aucun événement - + + + Aucun événement + + Vous n'avez aucun événement prévu pour cette date. @@ -557,13 +646,22 @@ export default function PlanningScreen() { onRequestClose={() => setShowTabloSelector(false)} > setShowTabloSelector(false)} > - - - Choisir un tablo + + + + Choisir un tablo + selectTablo(null)} > @@ -580,12 +681,14 @@ export default function PlanningScreen() { style={[ styles.tabloColorDot, { - backgroundColor: "#6b7280", + backgroundColor: subtitleColor, }, ]} /> - + Tous les tablos @@ -606,15 +709,34 @@ export default function PlanningScreen() { animationType="slide" onRequestClose={() => setShowCreateEventModal(false)} > - - - - Nouvel événement + + + + + Nouvel événement + setShowCreateEventModal(false)} style={styles.createEventCloseButton} > - + @@ -624,57 +746,92 @@ export default function PlanningScreen() { > {/* Title Field */} - Titre * + + Titre * + setNewEvent({ ...newEvent, title: text }) } placeholder="Titre de l'événement" - placeholderTextColor="#9ca3af" + placeholderTextColor={subtitleColor} /> {/* Date Field */} - Date + + Date + setNewEvent({ ...newEvent, start_date: text }) } placeholder="YYYY-MM-DD" - placeholderTextColor="#9ca3af" + placeholderTextColor={subtitleColor} /> {/* Time Field */} - Heure + + Heure + setNewEvent({ ...newEvent, start_time: text }) } placeholder="HH:MM" - placeholderTextColor="#9ca3af" + placeholderTextColor={subtitleColor} /> {/* Tablo Selector */} - Tablo * + + Tablo * + ( setNewEvent({ ...newEvent, tablo_id: item.id }) @@ -693,6 +850,12 @@ export default function PlanningScreen() { item.id} showsVerticalScrollIndicator={false} scrollEnabled={false} - style={styles.tabloListInForm} /> - + setShowCreateEventModal(false)} > - Annuler + + Annuler + state.signOut); const user = useUser(); + const colorScheme = useColorScheme(); + + // Theme-aware colors + const backgroundColor = useThemeColor( + { light: "#f8fafc", dark: "#111827" }, + "background" + ); + const cardBackgroundColor = useThemeColor( + { light: "#ffffff", dark: "#1f2937" }, + "background" + ); + const textColor = useThemeColor( + { light: "#1f2937", dark: "#f9fafb" }, + "text" + ); + const subtitleColor = useThemeColor( + { light: "#6b7280", dark: "#9ca3af" }, + "text" + ); + const borderColor = colorScheme === "dark" ? "#374151" : "#e5e7eb"; + + // Theme-aware gradient colors + const gradientColors: [string, string, string] = + colorScheme === "dark" + ? ["#1f2937", "#374151", "#4b5563"] + : ["#1e3a8a", "#3b82f6", "#60a5fa"]; // Settings state const [pushNotifications, setPushNotifications] = useState(true); const [emailNotifications, setEmailNotifications] = useState(true); - const [darkMode, setDarkMode] = useState(false); const [biometricAuth, setBiometricAuth] = useState(false); const handleSignOut = () => { @@ -72,8 +99,17 @@ export default function SettingsScreen() { const renderSettingsSection = (title: string, children: React.ReactNode) => ( - {title} - {children} + + {title} + + + {children} + ); @@ -86,7 +122,7 @@ export default function SettingsScreen() { showArrow: boolean = true ) => ( {icon} - {title} + + {title} + {subtitle && ( - {subtitle} + + {subtitle} + )} {rightComponent} {showArrow && onPress && ( - + )} @@ -124,20 +170,26 @@ export default function SettingsScreen() { , false ); return ( - - + + {/* Header */} - + {/* Account Section */} {renderSettingsSection( "Compte", @@ -198,9 +253,14 @@ export default function SettingsScreen() { {renderSwitchItem( , "Mode sombre", - "Utiliser le thème sombre", - darkMode, - setDarkMode + "Thème système automatique", + colorScheme === "dark", + () => { + Alert.alert( + "Thème automatique", + "Le thème suit automatiquement les préférences de votre appareil. Modifiez le thème dans les paramètres de votre appareil." + ); + } )} )} @@ -310,7 +370,6 @@ export default function SettingsScreen() { const styles = StyleSheet.create({ container: { flex: 1, - backgroundColor: "#f8fafc", }, headerGradient: { paddingTop: 50, @@ -353,7 +412,6 @@ const styles = StyleSheet.create({ }, content: { flex: 1, - backgroundColor: "#f8fafc", marginTop: -10, borderTopLeftRadius: 10, borderTopRightRadius: 10, @@ -366,12 +424,10 @@ const styles = StyleSheet.create({ sectionTitle: { fontSize: 18, fontWeight: "600", - color: "#1f2937", marginBottom: 12, marginLeft: 4, }, sectionContent: { - backgroundColor: "white", borderRadius: 16, shadowColor: "#000", shadowOffset: { width: 0, height: 2 }, @@ -379,6 +435,7 @@ const styles = StyleSheet.create({ shadowRadius: 8, elevation: 3, overflow: "hidden", + borderWidth: 1, }, settingsItem: { flexDirection: "row", @@ -387,7 +444,6 @@ const styles = StyleSheet.create({ paddingHorizontal: 20, paddingVertical: 16, borderBottomWidth: 1, - borderBottomColor: "#f3f4f6", }, settingsItemLeft: { flexDirection: "row", @@ -398,7 +454,7 @@ const styles = StyleSheet.create({ width: 40, height: 40, borderRadius: 20, - backgroundColor: "#f3f4f6", + backgroundColor: "rgba(0, 0, 0, 0.05)", justifyContent: "center", alignItems: "center", marginRight: 12, @@ -409,12 +465,10 @@ const styles = StyleSheet.create({ settingsItemTitle: { fontSize: 16, fontWeight: "500", - color: "#1f2937", marginBottom: 2, }, settingsItemSubtitle: { fontSize: 14, - color: "#6b7280", }, settingsItemRight: { flexDirection: "row", diff --git a/xtablo-expo/app/(app)/(tabs)/tablos.tsx b/xtablo-expo/app/(app)/(tabs)/tablos.tsx index 8234b4b..e4135c2 100644 --- a/xtablo-expo/app/(app)/(tabs)/tablos.tsx +++ b/xtablo-expo/app/(app)/(tabs)/tablos.tsx @@ -30,6 +30,8 @@ import { } from "lucide-react-native"; import { router } from "expo-router"; import { AVAILABLE_COLORS, ColorMap } from "@/constants/colors"; +import { useThemeColor } from "@/hooks/useThemeColor"; +import { useColorScheme } from "@/hooks/useColorScheme"; const { width } = Dimensions.get("window"); const numColumns = 2; @@ -46,6 +48,32 @@ export default function TablosScreen() { const [isCreateModalVisible, setIsCreateModalVisible] = useState(false); const { mutate: createTablo } = useCreateTablo(); const { mutate: deleteTablo } = useDeleteTablo(); + const colorScheme = useColorScheme(); + + // Theme-aware colors + const backgroundColor = useThemeColor( + { light: "#f8fafc", dark: "#111827" }, + "background" + ); + const cardBackgroundColor = useThemeColor( + { light: "#ffffff", dark: "#1f2937" }, + "background" + ); + const textColor = useThemeColor( + { light: "#1f2937", dark: "#f9fafb" }, + "text" + ); + const subtitleColor = useThemeColor( + { light: "#6b7280", dark: "#9ca3af" }, + "text" + ); + const borderColor = colorScheme === "dark" ? "#374151" : "#e5e7eb"; + + // Theme-aware gradient colors + const gradientColors: [string, string, string] = + colorScheme === "dark" + ? ["#1f2937", "#374151", "#4b5563"] + : ["#1e3a8a", "#3b82f6", "#60a5fa"]; const [newTablo, setNewTablo] = useState<{ name: string; @@ -190,7 +218,11 @@ export default function TablosScreen() { navigateToTablo(tablo)} onLongPress={() => handleDeleteTablo(tablo)} @@ -216,7 +248,10 @@ export default function TablosScreen() { {/* Tablo Info */} - + {tablo.name} - + router.push("/planning")} > - + @@ -275,7 +310,10 @@ export default function TablosScreen() { const renderListItem = ({ item: tablo }: { item: UserTablo }) => { return ( navigateToTablo(tablo)} onLongPress={() => handleDeleteTablo(tablo)} activeOpacity={0.8} @@ -294,7 +332,9 @@ export default function TablosScreen() { - {tablo.name} + + {tablo.name} + - + router.push("/planning")} > - + @@ -369,12 +409,15 @@ export default function TablosScreen() { } return ( - - + + {/* Beautiful Header */} {/* Content */} - + {isLoading && !refreshing ? ( - Chargement de vos tablos... + + Chargement de vos tablos... + ) : filteredTablos && filteredTablos.length > 0 ? ( ) : ( - Aucun tablo trouvé - + + Aucun tablo trouvé + + {filterStatus === "all" ? "Vous n'avez encore aucun tablo. Créez votre premier tablo pour commencer !" : `Aucun tablo avec le statut "${getStatusLabel( @@ -485,20 +532,37 @@ export default function TablosScreen() { behavior={Platform.OS === "ios" ? "padding" : "height"} style={styles.modalOverlay} > - + setIsCreateModalVisible(false)} > - + - Nouveau Tablo + + Nouveau Tablo + - Nom du Tablo + + Nom du Tablo + setNewTablo({ ...newTablo, name: text }) @@ -506,7 +570,9 @@ export default function TablosScreen() { /> - Couleur + + Couleur + {AVAILABLE_COLORS.map((color) => ( - Statut + + Statut + {(["todo", "in_progress", "done"] as const).map((status) => ( (); @@ -25,9 +27,32 @@ export default function ChannelScreen() { const channel = client.channel(type, id); const [hasMessages, setHasMessages] = useState(false); const [isLoading, setIsLoading] = useState(true); + const colorScheme = useColorScheme(); const headerHeight = useHeaderHeight(); + // Theme-aware colors + const backgroundColor = useThemeColor( + { light: "#f8fafc", dark: "#111827" }, + "background" + ); + const textColor = useThemeColor( + { light: "#1f2937", dark: "#f9fafb" }, + "text" + ); + const subtitleColor = useThemeColor( + { light: "#6b7280", dark: "#9ca3af" }, + "text" + ); + const iconColor = useThemeColor( + { light: "#d1d5db", dark: "#6b7280" }, + "icon" + ); + const iconSecondaryColor = useThemeColor( + { light: "#e5e7eb", dark: "#4b5563" }, + "icon" + ); + useEffect(() => { if (channel) { const checkMessages = async () => { @@ -64,31 +89,35 @@ export default function ChannelScreen() { const EmptyState = () => ( - + - + - + - Commencez la conversation - + + Commencez la conversation + + Soyez le premier à envoyer un message dans ce canal ! ); return ( - + {isLoading ? ( - Chargement des messages... + + Chargement des messages... + ) : hasMessages ? ( @@ -112,12 +141,10 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: "center", alignItems: "center", - backgroundColor: "#f8fafc", gap: 16, }, loadingText: { fontSize: 16, - color: "#6b7280", fontWeight: "500", }, emptyContainer: { @@ -125,7 +152,6 @@ const styles = StyleSheet.create({ justifyContent: "center", alignItems: "center", paddingHorizontal: 40, - backgroundColor: "#f8fafc", }, emptyIconContainer: { position: "relative", @@ -144,7 +170,7 @@ const styles = StyleSheet.create({ position: "absolute", top: 10, right: 5, - backgroundColor: "white", + backgroundColor: "rgba(255, 255, 255, 0.9)", borderRadius: 15, padding: 6, shadowColor: "#000", @@ -157,7 +183,7 @@ const styles = StyleSheet.create({ position: "absolute", bottom: 15, left: 8, - backgroundColor: "white", + backgroundColor: "rgba(255, 255, 255, 0.9)", borderRadius: 12, padding: 5, shadowColor: "#000", @@ -169,24 +195,22 @@ const styles = StyleSheet.create({ emptyTitle: { fontSize: 24, fontWeight: "bold", - color: "#1f2937", marginBottom: 12, textAlign: "center", }, emptyMessage: { fontSize: 16, - color: "#6b7280", textAlign: "center", lineHeight: 24, marginBottom: 32, }, emptyHint: { - backgroundColor: "white", + backgroundColor: "rgba(255, 255, 255, 0.9)", paddingHorizontal: 20, paddingVertical: 12, borderRadius: 20, borderWidth: 1, - borderColor: "#e5e7eb", + borderColor: "rgba(0, 0, 0, 0.1)", shadowColor: "#000", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.05, @@ -195,14 +219,10 @@ const styles = StyleSheet.create({ }, emptyHintText: { fontSize: 14, - color: "#9ca3af", fontWeight: "500", textAlign: "center", }, keyboardContainer: { - backgroundColor: "#f8fafc", - }, - messageInputContainer: { - backgroundColor: "#f8fafc", + flexShrink: 0, }, }); diff --git a/xtablo-expo/app/(app)/user/profile.tsx b/xtablo-expo/app/(app)/user/profile.tsx index ee0183c..cb64a84 100644 --- a/xtablo-expo/app/(app)/user/profile.tsx +++ b/xtablo-expo/app/(app)/user/profile.tsx @@ -20,6 +20,8 @@ import { Settings, Shield, } from "lucide-react-native"; +import { Stack } from "expo-router"; +import { SafeAreaView } from "react-native-safe-area-context"; export default function ProfileScreen() { const signOut = useAuthStore((state) => state.signOut); @@ -49,132 +51,140 @@ export default function ProfileScreen() { ]; return ( - - - - - {user.name || "Utilisateur"} - {user.email} - - - - {/* Contenu principal */} - - {/* Carte d'informations personnelles */} - - - - - Informations personnelles - + + + + + + + {user.name || "Utilisateur"} + {user.email} + - - - + {/* Contenu principal */} + + {/* Carte d'informations personnelles */} + + + + + Informations personnelles + - - Adresse e-mail - {user.email} + + + + + + + Adresse e-mail + {user.email} + - - + - - - - - - Nom d'affichage - {isEditing ? ( - - ) : ( - - {user.name || "Non défini"} - - )} - - { - if (isEditing) { - handleSaveDisplayName(); - } else { - setDisplayName(user.name ?? ""); - setIsEditing(true); - } - }} - > - {isEditing ? ( - - ) : ( - - )} - - - - - {/* Éléments de menu */} - - - - Préférences - - - {menuItems.map((item, index) => ( - - - - - - - {item.title} - {item.subtitle} - - + + + + + + Nom d'affichage + {isEditing ? ( + + ) : ( + + {user.name || "Non défini"} + + )} + + { + if (isEditing) { + handleSaveDisplayName(); + } else { + setDisplayName(user.name ?? ""); + setIsEditing(true); + } + }} + > + {isEditing ? ( + + ) : ( + + )} - {index < menuItems.length - 1 && } - ))} - + - {/* Bouton de déconnexion */} - - - - Se déconnecter - - - - + {/* Éléments de menu */} + + + + Préférences + + + {menuItems.map((item, index) => ( + + + + + + + {item.title} + {item.subtitle} + + + + {index < menuItems.length - 1 && ( + + )} + + ))} + + + {/* Bouton de déconnexion */} + + + + Se déconnecter + + + + + ); } diff --git a/xtablo-expo/app/login.tsx b/xtablo-expo/app/login.tsx index 7d9f699..653a22e 100644 --- a/xtablo-expo/app/login.tsx +++ b/xtablo-expo/app/login.tsx @@ -6,6 +6,8 @@ import { Link } from "expo-router"; import { Mail, Lock } from "lucide-react-native"; import { GoogleLoginButton } from "@/components/GoogleLoginButton"; import { AppleLoginButton } from "@/components/AppleLoginButton"; +import { useThemeColor } from "@/hooks/useThemeColor"; +import { useColorScheme } from "@/hooks/useColorScheme"; export default function Auth() { const [email, setEmail] = useState(""); @@ -14,12 +16,34 @@ export default function Auth() { const login = useAuthStore((state) => state.login); const authLoading = useAuthStore((state) => state.loading); const performOAuth = useAuthStore((state) => state.performOAuth); + const colorScheme = useColorScheme(); + + // Theme-aware colors + const backgroundColor = useThemeColor( + { light: "#f5f5f5", dark: "#111827" }, + "background" + ); + const textColor = useThemeColor({ light: "#333", dark: "#f9fafb" }, "text"); + const subtitleColor = useThemeColor( + { light: "#666", dark: "#9ca3af" }, + "text" + ); + const separatorColor = useThemeColor( + { light: "#ddd", dark: "#374151" }, + "text" + ); + const linkColor = useThemeColor( + { light: "#3b82f6", dark: "#60a5fa" }, + "text" + ); return ( - + - Connexion XTablo - Connectez-vous à votre compte + Connexion XTablo + + Connectez-vous à votre compte + - - ou - + + ou + performOAuth("google")} /> @@ -62,8 +86,10 @@ export default function Auth() { performOAuth("apple")} /> - Pas encore de compte ? - + + Pas encore de compte ?{" "} + + S'inscrire @@ -76,7 +102,6 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: "center", padding: 16, - backgroundColor: "#f5f5f5", // Light grey background }, logo: { width: 80, @@ -90,13 +115,11 @@ const styles = StyleSheet.create({ fontWeight: "bold", textAlign: "center", marginBottom: 3, - color: "#333", }, subtitle: { fontSize: 14, textAlign: "center", marginBottom: 16, - color: "#666", }, verticallySpaced: { paddingTop: 2, @@ -132,11 +155,9 @@ const styles = StyleSheet.create({ separator: { flex: 1, height: 1, - backgroundColor: "#ddd", }, separatorText: { marginHorizontal: 15, - color: "#666", fontSize: 14, }, linkContainer: { @@ -144,11 +165,8 @@ const styles = StyleSheet.create({ justifyContent: "center", marginTop: 12, }, - linkText: { - color: "#666", - }, + linkText: {}, link: { - color: "#007bff", fontWeight: "bold", }, }); diff --git a/xtablo-expo/app/signup.tsx b/xtablo-expo/app/signup.tsx index b59ecc2..55b54bf 100644 --- a/xtablo-expo/app/signup.tsx +++ b/xtablo-expo/app/signup.tsx @@ -4,6 +4,7 @@ import { Button, Input } from "@rn-vui/themed"; import { useAuthStore } from "@/stores/auth"; import { Link } from "expo-router"; import { Mail, Lock, User, Building2 } from "lucide-react-native"; +import { useThemeColor } from "@/hooks/useThemeColor"; export default function SignUp() { const [firstName, setFirstName] = useState(""); @@ -15,11 +16,30 @@ export default function SignUp() { const signUp = useAuthStore((state) => state.signUp); const authLoading = useAuthStore((state) => state.loading); + // Theme-aware colors + const backgroundColor = useThemeColor( + { light: "#f5f5f5", dark: "#111827" }, + "background" + ); + const textColor = useThemeColor({ light: "#333", dark: "#f9fafb" }, "text"); + const subtitleColor = useThemeColor( + { light: "#666", dark: "#9ca3af" }, + "text" + ); + const linkColor = useThemeColor( + { light: "#3b82f6", dark: "#60a5fa" }, + "text" + ); + return ( - + - Créer un compte XTablo - Rejoignez-nous ! + + Créer un compte XTablo + + + Rejoignez-nous ! + - Vous avez déjà un compte ? - + + Vous avez déjà un compte ?{" "} + + Se connecter @@ -99,7 +121,6 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: "center", padding: 16, - backgroundColor: "#f5f5f5", // Light grey background }, logo: { width: 80, @@ -113,13 +134,11 @@ const styles = StyleSheet.create({ fontWeight: "bold", textAlign: "center", marginBottom: 3, - color: "#333", }, subtitle: { fontSize: 14, textAlign: "center", marginBottom: 16, - color: "#666", }, verticallySpaced: { paddingTop: 2, @@ -167,11 +186,8 @@ const styles = StyleSheet.create({ justifyContent: "center", marginTop: 12, }, - linkText: { - color: "#666", - }, + linkText: {}, link: { - color: "#007bff", fontWeight: "bold", }, }); diff --git a/xtablo-expo/components/LoadingView.tsx b/xtablo-expo/components/LoadingView.tsx index 07a76ab..2d47d22 100644 --- a/xtablo-expo/components/LoadingView.tsx +++ b/xtablo-expo/components/LoadingView.tsx @@ -9,11 +9,26 @@ import Animated, { } from "react-native-reanimated"; import { ThemedView } from "@/components/ThemedView"; import { ThemedText } from "@/components/ThemedText"; +import { useThemeColor } from "@/hooks/useThemeColor"; export const LoadingView = () => { const rotation = useSharedValue(0); const width = useSharedValue(80); + // Theme-aware colors + const backgroundColor = useThemeColor( + { light: "#f8fafc", dark: "#111827" }, + "background" + ); + const textColor = useThemeColor( + { light: "#1f2937", dark: "#f9fafb" }, + "text" + ); + const subtitleColor = useThemeColor( + { light: "#6b7280", dark: "#9ca3af" }, + "text" + ); + rotation.value = withRepeat( withTiming(360, { easing: Easing.linear, duration: 2000 }), -1, @@ -37,7 +52,7 @@ export const LoadingView = () => { }); return ( - + { style={[styles.logo, animatedStyle]} /> - + XTablo - + Initialisation de l'application... @@ -61,7 +79,6 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: "center", alignItems: "center", - backgroundColor: "#f8fafc", }, loadingContent: { alignItems: "center", @@ -86,13 +103,11 @@ const styles = StyleSheet.create({ fontWeight: "bold", textAlign: "center", marginBottom: 8, - color: "#1f2937", }, subtitle: { fontSize: 16, textAlign: "center", marginBottom: 32, - color: "#6b7280", opacity: 0.8, }, }); diff --git a/xtablo-expo/components/SwipeableChannelPreview.tsx b/xtablo-expo/components/SwipeableChannelPreview.tsx index 3b78baf..90af0b4 100644 --- a/xtablo-expo/components/SwipeableChannelPreview.tsx +++ b/xtablo-expo/components/SwipeableChannelPreview.tsx @@ -19,6 +19,8 @@ import Animated, { import { Archive } from "lucide-react-native"; import { Channel } from "stream-chat"; import { DefaultStreamChatGenerics } from "stream-chat-expo"; +import { useThemeColor } from "@/hooks/useThemeColor"; +import { useColorScheme } from "@/hooks/useColorScheme"; interface SwipeableChannelPreviewProps { channel: Channel; @@ -32,6 +34,19 @@ export const SwipeableChannelPreview: React.FC< SwipeableChannelPreviewProps > = ({ channel, children }) => { const translateX = useSharedValue(0); + const colorScheme = useColorScheme(); + + // Theme-aware colors + const backgroundColor = useThemeColor( + { light: "#ffffff", dark: "#1f1f1f" }, + "background" + ); + const textColor = useThemeColor( + { light: "#ffffff", dark: "#ffffff" }, + "text" + ); + const archiveButtonColor = colorScheme === "dark" ? "#0f4a3c" : "#166534"; + const iconColor = "#ffffff"; const handleArchiveChannel = async () => { try { @@ -136,17 +151,31 @@ export const SwipeableChannelPreview: React.FC< {/* Right Actions Background */} - + - - Archiver + + + Archiver + {/* Channel Content */} - + {children} @@ -160,7 +189,6 @@ const styles = StyleSheet.create({ position: "relative", }, channelContainer: { - backgroundColor: "white", flex: 1, }, rightActionsContainer: { @@ -173,7 +201,6 @@ const styles = StyleSheet.create({ alignItems: "center", }, archiveButton: { - backgroundColor: "#166534", // Dark green color for archive justifyContent: "center", alignItems: "center", width: ACTION_WIDTH, @@ -186,7 +213,6 @@ const styles = StyleSheet.create({ gap: 4, }, actionText: { - color: "white", fontSize: 12, fontWeight: "600", textAlign: "center", diff --git a/xtablo-expo/providers/ChatProvider.tsx b/xtablo-expo/providers/ChatProvider.tsx index f9f1ee7..f4eb9bf 100644 --- a/xtablo-expo/providers/ChatProvider.tsx +++ b/xtablo-expo/providers/ChatProvider.tsx @@ -2,6 +2,10 @@ import { ActivityIndicator, View } from "react-native"; import { Chat, OverlayProvider, useCreateChatClient } from "stream-chat-expo"; import { useUser } from "@/providers/UserProvider"; import { Text } from "react-native"; +import { useThemeColor } from "@/hooks/useThemeColor"; +import { useColorScheme } from "@/hooks/useColorScheme"; +import { useMemo } from "react"; +import type { DeepPartial, Theme } from "stream-chat-expo"; export default function ChatProvider({ children, @@ -9,6 +13,92 @@ export default function ChatProvider({ children: React.ReactNode; }) { const user = useUser(); + const colorScheme = useColorScheme(); + + // Theme-aware colors + const backgroundColor = useThemeColor( + { light: "#f8fafc", dark: "#111827" }, + "background" + ); + const textColor = useThemeColor( + { light: "#1f2937", dark: "#f9fafb" }, + "text" + ); + + // Aggressive Stream Chat theme to force correct backgrounds + const streamChatTheme: DeepPartial = useMemo( + () => ({ + colors: { + // Force ALL background-related colors to match page background + white: backgroundColor, + white_snow: backgroundColor, + white_smoke: backgroundColor, + grey_whisper: backgroundColor, + // Keep text colors appropriate + black: colorScheme === "dark" ? "#f9fafb" : "#1f2937", + grey: colorScheme === "dark" ? "#9ca3af" : "#6b7280", + grey_gainsboro: colorScheme === "dark" ? "#4b5563" : "#9ca3af", + + // Accent colors + accent_blue: "#3b82f6", + accent_green: "#10b981", + accent_red: "#ef4444", + + // Overlays + overlay: + colorScheme === "dark" ? "rgba(0, 0, 0, 0.8)" : "rgba(0, 0, 0, 0.3)", + // Border colors + border: colorScheme === "dark" ? "#374151" : "#e5e7eb", + }, + + // Force channel list backgrounds + channelListMessenger: { + flatList: { + backgroundColor: backgroundColor, + }, + flatListContent: { + backgroundColor: backgroundColor, + }, + container: { + backgroundColor: backgroundColor, + }, + }, + + // Force channel preview backgrounds + channelPreview: { + container: { + backgroundColor: backgroundColor, + }, + title: { + color: colorScheme === "dark" ? "#f9fafb" : "#1f2937", + }, + message: { + color: colorScheme === "dark" ? "#9ca3af" : "#6b7280", + }, + date: { + color: colorScheme === "dark" ? "#6b7280" : "#9ca3af", + }, + }, + + // Force message backgrounds + messageSimple: { + content: { + container: { + backgroundColor: backgroundColor, + }, + }, + }, + + // Force input backgrounds + messageInput: { + container: { + backgroundColor: backgroundColor, + borderTopColor: colorScheme === "dark" ? "#374151" : "#e5e7eb", + }, + }, + }), + [colorScheme, backgroundColor] + ); const client = useCreateChatClient({ apiKey: process.env.EXPO_PUBLIC_STREAM_CHAT_API_KEY as string, @@ -22,8 +112,15 @@ export default function ChatProvider({ if (!user.streamToken) { return ( - - Chat Indisponible + + Chat Indisponible ); } @@ -33,7 +130,7 @@ export default function ChatProvider({ } return ( - + {children} );