diff --git a/js/app/components/name-color-picker/name-color-picker.tsx b/js/app/components/name-color-picker/name-color-picker.tsx index 40f20b10..966b7cf7 100644 --- a/js/app/components/name-color-picker/name-color-picker.tsx +++ b/js/app/components/name-color-picker/name-color-picker.tsx @@ -1,4 +1,9 @@ -import { Button, formatHandleWithAt, zero } from "@streamplace/components"; +import { + Button, + formatHandleWithAt, + Text, + zero, +} from "@streamplace/components"; import { Palette, SwatchBook, X } from "lucide-react-native"; import { useEffect, useState } from "react"; import { @@ -6,24 +11,52 @@ import { Modal, Platform, Pressable, - Text, TouchableOpacity, View, } from "react-native"; import ColorPicker, { HueSlider, + InputWidget, Panel1, - Preview, Swatches, } from "reanimated-color-picker"; import { useStore } from "store"; import { useChatProfile, useUserProfile } from "store/hooks"; import { PlaceStreamChatProfile } from "streamplace"; +/** + * Returns black or white depending on which contrasts better against the given color + */ +function getContrastColor(color: string): string { + let r: number, g: number, b: number; + + if (color.startsWith("#")) { + const hex = color.replace("#", ""); + r = parseInt(hex.substring(0, 2), 16); + g = parseInt(hex.substring(2, 4), 16); + b = parseInt(hex.substring(4, 6), 16); + } else if (color.startsWith("rgb")) { + const match = color.match(/(\d+),\s*(\d+),\s*(\d+)/); + if (match) { + r = parseInt(match[1]); + g = parseInt(match[2]); + b = parseInt(match[3]); + } else { + return "#fff"; + } + } else { + return "#fff"; + } + + const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255; + return luminance > 0.5 ? "#000" : "#fff"; +} + /** * Parses an RGB color string and returns an object with red, green, and blue values */ function parseRgbString(rgbString: string): PlaceStreamChatProfile.Color { + console.log(rgbString); if ( !rgbString || (!rgbString.startsWith("rgb(") && !rgbString.startsWith("rgba(")) @@ -45,6 +78,25 @@ function parseRgbString(rgbString: string): PlaceStreamChatProfile.Color { }; } +function rgbToHex(rgb: PlaceStreamChatProfile.Color) { + const hex = ( + (1 << 24) + + (rgb.red << 16) + + (rgb.green << 8) + + rgb.blue + ).toString(16); + return `#${hex.slice(-6)}`; +} + +// rgb(r, g, b) to hex +function cssRgbToHex(rgb: string) { + if (rgb.startsWith("#")) { + return rgb; + } + const parsed = parseRgbString(rgb); + return rgbToHex(parsed); +} + export function useNameColorPicker() { const [modalVisible, setModalVisible] = useState(false); const [tempColor, setTempColor] = useState("#bd6e86"); @@ -159,32 +211,77 @@ export function useNameColorPicker() { zero.bg.gray[800], zero.r.md, zero.p[3], - zero.mb[5], + zero.mb[3], zero.layout.flex.alignCenter, ]} > - - @{profile.handle} - - - Preview + + {formatHandleWithAt(profile)} )} {/* Color Picker */} - + setTempColor(result.rgb)} + onChangeJS={(result) => setTempColor(result.rgb)} > - - + + {cssRgbToHex(currentColor) !== cssRgbToHex(tempColor) && ( + + + {cssRgbToHex(currentColor)} + + + )} + + + @@ -250,47 +347,7 @@ export default function NameColorPicker({ text?: (color: string) => React.ReactNode; buttonProps?: any; }) { - const [modalVisible, setModalVisible] = useState(false); - const [tempColor, setTempColor] = useState("#bd6e86"); - const createChatProfileRecord = useStore( - (state) => state.createChatProfileRecord, - ); - const getChatProfileRecordFromPDS = useStore( - (state) => state.getChatProfileRecordFromPDS, - ); - const chatProfile = useChatProfile(); - const profile = useUserProfile(); - const isWeb = Platform.OS === "web"; - - const currentColor = chatProfile?.profile?.color - ? `rgb(${chatProfile.profile.color.red}, ${chatProfile.profile.color.green}, ${chatProfile.profile.color.blue})` - : "#bd6e86"; - - useEffect(() => { - if (profile?.did && !chatProfile?.profile) { - getChatProfileRecordFromPDS(); - } - setTempColor(currentColor); - }, [profile?.did, chatProfile?.profile?.color, currentColor]); - - const handleOpenModal = () => { - if (!isWeb) { - Keyboard.dismiss(); - } - setTempColor(currentColor); - setModalVisible(true); - }; - - const handleCloseModal = () => { - setModalVisible(false); - setTempColor(currentColor); // Reset to current color on cancel - }; - - const handleSaveColor = () => { - setModalVisible(false); - const parsed = parseRgbString(tempColor); - createChatProfileRecord(parsed.red, parsed.green, parsed.blue); - }; + const { currentColor, openModal, modal } = useNameColorPicker(); return ( @@ -298,7 +355,7 @@ export default function NameColorPicker({ variant="secondary" leftIcon={} style={[buttonProps?.style]} - onPress={handleOpenModal} + onPress={openModal} {...buttonProps} > @@ -306,149 +363,7 @@ export default function NameColorPicker({ - - - e.stopPropagation()} - > - {/* Header */} - - - - - Choose Color - - - - - - - - {/* User Preview */} - {profile?.handle && ( - - - {formatHandleWithAt(profile)} - - - Preview - - - )} - - {/* Color Picker */} - - setTempColor(result.rgb)} - > - - - - - - - - - - - - - - - - {/* Actions */} - - - - Cancel - - - - - Save Color - - - - - - + {modal} {children}