The letter-on-grey placeholder is replaced by a fill whose color is a deterministic hash of the project key, so each project keeps a stable color across the sidebar, projects list, and settings header. Mid- saturation tones keep the white letter legible in both themes.
46 lines
1.1 KiB
TypeScript
46 lines
1.1 KiB
TypeScript
import { useMemo } from "react";
|
|
import {
|
|
Image,
|
|
type ImageStyle,
|
|
type StyleProp,
|
|
Text,
|
|
type TextStyle,
|
|
View,
|
|
type ViewStyle,
|
|
} from "react-native";
|
|
import { deriveProjectIconColor } from "@/utils/project-icon-color";
|
|
|
|
const WHITE_TEXT = { color: "#ffffff" } as const;
|
|
|
|
export function ProjectIconView({
|
|
iconDataUri,
|
|
initial,
|
|
projectKey,
|
|
imageStyle,
|
|
fallbackStyle,
|
|
textStyle,
|
|
}: {
|
|
iconDataUri: string | null;
|
|
initial: string;
|
|
projectKey: string;
|
|
imageStyle: StyleProp<ImageStyle>;
|
|
fallbackStyle: StyleProp<ViewStyle>;
|
|
textStyle: StyleProp<TextStyle>;
|
|
}) {
|
|
const imageSource = useMemo(() => ({ uri: iconDataUri ?? "" }), [iconDataUri]);
|
|
const fallbackStyles = useMemo(
|
|
() => [fallbackStyle, { backgroundColor: deriveProjectIconColor(projectKey) }],
|
|
[fallbackStyle, projectKey],
|
|
);
|
|
const textStyles = useMemo(() => [textStyle, WHITE_TEXT], [textStyle]);
|
|
|
|
if (iconDataUri) {
|
|
return <Image source={imageSource} style={imageStyle} />;
|
|
}
|
|
return (
|
|
<View style={fallbackStyles}>
|
|
<Text style={textStyles}>{initial}</Text>
|
|
</View>
|
|
);
|
|
}
|