Dark mode

This commit is contained in:
Arthur Belleville 2025-07-20 19:28:56 +02:00
parent 527f4a8fb1
commit be236b64df
No known key found for this signature in database
11 changed files with 940 additions and 380 deletions

View file

@ -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 (
<Text style={styles.customChannelTitle} numberOfLines={1}>
<Text
style={[styles.customChannelTitle, { color: textColor }]}
numberOfLines={1}
>
{channelName}
</Text>
);
@ -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<Record<string, number>>({});
@ -179,7 +236,9 @@ export default function HomeScreen() {
/>
{/* Status indicator (online/active) */}
<View style={styles.statusIndicator} />
<View
style={[styles.statusIndicator, { borderColor: cardBackgroundColor }]}
/>
</View>
);
};
@ -289,12 +348,15 @@ export default function HomeScreen() {
if (isLoading) {
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<View style={[styles.container, { backgroundColor }]}>
<StatusBar
barStyle={colorScheme === "dark" ? "light-content" : "light-content"}
backgroundColor={gradientColors[0]}
/>
{/* Loading Header */}
<LinearGradient
colors={["#1e3a8a", "#3b82f6", "#60a5fa"]}
colors={gradientColors}
style={styles.headerGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
@ -316,14 +378,34 @@ export default function HomeScreen() {
</LinearGradient>
{/* Loading Content */}
<View style={styles.loadingContentContainer}>
<View
style={[
styles.loadingContentContainer,
{ backgroundColor: loadingColors.container },
]}
>
{/* Loading Skeleton Items */}
{[1, 2, 3, 4, 5].map((item) => (
<View key={item} style={styles.loadingItem}>
<View style={styles.loadingAvatar} />
<View
style={[
styles.loadingAvatar,
{ backgroundColor: loadingColors.item },
]}
/>
<View style={styles.loadingTextContainer}>
<View style={styles.loadingTitle} />
<View style={styles.loadingSubtitle} />
<View
style={[
styles.loadingTitle,
{ backgroundColor: loadingColors.item },
]}
/>
<View
style={[
styles.loadingSubtitle,
{ backgroundColor: loadingColors.itemSecondary },
]}
/>
</View>
</View>
))}
@ -333,12 +415,15 @@ export default function HomeScreen() {
}
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<View style={[styles.container, { backgroundColor }]}>
<StatusBar
barStyle={colorScheme === "dark" ? "light-content" : "light-content"}
backgroundColor={gradientColors[0]}
/>
{/* Beautiful Header */}
<LinearGradient
colors={["#1e3a8a", "#3b82f6", "#60a5fa"]}
colors={gradientColors}
style={styles.headerGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
@ -374,7 +459,7 @@ export default function HomeScreen() {
</LinearGradient>
{/* Channel List with animated search */}
<View style={styles.channelListContainer}>
<View style={[styles.channelListContainer, { backgroundColor }]}>
{/* <SearchHeader /> */}
<ChannelList
filters={filters}
@ -409,7 +494,9 @@ export default function HomeScreen() {
// Show loading state during search
LoadingIndicator={() => (
<View style={styles.searchLoadingContainer}>
<Text style={styles.searchLoadingText}>
<Text
style={[styles.searchLoadingText, { color: subtitleColor }]}
>
{/* {debouncedSearchQuery
? "Recherche en cours..."
: "Chargement..."} */}
@ -420,14 +507,18 @@ export default function HomeScreen() {
// Show empty state when no results
EmptyStateIndicator={() => (
<View style={styles.emptySearchContainer}>
<Search size={48} color="#d1d5db" />
<Text style={styles.emptySearchTitle}>
<Search size={48} color={emptyIconColor} />
<Text
style={[styles.emptySearchTitle, { color: emptyTextColor }]}
>
{/* {debouncedSearchQuery
? "Aucun résultat"
: "Aucune conversation"} */}
Aucune conversation
</Text>
<Text style={styles.emptySearchMessage}>
<Text
style={[styles.emptySearchMessage, { color: subtitleColor }]}
>
{/* {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%",
},

View file

@ -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 }) => (
<TouchableOpacity
style={styles.tabloOption}
style={[styles.tabloOption, { borderBottomColor: borderColor }]}
onPress={() => selectTablo(item)}
>
<View style={styles.tabloOptionLeft}>
@ -189,7 +218,9 @@ export default function PlanningScreen() {
]}
/>
<View style={styles.tabloOptionContent}>
<Text style={styles.tabloOptionName}>{item.name}</Text>
<Text style={[styles.tabloOptionName, { color: textColor }]}>
{item.name}
</Text>
</View>
</View>
{selectedTablo?.id === item.id && <Check size={20} color="#3b82f6" />}
@ -197,22 +228,31 @@ export default function PlanningScreen() {
);
const renderEvent = ({ item }: { item: EventAndTablo }) => (
<TouchableOpacity style={styles.eventCard}>
<TouchableOpacity
style={[
styles.eventCard,
{ backgroundColor: cardBackgroundColor, borderColor },
]}
>
<View
style={[styles.eventColorBar, { backgroundColor: item.tablo_color }]}
/>
<View style={styles.eventContent}>
<Text style={styles.eventTitle}>{item.title}</Text>
<Text style={[styles.eventTitle, { color: textColor }]}>
{item.title}
</Text>
<View style={styles.eventDetails}>
<View style={styles.eventDetailItem}>
<Clock size={14} color="#6b7280" />
<Text style={styles.eventDetailText}>
<Clock size={14} color={subtitleColor} />
<Text style={[styles.eventDetailText, { color: subtitleColor }]}>
{item.start_time?.substring(0, 5)}
</Text>
</View>
<View style={styles.eventDetailItem}>
<MapPin size={14} color="#6b7280" />
<Text style={styles.eventDetailText}>{item.tablo_name}</Text>
<MapPin size={14} color={subtitleColor} />
<Text style={[styles.eventDetailText, { color: subtitleColor }]}>
{item.tablo_name}
</Text>
</View>
</View>
</View>
@ -233,7 +273,8 @@ export default function PlanningScreen() {
<Text
style={[
styles.dayText,
!isCurrentMonth(date) && styles.inactiveDayText,
{ color: textColor },
!isCurrentMonth(date) && { color: inactiveDayColor },
isSelected(date) && styles.selectedDayText,
isToday(date) && !isSelected(date) && styles.todayDayText,
]}
@ -252,6 +293,7 @@ export default function PlanningScreen() {
<TouchableOpacity
style={[
styles.weekDayHeader,
{ backgroundColor: cardBackgroundColor },
isToday(date) && styles.todayWeekDay,
isSelected(date) && styles.selectedWeekDay,
isToday(date) && isSelected(date) && styles.todaySelectedWeekDay,
@ -261,6 +303,7 @@ export default function PlanningScreen() {
<Text
style={[
styles.weekDayName,
{ color: subtitleColor },
isSelected(date) && { color: "white" },
isToday(date) && isSelected(date) && { color: "white" },
]}
@ -270,6 +313,7 @@ export default function PlanningScreen() {
<Text
style={[
styles.weekDayNumber,
{ color: textColor },
isSelected(date) && styles.selectedWeekDayText,
isToday(date) && !isSelected(date) && styles.todayWeekDayText,
isToday(date) &&
@ -300,8 +344,15 @@ export default function PlanningScreen() {
/>
))}
{dayEvents.length > 6 && (
<View style={styles.weekEventMoreCircle}>
<Text style={styles.weekEventMoreText}>
<View
style={[
styles.weekEventMoreCircle,
{ backgroundColor: borderColor },
]}
>
<Text
style={[styles.weekEventMoreText, { color: subtitleColor }]}
>
+{dayEvents.length - 6}
</Text>
</View>
@ -318,14 +369,16 @@ export default function PlanningScreen() {
return (
<View style={styles.selectedDayEventsContainer}>
<View style={styles.selectedDayHeader}>
<Text style={styles.selectedDayTitle}>
<Text style={[styles.selectedDayTitle, { color: textColor }]}>
{selectedDate.toLocaleDateString("fr-FR", {
weekday: "long",
day: "numeric",
month: "long",
})}
</Text>
<Text style={styles.selectedDayEventsCount}>
<Text
style={[styles.selectedDayEventsCount, { color: subtitleColor }]}
>
{selectedDayEvents.length} événement
{selectedDayEvents.length > 1 ? "s" : ""}
</Text>
@ -342,9 +395,11 @@ export default function PlanningScreen() {
/>
) : (
<View style={styles.selectedDayEmptyState}>
<CalendarIcon size={40} color="#d1d5db" />
<Text style={styles.emptyStateTitle}>Aucun événement</Text>
<Text style={styles.emptyStateText}>
<CalendarIcon size={40} color={emptyIconColor} />
<Text style={[styles.emptyStateTitle, { color: textColor }]}>
Aucun événement
</Text>
<Text style={[styles.emptyStateText, { color: subtitleColor }]}>
Vous n'avez aucun événement prévu pour cette date.
</Text>
</View>
@ -358,12 +413,17 @@ export default function PlanningScreen() {
const todayEvents = getEventsForDate(selectedDate);
return (
<ScrollView style={styles.container} showsVerticalScrollIndicator={false}>
<ScrollView
style={[styles.container, { backgroundColor }]}
showsVerticalScrollIndicator={false}
>
{/* Header */}
<View style={styles.header}>
<View>
<Text style={styles.headerTitle}>Planning</Text>
<Text style={styles.headerSubtitle}>
<Text style={[styles.headerTitle, { color: textColor }]}>
Planning
</Text>
<Text style={[styles.headerSubtitle, { color: subtitleColor }]}>
{viewMode === "month"
? months[currentMonth.getMonth()] +
" " +
@ -386,7 +446,12 @@ export default function PlanningScreen() {
{/* View Mode Toggle */}
<View style={styles.viewModeContainer}>
<View style={styles.viewModeToggle}>
<View
style={[
styles.viewModeToggle,
{ backgroundColor: viewModeToggleColor },
]}
>
<TouchableOpacity
style={[
styles.viewModeButton,
@ -396,11 +461,12 @@ export default function PlanningScreen() {
>
<Grid3x3
size={18}
color={viewMode === "month" ? "white" : "#6b7280"}
color={viewMode === "month" ? "white" : subtitleColor}
/>
<Text
style={[
styles.viewModeText,
{ color: viewMode === "month" ? "white" : subtitleColor },
viewMode === "month" && styles.activeViewModeText,
]}
>
@ -416,11 +482,12 @@ export default function PlanningScreen() {
>
<Rows3
size={18}
color={viewMode === "week" ? "white" : "#6b7280"}
color={viewMode === "week" ? "white" : subtitleColor}
/>
<Text
style={[
styles.viewModeText,
{ color: viewMode === "week" ? "white" : subtitleColor },
viewMode === "week" && styles.activeViewModeText,
]}
>
@ -433,13 +500,20 @@ export default function PlanningScreen() {
{/* Tablo Selector */}
<View style={styles.tabloSelectorContainer}>
<TouchableOpacity
style={styles.tabloSelector}
style={[
styles.tabloSelector,
{ backgroundColor: cardBackgroundColor, borderColor },
]}
onPress={() => setShowTabloSelector(true)}
>
<View style={styles.tabloSelectorLeft}>
<Table size={20} color="#6b7280" />
<Table size={20} color={subtitleColor} />
<View style={styles.tabloSelectorContent}>
<Text style={styles.tabloSelectorLabel}>Tablo actuel</Text>
<Text
style={[styles.tabloSelectorLabel, { color: subtitleColor }]}
>
Tablo actuel
</Text>
<View style={styles.tabloSelectorRow}>
<View
style={[
@ -447,22 +521,27 @@ export default function PlanningScreen() {
{
backgroundColor: selectedTablo?.color
? ColorMap[selectedTablo.color]
: "#6b7280",
: subtitleColor,
},
]}
/>
<Text style={styles.tabloSelectorName}>
<Text style={[styles.tabloSelectorName, { color: textColor }]}>
{selectedTablo?.name ?? "Tous les tablos"}
</Text>
</View>
</View>
</View>
<ChevronDown size={20} color="#6b7280" />
<ChevronDown size={20} color={subtitleColor} />
</TouchableOpacity>
</View>
{/* Calendar/Week View */}
<Card containerStyle={styles.calendarCard}>
<Card
containerStyle={[
styles.calendarCard,
{ backgroundColor: cardBackgroundColor },
]}
>
<View style={styles.calendarHeader}>
<TouchableOpacity
onPress={() =>
@ -473,7 +552,7 @@ export default function PlanningScreen() {
>
<ChevronLeft size={24} color="#3b82f6" />
</TouchableOpacity>
<Text style={styles.calendarTitle}>
<Text style={[styles.calendarTitle, { color: textColor }]}>
{viewMode === "month"
? `${
months[currentMonth.getMonth()]
@ -495,9 +574,17 @@ export default function PlanningScreen() {
{viewMode === "month" ? (
<>
<View style={styles.weekHeader}>
<View
style={[
styles.weekHeader,
{ borderBottomColor: weekHeaderBorderColor },
]}
>
{daysOfWeek.map((day, i) => (
<Text key={`${day}-${i}`} style={styles.weekDay}>
<Text
key={`${day}-${i}`}
style={[styles.weekDay, { color: subtitleColor }]}
>
{day}
</Text>
))}
@ -524,7 +611,7 @@ export default function PlanningScreen() {
<View style={styles.eventsSection}>
<View style={styles.eventsSectionHeader}>
<CalendarIcon size={20} color="#3b82f6" />
<Text style={styles.eventsSectionTitle}>
<Text style={[styles.eventsSectionTitle, { color: textColor }]}>
Événements du jour ({todayEvents.length})
</Text>
</View>
@ -539,9 +626,11 @@ export default function PlanningScreen() {
/>
) : (
<View style={styles.selectedDayEmptyState}>
<CalendarIcon size={40} color="#d1d5db" />
<Text style={styles.emptyStateTitle}>Aucun événement</Text>
<Text style={styles.emptyStateText}>
<CalendarIcon size={40} color={emptyIconColor} />
<Text style={[styles.emptyStateTitle, { color: textColor }]}>
Aucun événement
</Text>
<Text style={[styles.emptyStateText, { color: subtitleColor }]}>
Vous n'avez aucun événement prévu pour cette date.
</Text>
</View>
@ -557,13 +646,22 @@ export default function PlanningScreen() {
onRequestClose={() => setShowTabloSelector(false)}
>
<TouchableOpacity
style={styles.modalOverlay}
style={[styles.modalOverlay, { backgroundColor: modalOverlayColor }]}
activeOpacity={1}
onPress={() => setShowTabloSelector(false)}
>
<View style={styles.modalContent}>
<View style={styles.modalHeader}>
<Text style={styles.modalTitle}>Choisir un tablo</Text>
<View
style={[
styles.modalContent,
{ backgroundColor: cardBackgroundColor, borderColor },
]}
>
<View
style={[styles.modalHeader, { borderBottomColor: borderColor }]}
>
<Text style={[styles.modalTitle, { color: textColor }]}>
Choisir un tablo
</Text>
</View>
<FlatList
data={tablos ?? []}
@ -572,7 +670,10 @@ export default function PlanningScreen() {
showsVerticalScrollIndicator={false}
ListHeaderComponent={
<TouchableOpacity
style={styles.tabloOption}
style={[
styles.tabloOption,
{ borderBottomColor: borderColor },
]}
onPress={() => selectTablo(null)}
>
<View style={styles.tabloOptionLeft}>
@ -580,12 +681,14 @@ export default function PlanningScreen() {
style={[
styles.tabloColorDot,
{
backgroundColor: "#6b7280",
backgroundColor: subtitleColor,
},
]}
/>
<View style={styles.tabloOptionContent}>
<Text style={styles.tabloOptionName}>
<Text
style={[styles.tabloOptionName, { color: textColor }]}
>
Tous les tablos
</Text>
</View>
@ -606,15 +709,34 @@ export default function PlanningScreen() {
animationType="slide"
onRequestClose={() => setShowCreateEventModal(false)}
>
<View style={styles.createEventModalOverlay}>
<View style={styles.createEventModalContent}>
<View style={styles.createEventModalHeader}>
<Text style={styles.createEventModalTitle}>Nouvel événement</Text>
<View
style={[
styles.createEventModalOverlay,
{ backgroundColor: modalOverlayColor },
]}
>
<View
style={[
styles.createEventModalContent,
{ backgroundColor: cardBackgroundColor },
]}
>
<View
style={[
styles.createEventModalHeader,
{ borderBottomColor: borderColor },
]}
>
<Text
style={[styles.createEventModalTitle, { color: textColor }]}
>
Nouvel événement
</Text>
<TouchableOpacity
onPress={() => setShowCreateEventModal(false)}
style={styles.createEventCloseButton}
>
<X size={24} color="#6b7280" />
<X size={24} color={subtitleColor} />
</TouchableOpacity>
</View>
@ -624,57 +746,92 @@ export default function PlanningScreen() {
>
{/* Title Field */}
<View style={styles.formField}>
<Text style={styles.formLabel}>Titre *</Text>
<Text style={[styles.formLabel, { color: subtitleColor }]}>
Titre *
</Text>
<TextInput
style={styles.formInput}
style={[
styles.formInput,
{
backgroundColor: cardBackgroundColor,
borderColor,
color: textColor,
},
]}
value={newEvent.title}
onChangeText={(text) =>
setNewEvent({ ...newEvent, title: text })
}
placeholder="Titre de l'événement"
placeholderTextColor="#9ca3af"
placeholderTextColor={subtitleColor}
/>
</View>
{/* Date Field */}
<View style={styles.formField}>
<Text style={styles.formLabel}>Date</Text>
<Text style={[styles.formLabel, { color: subtitleColor }]}>
Date
</Text>
<TextInput
style={styles.formInput}
style={[
styles.formInput,
{
backgroundColor: cardBackgroundColor,
borderColor,
color: textColor,
},
]}
value={newEvent.start_date}
onChangeText={(text) =>
setNewEvent({ ...newEvent, start_date: text })
}
placeholder="YYYY-MM-DD"
placeholderTextColor="#9ca3af"
placeholderTextColor={subtitleColor}
/>
</View>
{/* Time Field */}
<View style={styles.formField}>
<Text style={styles.formLabel}>Heure</Text>
<Text style={[styles.formLabel, { color: subtitleColor }]}>
Heure
</Text>
<TextInput
style={styles.formInput}
style={[
styles.formInput,
{
backgroundColor: cardBackgroundColor,
borderColor,
color: textColor,
},
]}
value={newEvent.start_time}
onChangeText={(text) =>
setNewEvent({ ...newEvent, start_time: text })
}
placeholder="HH:MM"
placeholderTextColor="#9ca3af"
placeholderTextColor={subtitleColor}
/>
</View>
{/* Tablo Selector */}
<View style={styles.formField}>
<Text style={styles.formLabel}>Tablo *</Text>
<Text style={[styles.formLabel, { color: subtitleColor }]}>
Tablo *
</Text>
<FlatList
style={[
styles.tabloListInForm,
{ backgroundColor: cardBackgroundColor, borderColor },
]}
data={tablos ?? []}
renderItem={({ item }) => (
<TouchableOpacity
style={[
styles.tabloOptionInForm,
newEvent.tablo_id === item.id &&
styles.selectedTabloOptionInForm,
{ borderBottomColor: borderColor },
newEvent.tablo_id === item.id && {
backgroundColor: selectedOptionBgColor,
},
]}
onPress={() =>
setNewEvent({ ...newEvent, tablo_id: item.id })
@ -693,6 +850,12 @@ export default function PlanningScreen() {
<Text
style={[
styles.tabloOptionNameInForm,
{
color:
newEvent.tablo_id === item.id
? "#3b82f6"
: textColor,
},
newEvent.tablo_id === item.id &&
styles.selectedTabloOptionNameInForm,
]}
@ -708,17 +871,23 @@ export default function PlanningScreen() {
keyExtractor={(item) => item.id}
showsVerticalScrollIndicator={false}
scrollEnabled={false}
style={styles.tabloListInForm}
/>
</View>
</ScrollView>
<View style={styles.createEventModalActions}>
<View
style={[
styles.createEventModalActions,
{ borderTopColor: borderColor },
]}
>
<TouchableOpacity
style={styles.cancelButton}
style={[styles.cancelButton, { borderColor }]}
onPress={() => setShowCreateEventModal(false)}
>
<Text style={styles.cancelButtonText}>Annuler</Text>
<Text style={[styles.cancelButtonText, { color: textColor }]}>
Annuler
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.saveButton}
@ -738,7 +907,7 @@ export default function PlanningScreen() {
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#f8fafc",
// backgroundColor is set dynamically
},
header: {
flexDirection: "row",
@ -751,12 +920,12 @@ const styles = StyleSheet.create({
headerTitle: {
fontSize: 28,
fontWeight: "bold",
color: "#1f2937",
// color is set dynamically
marginBottom: 4,
},
headerSubtitle: {
fontSize: 16,
color: "#6b7280",
// color is set dynamically
textTransform: "capitalize",
},
addButton: {
@ -778,7 +947,7 @@ const styles = StyleSheet.create({
},
viewModeToggle: {
flexDirection: "row",
backgroundColor: "#f3f4f6",
// backgroundColor is set dynamically
borderRadius: 12,
padding: 4,
},
@ -798,7 +967,7 @@ const styles = StyleSheet.create({
viewModeText: {
fontSize: 14,
fontWeight: "500",
color: "#6b7280",
// color is set dynamically
},
activeViewModeText: {
color: "white",
@ -808,7 +977,7 @@ const styles = StyleSheet.create({
marginBottom: 20,
},
tabloSelector: {
backgroundColor: "white",
// backgroundColor is set dynamically
borderRadius: 12,
padding: 16,
flexDirection: "row",
@ -831,7 +1000,7 @@ const styles = StyleSheet.create({
},
tabloSelectorLabel: {
fontSize: 12,
color: "#6b7280",
// color is set dynamically
marginBottom: 2,
},
tabloSelectorRow: {
@ -841,7 +1010,7 @@ const styles = StyleSheet.create({
tabloSelectorName: {
fontSize: 16,
fontWeight: "600",
color: "#1f2937",
// color is set dynamically
marginLeft: 8,
},
tabloColorDot: {
@ -870,7 +1039,7 @@ const styles = StyleSheet.create({
calendarTitle: {
fontSize: 18,
fontWeight: "600",
color: "#1f2937",
// color is set dynamically
},
weekHeader: {
flexDirection: "row",
@ -878,12 +1047,12 @@ const styles = StyleSheet.create({
marginBottom: 16,
paddingBottom: 12,
borderBottomWidth: 1,
borderBottomColor: "#e5e7eb",
// borderBottomColor is set dynamically
},
weekDay: {
fontSize: 14,
fontWeight: "500",
color: "#6b7280",
// color is set dynamically
textAlign: "center",
width: 40,
},
@ -900,11 +1069,11 @@ const styles = StyleSheet.create({
},
dayText: {
fontSize: 16,
color: "#1f2937",
// color is set dynamically
fontWeight: "500",
},
inactiveDayText: {
color: "#d1d5db",
// color is set dynamically
},
selectedDay: {
backgroundColor: "#3b82f6",
@ -947,7 +1116,7 @@ const styles = StyleSheet.create({
marginHorizontal: 2,
},
weekDayHeader: {
backgroundColor: "#f8fafc",
// backgroundColor is set dynamically
borderRadius: 8,
padding: 8,
alignItems: "center",
@ -964,12 +1133,12 @@ const styles = StyleSheet.create({
},
weekDayName: {
fontSize: 12,
color: "#6b7280",
// color is set dynamically
fontWeight: "500",
},
weekDayNumber: {
fontSize: 16,
color: "#1f2937",
// color is set dynamically
fontWeight: "600",
marginTop: 2,
},
@ -1037,14 +1206,14 @@ const styles = StyleSheet.create({
width: 20,
height: 20,
borderRadius: 10,
backgroundColor: "#e5e7eb",
// backgroundColor is set dynamically
justifyContent: "center",
alignItems: "center",
margin: 2,
},
weekEventMoreText: {
fontSize: 10,
color: "#6b7280",
// color is set dynamically
},
weekEmptySlot: {
flex: 1,
@ -1069,11 +1238,10 @@ const styles = StyleSheet.create({
eventsSectionTitle: {
fontSize: 18,
fontWeight: "600",
color: "#1f2937",
// color is set dynamically
marginLeft: 8,
},
eventCard: {
backgroundColor: "white",
borderRadius: 12,
marginBottom: 12,
flexDirection: "row",
@ -1082,6 +1250,7 @@ const styles = StyleSheet.create({
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
borderWidth: 1,
},
eventColorBar: {
width: 4,
@ -1095,7 +1264,6 @@ const styles = StyleSheet.create({
eventTitle: {
fontSize: 16,
fontWeight: "600",
color: "#1f2937",
marginBottom: 8,
},
eventDetails: {
@ -1108,7 +1276,6 @@ const styles = StyleSheet.create({
},
eventDetailText: {
fontSize: 14,
color: "#6b7280",
},
emptyState: {
display: "flex",
@ -1121,25 +1288,24 @@ const styles = StyleSheet.create({
emptyStateTitle: {
fontSize: 18,
fontWeight: "600",
color: "#6b7280",
// color is set dynamically
marginTop: 8,
marginBottom: 8,
},
emptyStateText: {
fontSize: 14,
color: "#9ca3af",
// color is set dynamically
textAlign: "center",
lineHeight: 20,
},
modalOverlay: {
flex: 1,
backgroundColor: "rgba(0, 0, 0, 0.5)",
// backgroundColor is set dynamically
justifyContent: "center",
alignItems: "center",
paddingHorizontal: 20,
},
modalContent: {
backgroundColor: "white",
borderRadius: 16,
width: "100%",
maxWidth: 400,
@ -1149,16 +1315,15 @@ const styles = StyleSheet.create({
shadowOpacity: 0.25,
shadowRadius: 20,
elevation: 10,
borderWidth: 1,
},
modalHeader: {
padding: 20,
borderBottomWidth: 1,
borderBottomColor: "#e5e7eb",
},
modalTitle: {
fontSize: 18,
fontWeight: "600",
color: "#1f2937",
textAlign: "center",
},
tabloOption: {
@ -1167,7 +1332,6 @@ const styles = StyleSheet.create({
alignItems: "center",
padding: 16,
borderBottomWidth: 1,
borderBottomColor: "#f3f4f6",
},
tabloOptionLeft: {
flexDirection: "row",
@ -1181,7 +1345,6 @@ const styles = StyleSheet.create({
tabloOptionName: {
fontSize: 16,
fontWeight: "500",
color: "#1f2937",
marginBottom: 2,
},
tabloOptionCount: {
@ -1204,11 +1367,11 @@ const styles = StyleSheet.create({
selectedDayTitle: {
fontSize: 18,
fontWeight: "600",
color: "#1f2937",
// color is set dynamically
},
selectedDayEventsCount: {
fontSize: 14,
color: "#6b7280",
// color is set dynamically
},
selectedDayEventsList: {
paddingBottom: 10,
@ -1222,11 +1385,10 @@ const styles = StyleSheet.create({
// Create Event Modal Styles
createEventModalOverlay: {
flex: 1,
backgroundColor: "rgba(0, 0, 0, 0.5)",
// backgroundColor is set dynamically
justifyContent: "flex-end",
},
createEventModalContent: {
backgroundColor: "white",
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
maxHeight: "90%",
@ -1242,12 +1404,10 @@ const styles = StyleSheet.create({
alignItems: "center",
padding: 20,
borderBottomWidth: 1,
borderBottomColor: "#e5e7eb",
},
createEventModalTitle: {
fontSize: 20,
fontWeight: "600",
color: "#1f2937",
},
createEventCloseButton: {
padding: 4,
@ -1262,23 +1422,17 @@ const styles = StyleSheet.create({
formLabel: {
fontSize: 16,
fontWeight: "500",
color: "#374151",
marginBottom: 8,
},
formInput: {
borderWidth: 1,
borderColor: "#d1d5db",
borderRadius: 8,
padding: 12,
fontSize: 16,
color: "#1f2937",
backgroundColor: "white",
},
tabloListInForm: {
borderWidth: 1,
borderColor: "#d1d5db",
borderRadius: 8,
backgroundColor: "white",
maxHeight: 150,
},
tabloOptionInForm: {
@ -1287,15 +1441,13 @@ const styles = StyleSheet.create({
alignItems: "center",
padding: 12,
borderBottomWidth: 1,
borderBottomColor: "#f3f4f6",
},
selectedTabloOptionInForm: {
backgroundColor: "#eff6ff",
// backgroundColor is set dynamically
},
tabloOptionNameInForm: {
fontSize: 16,
fontWeight: "500",
color: "#1f2937",
marginLeft: 8,
},
selectedTabloOptionNameInForm: {
@ -1306,7 +1458,6 @@ const styles = StyleSheet.create({
justifyContent: "space-between",
padding: 20,
borderTopWidth: 1,
borderTopColor: "#e5e7eb",
gap: 12,
},
cancelButton: {
@ -1315,14 +1466,12 @@ const styles = StyleSheet.create({
paddingHorizontal: 20,
borderRadius: 8,
borderWidth: 1,
borderColor: "#d1d5db",
alignItems: "center",
justifyContent: "center",
},
cancelButtonText: {
fontSize: 16,
fontWeight: "500",
color: "#6b7280",
},
saveButton: {
flex: 1,

View file

@ -29,15 +29,42 @@ import {
Heart,
} from "lucide-react-native";
import { router } from "expo-router";
import { useThemeColor } from "@/hooks/useThemeColor";
import { useColorScheme } from "@/hooks/useColorScheme";
export default function SettingsScreen() {
const signOut = useAuthStore((state) => 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) => (
<View style={styles.section}>
<Text style={styles.sectionTitle}>{title}</Text>
<View style={styles.sectionContent}>{children}</View>
<Text style={[styles.sectionTitle, { color: subtitleColor }]}>
{title}
</Text>
<View
style={[
styles.sectionContent,
{ backgroundColor: cardBackgroundColor, borderColor },
]}
>
{children}
</View>
</View>
);
@ -86,7 +122,7 @@ export default function SettingsScreen() {
showArrow: boolean = true
) => (
<TouchableOpacity
style={styles.settingsItem}
style={[styles.settingsItem, { borderBottomColor: borderColor }]}
onPress={onPress}
disabled={!onPress}
activeOpacity={onPress ? 0.7 : 1}
@ -94,16 +130,26 @@ export default function SettingsScreen() {
<View style={styles.settingsItemLeft}>
<View style={styles.iconContainer}>{icon}</View>
<View style={styles.settingsItemContent}>
<Text style={styles.settingsItemTitle}>{title}</Text>
<Text style={[styles.settingsItemTitle, { color: textColor }]}>
{title}
</Text>
{subtitle && (
<Text style={styles.settingsItemSubtitle}>{subtitle}</Text>
<Text
style={[styles.settingsItemSubtitle, { color: subtitleColor }]}
>
{subtitle}
</Text>
)}
</View>
</View>
<View style={styles.settingsItemRight}>
{rightComponent}
{showArrow && onPress && (
<ChevronRight size={20} color="#9ca3af" style={{ marginLeft: 8 }} />
<ChevronRight
size={20}
color={subtitleColor}
style={{ marginLeft: 8 }}
/>
)}
</View>
</TouchableOpacity>
@ -124,20 +170,26 @@ export default function SettingsScreen() {
<Switch
value={value}
onValueChange={onValueChange}
trackColor={{ false: "#e5e7eb", true: "#3b82f6" }}
trackColor={{
false: colorScheme === "dark" ? "#374151" : "#e5e7eb",
true: "#3b82f6",
}}
thumbColor={value ? "#ffffff" : "#ffffff"}
ios_backgroundColor="#e5e7eb"
ios_backgroundColor={colorScheme === "dark" ? "#374151" : "#e5e7eb"}
/>,
false
);
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<View style={[styles.container, { backgroundColor }]}>
<StatusBar
barStyle={colorScheme === "dark" ? "light-content" : "light-content"}
backgroundColor={gradientColors[0]}
/>
{/* Header */}
<LinearGradient
colors={["#1e3a8a", "#3b82f6", "#60a5fa"]}
colors={gradientColors}
style={styles.headerGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
@ -154,7 +206,10 @@ export default function SettingsScreen() {
<View style={styles.decorativeCircle2} />
</LinearGradient>
<ScrollView style={styles.content} showsVerticalScrollIndicator={false}>
<ScrollView
style={[styles.content, { backgroundColor }]}
showsVerticalScrollIndicator={false}
>
{/* Account Section */}
{renderSettingsSection(
"Compte",
@ -198,9 +253,14 @@ export default function SettingsScreen() {
{renderSwitchItem(
<Moon size={20} color="#6366f1" />,
"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",

View file

@ -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() {
<TouchableOpacity
style={[
styles.tabloCard,
{ width: viewMode === "grid" ? itemWidth : "100%" },
{
width: viewMode === "grid" ? itemWidth : "100%",
backgroundColor: cardBackgroundColor,
borderColor: borderColor,
},
]}
onPress={() => navigateToTablo(tablo)}
onLongPress={() => handleDeleteTablo(tablo)}
@ -216,7 +248,10 @@ export default function TablosScreen() {
{/* Tablo Info */}
<View style={styles.tabloInfo}>
<View style={styles.nameRow}>
<Text style={styles.tabloName} numberOfLines={2}>
<Text
style={[styles.tabloName, { color: textColor }]}
numberOfLines={2}
>
{tablo.name}
</Text>
<View
@ -257,13 +292,13 @@ export default function TablosScreen() {
router.push(`/channel/${getChannelCid(tablo.id)}`)
}
>
<MessageCircle size={16} color="#6b7280" />
<MessageCircle size={16} color={subtitleColor} />
</TouchableOpacity>
<TouchableOpacity
style={styles.cardActionButton}
onPress={() => router.push("/planning")}
>
<Calendar size={16} color="#6b7280" />
<Calendar size={16} color={subtitleColor} />
</TouchableOpacity>
</View>
</View>
@ -275,7 +310,10 @@ export default function TablosScreen() {
const renderListItem = ({ item: tablo }: { item: UserTablo }) => {
return (
<TouchableOpacity
style={styles.listItem}
style={[
styles.listItem,
{ backgroundColor: cardBackgroundColor, borderColor: borderColor },
]}
onPress={() => navigateToTablo(tablo)}
onLongPress={() => handleDeleteTablo(tablo)}
activeOpacity={0.8}
@ -294,7 +332,9 @@ export default function TablosScreen() {
<View style={styles.listItemContent}>
<View style={styles.listNameRow}>
<View style={styles.listNameContainer}>
<Text style={styles.listName}>{tablo.name}</Text>
<Text style={[styles.listName, { color: textColor }]}>
{tablo.name}
</Text>
</View>
<View
style={[
@ -333,13 +373,13 @@ export default function TablosScreen() {
router.push(`/channel/${getChannelCid(tablo.id)}`)
}
>
<MessageCircle size={16} color="#6b7280" />
<MessageCircle size={16} color={subtitleColor} />
</TouchableOpacity>
<TouchableOpacity
style={styles.listActionButton}
onPress={() => router.push("/planning")}
>
<Calendar size={16} color="#6b7280" />
<Calendar size={16} color={subtitleColor} />
</TouchableOpacity>
</View>
</View>
@ -369,12 +409,15 @@ export default function TablosScreen() {
}
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<View style={[styles.container, { backgroundColor }]}>
<StatusBar
barStyle={colorScheme === "dark" ? "light-content" : "light-content"}
backgroundColor={gradientColors[0]}
/>
{/* Beautiful Header */}
<LinearGradient
colors={["#1e3a8a", "#3b82f6", "#60a5fa"]}
colors={gradientColors}
style={styles.headerGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
@ -423,10 +466,12 @@ export default function TablosScreen() {
</LinearGradient>
{/* Content */}
<View style={styles.contentContainer}>
<View style={[styles.contentContainer, { backgroundColor }]}>
{isLoading && !refreshing ? (
<View style={styles.loadingContainer}>
<Text style={styles.loadingText}>Chargement de vos tablos...</Text>
<Text style={[styles.loadingText, { color: subtitleColor }]}>
Chargement de vos tablos...
</Text>
</View>
) : filteredTablos && filteredTablos.length > 0 ? (
<FlatList
@ -443,8 +488,10 @@ export default function TablosScreen() {
/>
) : (
<View style={styles.emptyContainer}>
<Text style={styles.emptyTitle}>Aucun tablo trouvé</Text>
<Text style={styles.emptyMessage}>
<Text style={[styles.emptyTitle, { color: textColor }]}>
Aucun tablo trouvé
</Text>
<Text style={[styles.emptyMessage, { color: subtitleColor }]}>
{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}
>
<View style={styles.modalContent}>
<View
style={[
styles.modalContent,
{ backgroundColor: cardBackgroundColor, borderColor },
]}
>
<TouchableOpacity
style={styles.modalCloseButton}
onPress={() => setIsCreateModalVisible(false)}
>
<X size={24} color="#6b7280" />
<X size={24} color={subtitleColor} />
</TouchableOpacity>
<Text style={styles.modalTitle}>Nouveau Tablo</Text>
<Text style={[styles.modalTitle, { color: textColor }]}>
Nouveau Tablo
</Text>
<ScrollView>
<View style={styles.modalFormGroup}>
<Text style={styles.modalLabel}>Nom du Tablo</Text>
<Text style={[styles.modalLabel, { color: subtitleColor }]}>
Nom du Tablo
</Text>
<TextInput
style={styles.modalInput}
style={[
styles.modalInput,
{
backgroundColor: cardBackgroundColor,
borderColor,
color: textColor,
},
]}
placeholder="Ex: Projet React Native"
placeholderTextColor={subtitleColor}
value={newTablo.name}
onChangeText={(text) =>
setNewTablo({ ...newTablo, name: text })
@ -506,7 +570,9 @@ export default function TablosScreen() {
/>
</View>
<View style={styles.modalFormGroup}>
<Text style={styles.modalLabel}>Couleur</Text>
<Text style={[styles.modalLabel, { color: subtitleColor }]}>
Couleur
</Text>
<View style={styles.colorPicker}>
{AVAILABLE_COLORS.map((color) => (
<TouchableOpacity
@ -522,7 +588,9 @@ export default function TablosScreen() {
</View>
</View>
<View style={styles.modalFormGroup}>
<Text style={styles.modalLabel}>Statut</Text>
<Text style={[styles.modalLabel, { color: subtitleColor }]}>
Statut
</Text>
<View style={styles.statusPicker}>
{(["todo", "in_progress", "done"] as const).map((status) => (
<TouchableOpacity
@ -978,7 +1046,6 @@ const styles = StyleSheet.create({
backgroundColor: "rgba(0, 0, 0, 0.5)",
},
modalContent: {
backgroundColor: "white",
borderRadius: 20,
padding: 20,
width: "90%",
@ -988,6 +1055,7 @@ const styles = StyleSheet.create({
shadowOpacity: 0.3,
shadowRadius: 10,
elevation: 10,
borderWidth: 1,
},
modalCloseButton: {
alignSelf: "flex-end",
@ -996,7 +1064,6 @@ const styles = StyleSheet.create({
modalTitle: {
fontSize: 24,
fontWeight: "bold",
color: "#1f2937",
marginBottom: 20,
},
modalFormGroup: {
@ -1004,16 +1071,13 @@ const styles = StyleSheet.create({
},
modalLabel: {
fontSize: 16,
color: "#6b7280",
marginBottom: 8,
},
modalInput: {
borderWidth: 1,
borderColor: "#e5e7eb",
borderRadius: 12,
padding: 12,
fontSize: 16,
color: "#1f2937",
},
colorPicker: {
flexDirection: "row",

View file

@ -17,6 +17,8 @@ import {
import { MessageCircle, Users, Smile } from "lucide-react-native";
import { useEffect, useState } from "react";
import { useHeaderHeight } from "@react-navigation/elements";
import { useThemeColor } from "@/hooks/useThemeColor";
import { useColorScheme } from "@/hooks/useColorScheme";
export default function ChannelScreen() {
const { cid } = useLocalSearchParams<{ cid: string }>();
@ -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 = () => (
<View style={styles.emptyContainer}>
<View style={styles.emptyIconContainer}>
<MessageCircle size={64} color="#d1d5db" strokeWidth={1.5} />
<MessageCircle size={64} color={iconColor} strokeWidth={1.5} />
<View style={styles.decorativeElements}>
<View style={styles.floatingIcon1}>
<Users size={20} color="#e5e7eb" />
<Users size={20} color={iconSecondaryColor} />
</View>
<View style={styles.floatingIcon2}>
<Smile size={18} color="#e5e7eb" />
<Smile size={18} color={iconSecondaryColor} />
</View>
</View>
</View>
<Text style={styles.emptyTitle}>Commencez la conversation</Text>
<Text style={styles.emptyMessage}>
<Text style={[styles.emptyTitle, { color: textColor }]}>
Commencez la conversation
</Text>
<Text style={[styles.emptyMessage, { color: subtitleColor }]}>
Soyez le premier à envoyer un message dans ce canal !
</Text>
</View>
);
return (
<SafeAreaView style={{ flex: 1, backgroundColor: "#f8fafc" }}>
<SafeAreaView style={{ flex: 1, backgroundColor }}>
<Channel channel={channel} keyboardVerticalOffset={headerHeight}>
{isLoading ? (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#3b82f6" />
<Text style={styles.loadingText}>Chargement des messages...</Text>
<Text style={[styles.loadingText, { color: subtitleColor }]}>
Chargement des messages...
</Text>
</View>
) : hasMessages ? (
<MessageList />
@ -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,
},
});

View file

@ -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 (
<ScrollView style={styles.container} showsVerticalScrollIndicator={false}>
<LinearGradient
colors={["#1e3a8a", "#3b82f6", "#60a5fa"]}
style={styles.headerGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<View style={styles.headerContent}>
<Avatar
size={120}
rounded
source={user.avatar_url ? { uri: user.avatar_url } : undefined}
icon={
!user.avatar_url
? { name: "user", type: "font-awesome" }
: undefined
}
containerStyle={styles.avatar}
/>
<Text style={styles.userName}>{user.name || "Utilisateur"}</Text>
<Text style={styles.userEmail}>{user.email}</Text>
</View>
</LinearGradient>
{/* Contenu principal */}
<View style={styles.content}>
{/* Carte d'informations personnelles */}
<Card containerStyle={styles.mainCard}>
<View style={styles.cardHeader}>
<User size={20} color="#3b82f6" />
<Text style={styles.cardHeaderTitle}>
Informations personnelles
</Text>
<SafeAreaView>
<Stack.Screen options={{ headerShown: false }} />
<ScrollView style={styles.container} showsVerticalScrollIndicator={false}>
<LinearGradient
colors={["#1e3a8a", "#3b82f6", "#60a5fa"]}
style={styles.headerGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<View style={styles.headerContent}>
<Avatar
size={120}
rounded
source={user.avatar_url ? { uri: user.avatar_url } : undefined}
icon={
!user.avatar_url
? { name: "user", type: "font-awesome" }
: undefined
}
containerStyle={styles.avatar}
/>
<Text style={styles.userName}>{user.name || "Utilisateur"}</Text>
<Text style={styles.userEmail}>{user.email}</Text>
</View>
</LinearGradient>
<View style={styles.infoItem}>
<View style={styles.infoIconContainer}>
<Mail size={18} color="#6b7280" />
{/* Contenu principal */}
<View style={styles.content}>
{/* Carte d'informations personnelles */}
<Card containerStyle={styles.mainCard}>
<View style={styles.cardHeader}>
<User size={20} color="#3b82f6" />
<Text style={styles.cardHeaderTitle}>
Informations personnelles
</Text>
</View>
<View style={styles.infoContent}>
<Text style={styles.infoLabel}>Adresse e-mail</Text>
<Text style={styles.infoValue}>{user.email}</Text>
<View style={styles.infoItem}>
<View style={styles.infoIconContainer}>
<Mail size={18} color="#6b7280" />
</View>
<View style={styles.infoContent}>
<Text style={styles.infoLabel}>Adresse e-mail</Text>
<Text style={styles.infoValue}>{user.email}</Text>
</View>
</View>
</View>
<View style={styles.divider} />
<View style={styles.divider} />
<View style={styles.infoItem}>
<View style={styles.infoIconContainer}>
<User size={18} color="#6b7280" />
</View>
<View style={styles.infoContent}>
<Text style={styles.infoLabel}>Nom d'affichage</Text>
{isEditing ? (
<Input
placeholder="Entrez le nom d'affichage"
value={displayName}
onChangeText={setDisplayName}
containerStyle={styles.inputContainer}
inputStyle={styles.inputText}
autoFocus
/>
) : (
<Text style={styles.infoValue}>
{user.name || "Non défini"}
</Text>
)}
</View>
<TouchableOpacity
style={styles.editButton}
onPress={() => {
if (isEditing) {
handleSaveDisplayName();
} else {
setDisplayName(user.name ?? "");
setIsEditing(true);
}
}}
>
{isEditing ? (
<Check size={18} color="#10b981" />
) : (
<Edit3 size={18} color="#6b7280" />
)}
</TouchableOpacity>
</View>
</Card>
{/* Éléments de menu */}
<Card containerStyle={styles.menuCard}>
<View style={styles.cardHeader}>
<Settings size={20} color="#3b82f6" />
<Text style={styles.cardHeaderTitle}>Préférences</Text>
</View>
{menuItems.map((item, index) => (
<View key={index}>
<TouchableOpacity style={styles.menuItem} onPress={item.onPress}>
<View style={styles.menuIconContainer}>
<item.icon size={20} color="#6b7280" />
</View>
<View style={styles.menuContent}>
<Text style={styles.menuTitle}>{item.title}</Text>
<Text style={styles.menuSubtitle}>{item.subtitle}</Text>
</View>
<Text style={styles.menuArrow}></Text>
<View style={styles.infoItem}>
<View style={styles.infoIconContainer}>
<User size={18} color="#6b7280" />
</View>
<View style={styles.infoContent}>
<Text style={styles.infoLabel}>Nom d'affichage</Text>
{isEditing ? (
<Input
placeholder="Entrez le nom d'affichage"
value={displayName}
onChangeText={setDisplayName}
containerStyle={styles.inputContainer}
inputStyle={styles.inputText}
autoFocus
/>
) : (
<Text style={styles.infoValue}>
{user.name || "Non défini"}
</Text>
)}
</View>
<TouchableOpacity
style={styles.editButton}
onPress={() => {
if (isEditing) {
handleSaveDisplayName();
} else {
setDisplayName(user.name ?? "");
setIsEditing(true);
}
}}
>
{isEditing ? (
<Check size={18} color="#10b981" />
) : (
<Edit3 size={18} color="#6b7280" />
)}
</TouchableOpacity>
{index < menuItems.length - 1 && <View style={styles.divider} />}
</View>
))}
</Card>
</Card>
{/* Bouton de déconnexion */}
<TouchableOpacity style={styles.signOutContainer} onPress={signOut}>
<LinearGradient
colors={["#ef4444", "#dc2626"]}
style={styles.signOutGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
>
<LogOut size={20} color="white" />
<Text style={styles.signOutText}>Se déconnecter</Text>
</LinearGradient>
</TouchableOpacity>
</View>
</ScrollView>
{/* Éléments de menu */}
<Card containerStyle={styles.menuCard}>
<View style={styles.cardHeader}>
<Settings size={20} color="#3b82f6" />
<Text style={styles.cardHeaderTitle}>Préférences</Text>
</View>
{menuItems.map((item, index) => (
<View key={index}>
<TouchableOpacity
style={styles.menuItem}
onPress={item.onPress}
>
<View style={styles.menuIconContainer}>
<item.icon size={20} color="#6b7280" />
</View>
<View style={styles.menuContent}>
<Text style={styles.menuTitle}>{item.title}</Text>
<Text style={styles.menuSubtitle}>{item.subtitle}</Text>
</View>
<Text style={styles.menuArrow}></Text>
</TouchableOpacity>
{index < menuItems.length - 1 && (
<View style={styles.divider} />
)}
</View>
))}
</Card>
{/* Bouton de déconnexion */}
<TouchableOpacity style={styles.signOutContainer} onPress={signOut}>
<LinearGradient
colors={["#ef4444", "#dc2626"]}
style={styles.signOutGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
>
<LogOut size={20} color="white" />
<Text style={styles.signOutText}>Se déconnecter</Text>
</LinearGradient>
</TouchableOpacity>
</View>
</ScrollView>
</SafeAreaView>
);
}

View file

@ -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 (
<View style={styles.container}>
<View style={[styles.container, { backgroundColor }]}>
<Image source={require("@/assets/images/logo.png")} style={styles.logo} />
<Text style={styles.title}>Connexion XTablo</Text>
<Text style={styles.subtitle}>Connectez-vous à votre compte</Text>
<Text style={[styles.title, { color: textColor }]}>Connexion XTablo</Text>
<Text style={[styles.subtitle, { color: subtitleColor }]}>
Connectez-vous à votre compte
</Text>
<View style={[styles.verticallySpaced, styles.mt10]}>
<Input
label="Adresse email"
@ -51,9 +75,9 @@ export default function Auth() {
/>
</View>
<View style={styles.separatorContainer}>
<View style={styles.separator} />
<Text style={styles.separatorText}>ou</Text>
<View style={styles.separator} />
<View style={[styles.separator, { backgroundColor: separatorColor }]} />
<Text style={[styles.separatorText, { color: subtitleColor }]}>ou</Text>
<View style={[styles.separator, { backgroundColor: separatorColor }]} />
</View>
<View style={styles.verticallySpaced}>
<GoogleLoginButton onPress={() => performOAuth("google")} />
@ -62,8 +86,10 @@ export default function Auth() {
<AppleLoginButton onPress={() => performOAuth("apple")} />
</View>
<View style={styles.linkContainer}>
<Text style={styles.linkText}>Pas encore de compte ? </Text>
<Link href="/signup" style={styles.link}>
<Text style={[styles.linkText, { color: subtitleColor }]}>
Pas encore de compte ?{" "}
</Text>
<Link href="/signup" style={[styles.link, { color: linkColor }]}>
S'inscrire
</Link>
</View>
@ -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",
},
});

View file

@ -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 (
<View style={styles.container}>
<View style={[styles.container, { backgroundColor }]}>
<Image source={require("@/assets/images/logo.png")} style={styles.logo} />
<Text style={styles.title}>Créer un compte XTablo</Text>
<Text style={styles.subtitle}>Rejoignez-nous !</Text>
<Text style={[styles.title, { color: textColor }]}>
Créer un compte XTablo
</Text>
<Text style={[styles.subtitle, { color: subtitleColor }]}>
Rejoignez-nous !
</Text>
<View style={[styles.verticallySpaced, styles.mt10]}>
<Input
label="Prénom"
@ -84,8 +104,10 @@ export default function SignUp() {
/>
</View>
<View style={styles.linkContainer}>
<Text style={styles.linkText}>Vous avez déjà un compte ? </Text>
<Link href="/login" style={styles.link}>
<Text style={[styles.linkText, { color: subtitleColor }]}>
Vous avez déjà un compte ?{" "}
</Text>
<Link href="/login" style={[styles.link, { color: linkColor }]}>
Se connecter
</Link>
</View>
@ -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",
},
});

View file

@ -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 (
<ThemedView style={styles.loadingContainer}>
<ThemedView style={[styles.loadingContainer, { backgroundColor }]}>
<View style={styles.loadingContent}>
<View style={styles.logoContainer}>
<Animated.Image
@ -45,10 +60,13 @@ export const LoadingView = () => {
style={[styles.logo, animatedStyle]}
/>
</View>
<ThemedText type="title" style={styles.title}>
<ThemedText type="title" style={[styles.title, { color: textColor }]}>
XTablo
</ThemedText>
<ThemedText type="subtitle" style={styles.subtitle}>
<ThemedText
type="subtitle"
style={[styles.subtitle, { color: subtitleColor }]}
>
Initialisation de l'application...
</ThemedText>
</View>
@ -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,
},
});

View file

@ -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<DefaultStreamChatGenerics>;
@ -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<
<View style={styles.container}>
{/* Right Actions Background */}
<View style={styles.rightActionsContainer}>
<Pressable style={styles.archiveButton} onPress={onArchivePress}>
<Pressable
style={[
styles.archiveButton,
{ backgroundColor: archiveButtonColor },
]}
onPress={onArchivePress}
>
<Animated.View style={[styles.actionContent, actionAnimatedStyle]}>
<Archive size={24} color="white" />
<Text style={styles.actionText}>Archiver</Text>
<Archive size={24} color={iconColor} />
<Text style={[styles.actionText, { color: textColor }]}>
Archiver
</Text>
</Animated.View>
</Pressable>
</View>
{/* Channel Content */}
<GestureDetector gesture={gestureHandler}>
<Animated.View style={[styles.channelContainer, channelAnimatedStyle]}>
<Animated.View
style={[
styles.channelContainer,
channelAnimatedStyle,
{ backgroundColor },
]}
>
{children}
</Animated.View>
</GestureDetector>
@ -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",

View file

@ -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<Theme> = 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 (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Chat Indisponible</Text>
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor,
}}
>
<Text style={{ color: textColor }}>Chat Indisponible</Text>
</View>
);
}
@ -33,7 +130,7 @@ export default function ChatProvider({
}
return (
<OverlayProvider>
<OverlayProvider value={{ style: streamChatTheme }}>
<Chat client={client}>{children}</Chat>
</OverlayProvider>
);