Improve loading view, and fix bug with colors

This commit is contained in:
Arthur Belleville 2025-07-19 18:55:06 +02:00
parent a847b59c12
commit 59e896ee69
No known key found for this signature in database

View file

@ -17,96 +17,6 @@ import { ColorMap } from "@/constants/colors";
import { UserTablo } from "@/types/tablos.types";
// Custom Avatar Component for Channel List
const CustomChannelAvatar = ({
channel,
tablos,
}: {
channel: Channel;
tablos: UserTablo[];
}) => {
const tabloId = channel?.id || "";
const tablo = tablos?.find((t) => t.id === tabloId);
const tabloColor = tablo?.color || "bg-blue-500";
const tabloName = tablo?.name || channel?.data?.name || "Tablo";
// Get members info
const members = channel?.state?.members || {};
const memberCount = Object.keys(members).length;
// Generate initials from tablo name
const getInitials = (name: string) => {
return name
.split(" ")
.map((word) => word.charAt(0))
.join("")
.toUpperCase()
.slice(0, 2);
};
// // Create gradient colors based on tablo color
const getTabloGradientColors = (colorKey: string): [string, string] => {
const baseColor = ColorMap[colorKey] || ColorMap["bg-blue-500"];
// Create a lighter version for gradient effect
const lightenColor = (hex: string, percent: number): string => {
const num = parseInt(hex.replace("#", ""), 16);
const amt = Math.round(2.55 * percent);
const R = Math.min(255, Math.max(0, (num >> 16) + amt));
const G = Math.min(255, Math.max(0, ((num >> 8) & 0x00ff) + amt));
const B = Math.min(255, Math.max(0, (num & 0x0000ff) + amt));
return "#" + ((1 << 24) + (R << 16) + (G << 8) + B).toString(16).slice(1);
};
// Create a darker version for gradient effect
const darkenColor = (hex: string, percent: number): string => {
const num = parseInt(hex.replace("#", ""), 16);
const amt = Math.round(2.55 * percent);
const R = Math.min(255, Math.max(0, (num >> 16) - amt));
const G = Math.min(255, Math.max(0, ((num >> 8) & 0x00ff) - amt));
const B = Math.min(255, Math.max(0, (num & 0x0000ff) - amt));
return "#" + ((1 << 24) + (R << 16) + (G << 8) + B).toString(16).slice(1);
};
const lightColor = lightenColor(baseColor, 15);
const darkColor = darkenColor(baseColor, 10);
return [lightColor, darkColor];
};
const initials = getInitials(tabloName);
const gradientColors = getTabloGradientColors(tabloColor);
return (
<View style={styles.avatarContainer}>
<LinearGradient
colors={gradientColors}
style={styles.avatarGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<Text style={styles.avatarInitials}>{initials}</Text>
{/* Member count indicator for group channels */}
{memberCount > 2 && (
<View style={styles.memberCountBadge}>
<Text style={styles.memberCountText}>{memberCount}</Text>
</View>
)}
</LinearGradient>
{/* Decorative ring */}
<View
style={[
styles.avatarRing,
{ borderColor: `${ColorMap[tabloColor]}30` },
]}
/>
{/* Status indicator (online/active) */}
<View style={styles.statusIndicator} />
</View>
);
};
// Custom Title Component for bigger channel names
const CustomChannelTitle = ({ channel }: { channel: Channel }) => {
@ -121,7 +31,7 @@ const CustomChannelTitle = ({ channel }: { channel: Channel }) => {
export default function HomeScreen() {
const user = useUser();
const { data: tablos } = useTablosList();
const { data: tablos, isLoading } = useTablosList();
// Search animation state
// const [isSearchVisible, setIsSearchVisible] = useState(false);
@ -164,10 +74,100 @@ export default function HomeScreen() {
limit: 20,
};
// Create a wrapper component for the avatar that has access to tablos data
const AvatarWithTablos = ({ channel }: { channel: Channel }) => (
<CustomChannelAvatar channel={channel} tablos={tablos || []} />
);
const CustomChannelAvatar = ({
channel,
tablos,
}: {
channel: Channel;
tablos: UserTablo[];
}) => {
const tabloId = channel?.id || "";
const tablo = tablos?.find((t) => t.id === tabloId);
const tabloColor = tablo?.color || "bg-blue-500";
const tabloName = tablo?.name || channel?.data?.name || "Tablo";
// Get members info
const members = channel?.state?.members || {};
const memberCount = Object.keys(members).length;
// Generate initials from tablo name
const getInitials = (name: string) => {
return name
.split(" ")
.map((word) => word.charAt(0))
.join("")
.toUpperCase()
.slice(0, 2);
};
// // Create gradient colors based on tablo color
const getTabloGradientColors = (colorKey: string): [string, string] => {
const baseColor = ColorMap[colorKey] || ColorMap["bg-blue-500"];
// Create a lighter version for gradient effect
const lightenColor = (hex: string, percent: number): string => {
const num = parseInt(hex.replace("#", ""), 16);
const amt = Math.round(2.55 * percent);
const R = Math.min(255, Math.max(0, (num >> 16) + amt));
const G = Math.min(255, Math.max(0, ((num >> 8) & 0x00ff) + amt));
const B = Math.min(255, Math.max(0, (num & 0x0000ff) + amt));
return (
"#" + ((1 << 24) + (R << 16) + (G << 8) + B).toString(16).slice(1)
);
};
// Create a darker version for gradient effect
const darkenColor = (hex: string, percent: number): string => {
const num = parseInt(hex.replace("#", ""), 16);
const amt = Math.round(2.55 * percent);
const R = Math.min(255, Math.max(0, (num >> 16) - amt));
const G = Math.min(255, Math.max(0, ((num >> 8) & 0x00ff) - amt));
const B = Math.min(255, Math.max(0, (num & 0x0000ff) - amt));
return (
"#" + ((1 << 24) + (R << 16) + (G << 8) + B).toString(16).slice(1)
);
};
const lightColor = lightenColor(baseColor, 15);
const darkColor = darkenColor(baseColor, 10);
return [lightColor, darkColor];
};
const initials = getInitials(tabloName);
const gradientColors = getTabloGradientColors(tabloColor);
return (
<View style={styles.avatarContainer}>
<LinearGradient
colors={gradientColors}
style={styles.avatarGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<Text style={styles.avatarInitials}>{initials}</Text>
{/* Member count indicator for group channels */}
{memberCount > 2 && (
<View style={styles.memberCountBadge}>
<Text style={styles.memberCountText}>{memberCount}</Text>
</View>
)}
</LinearGradient>
{/* Decorative ring */}
<View
style={[
styles.avatarRing,
{ borderColor: `${ColorMap[tabloColor]}30` },
]}
/>
{/* Status indicator (online/active) */}
<View style={styles.statusIndicator} />
</View>
);
};
// Toggle search animation
// const toggleSearch = () => {
@ -272,6 +272,51 @@ export default function HomeScreen() {
// </Animated.View>
// );
if (isLoading) {
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
{/* Loading Header */}
<LinearGradient
colors={["#1e3a8a", "#3b82f6", "#60a5fa"]}
style={styles.headerGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<View style={styles.headerContent}>
<View style={styles.headerBottom}>
<View style={styles.titleContainer}>
<Text style={styles.headerTitle}>Discussions</Text>
<Text style={styles.headerSubtitle}>
Chargement de vos conversations...
</Text>
</View>
</View>
</View>
{/* Decorative Elements */}
<View style={styles.decorativeCircle1} />
<View style={styles.decorativeCircle2} />
</LinearGradient>
{/* Loading Content */}
<View style={styles.loadingContentContainer}>
{/* Loading Skeleton Items */}
{[1, 2, 3, 4, 5].map((item) => (
<View key={item} style={styles.loadingItem}>
<View style={styles.loadingAvatar} />
<View style={styles.loadingTextContainer}>
<View style={styles.loadingTitle} />
<View style={styles.loadingSubtitle} />
</View>
</View>
))}
</View>
</View>
);
}
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
@ -327,7 +372,12 @@ export default function HomeScreen() {
}}
sort={sort}
options={options}
PreviewAvatar={AvatarWithTablos}
PreviewAvatar={(props) => (
<CustomChannelAvatar
channel={props.channel}
tablos={tablos || []}
/>
)}
PreviewTitle={CustomChannelTitle}
// ListHeaderComponent={SearchHeader}
// Show loading state during search
@ -655,4 +705,44 @@ const styles = StyleSheet.create({
textAlign: "center",
paddingHorizontal: 20,
},
// Loading Skeleton Styles
loadingContentContainer: {
flex: 1,
backgroundColor: "#f8fafc",
marginTop: -10,
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
paddingTop: 20,
paddingHorizontal: 20,
},
loadingItem: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 12,
marginBottom: 8,
},
loadingAvatar: {
width: 56,
height: 56,
borderRadius: 16,
backgroundColor: "#e5e7eb",
marginRight: 12,
},
loadingTextContainer: {
flex: 1,
},
loadingTitle: {
height: 20,
backgroundColor: "#e5e7eb",
borderRadius: 4,
marginBottom: 8,
width: "70%",
},
loadingSubtitle: {
height: 16,
backgroundColor: "#f3f4f6",
borderRadius: 4,
width: "50%",
},
});