diff --git a/app/views/WorkspaceView/ServerAvatar.tsx b/app/views/WorkspaceView/ServerAvatar.tsx index 99a6e47f88d..475775d295b 100644 --- a/app/views/WorkspaceView/ServerAvatar.tsx +++ b/app/views/WorkspaceView/ServerAvatar.tsx @@ -1,9 +1,11 @@ -import React from 'react'; +import React, { useState } from 'react'; import { StyleSheet, View } from 'react-native'; import { Image } from 'expo-image'; +import SkeletonPlaceholder from 'react-native-skeleton-placeholder'; import { isTablet } from '../../lib/methods/helpers'; import { useTheme } from '../../theme'; +import { CustomIcon } from '../../containers/CustomIcon'; const SIZE = 96; const MARGIN_TOP = isTablet ? 0 : 64; @@ -21,6 +23,27 @@ const styles = StyleSheet.create({ width: SIZE, height: SIZE, borderRadius: BORDER_RADIUS + }, + imageContainer: { + width: SIZE, + height: SIZE, + position: 'relative' + }, + skeletonOverlay: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + justifyContent: 'center', + alignItems: 'center' + }, + fallbackContainer: { + width: SIZE, + height: SIZE, + borderRadius: BORDER_RADIUS, + justifyContent: 'center', + alignItems: 'center' } }); @@ -29,13 +52,88 @@ interface IServerAvatar { image: string; } -// TODO: missing skeleton const ServerAvatar = React.memo(({ url, image }: IServerAvatar) => { - const { colors } = useTheme(); + const { colors, theme } = useTheme(); + const isDarkMode = theme === 'dark' || theme === 'black'; + + const imageUri = image ? `${url}/${image}` : null; + + // Track loading and error states + const [loading, setLoading] = useState(() => !!imageUri); + const [error, setError] = useState(false); + + // Reset states when image changes + React.useEffect(() => { + if (imageUri) { + setLoading(true); + setError(false); + } else { + setLoading(false); + setError(false); + } + }, [imageUri]); + + const handleLoadStart = () => { + setLoading(true); + setError(false); + }; + + const handleLoad = () => { + setLoading(false); + setError(false); + }; + + const handleError = () => { + setLoading(false); + setError(true); + }; + + // Show fallback icon on error or when no image is provided + if (error || !imageUri) { + return ( + + + + + + ); + } + // Show the actual image with an overlaid skeleton while loading return ( - {image && } + + + {loading && ( + + + + + + )} + ); });