Improve loading view, and fix bug with colors
This commit is contained in:
parent
a847b59c12
commit
59e896ee69
1 changed files with 186 additions and 96 deletions
|
|
@ -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%",
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue