import { PurchasePrefixComposer } from '@nitrots/nitro-renderer'; import { FC, useEffect, useMemo, useRef, useState } from 'react'; import { LocalizeText, SendMessageComposer, PRESET_PREFIX_EFFECTS, parsePrefixColors, getPrefixEffectStyle, PREFIX_EFFECT_KEYFRAMES } from '../../../../../api'; import { CatalogLayoutProps } from './CatalogLayout.types'; import data from '@emoji-mart/data'; import Picker from '@emoji-mart/react'; const PRESET_COLORS: string[] = [ '#FF0000', '#FF6600', '#FFCC00', '#33CC00', '#00CCFF', '#0066FF', '#9933FF', '#FF33CC', '#FFFFFF', '#CCCCCC', '#999999', '#333333', '#FF9999', '#99FF99', '#9999FF', '#FFD700', '#FF4500', '#00CED1', '#8A2BE2', '#DC143C' ]; export const CatalogLayoutCustomPrefixView: FC = props => { const { page = null, hideNavigation = null } = props; useEffect(() => { hideNavigation(); }, [ page, hideNavigation ]); const [ prefixText, setPrefixText ] = useState(''); const [ colorMode, setColorMode ] = useState<'single' | 'perLetter'>('single'); const [ singleColor, setSingleColor ] = useState('#FFFFFF'); const [ letterColors, setLetterColors ] = useState>({}); const [ selectedLetterIndex, setSelectedLetterIndex ] = useState(null); const [ customColorInput, setCustomColorInput ] = useState('#FFFFFF'); const [ selectedIcon, setSelectedIcon ] = useState(''); const [ showIconPicker, setShowIconPicker ] = useState(false); const [ selectedEffect, setSelectedEffect ] = useState(''); const [ purchased, setPurchased ] = useState(false); const colorString = useMemo(() => { if(colorMode === 'single') return singleColor; if(!prefixText.length) return singleColor; return [ ...prefixText ].map((_, i) => letterColors[i] || singleColor).join(','); }, [ colorMode, singleColor, letterColors, prefixText ]); const previewColors = useMemo(() => { return parsePrefixColors(prefixText || '...', colorString || '#FFFFFF'); }, [ prefixText, colorString ]); const isValid = useMemo(() => { if(!prefixText.trim().length || prefixText.trim().length > 15) return false; if(colorMode === 'single') return /^#[0-9A-Fa-f]{6}$/.test(singleColor); const colors = colorString.split(','); return colors.every(c => /^#[0-9A-Fa-f]{6}$/.test(c)); }, [ prefixText, colorMode, singleColor, colorString ]); const handlePurchase = () => { if(!isValid) return; SendMessageComposer(new PurchasePrefixComposer(prefixText.trim(), colorString, selectedIcon, selectedEffect)); setPurchased(true); setTimeout(() => setPurchased(false), 2000); }; const handleColorSelect = (color: string) => { if(colorMode === 'single') { setSingleColor(color); setCustomColorInput(color); } else if(selectedLetterIndex !== null) { setLetterColors(prev => ({ ...prev, [selectedLetterIndex]: color })); setCustomColorInput(color); // Auto-avanza alla lettera successiva if(selectedLetterIndex < prefixText.length - 1) { const nextIdx = selectedLetterIndex + 1; setSelectedLetterIndex(nextIdx); setCustomColorInput(letterColors[nextIdx] || singleColor); } } }; const handleCustomColorChange = (value: string) => { setCustomColorInput(value); if(/^#[0-9A-Fa-f]{6}$/.test(value)) { if(colorMode === 'single') { setSingleColor(value); } else if(selectedLetterIndex !== null) { setLetterColors(prev => ({ ...prev, [selectedLetterIndex]: value })); } } }; const handleTextChange = (newText: string) => { setPrefixText(newText); if(selectedLetterIndex !== null && selectedLetterIndex >= newText.length) { setSelectedLetterIndex(newText.length > 0 ? newText.length - 1 : null); } }; const applyColorToAll = () => { if(!prefixText.length) return; const newColors: Record = {}; [ ...prefixText ].forEach((_, i) => { newColors[i] = customColorInput; }); setLetterColors(newColors); }; const hasMultiColor = colorMode === 'perLetter' && previewColors.length > 1 && new Set(previewColors).size > 1; const currentActiveColor = colorMode === 'single' ? singleColor : (selectedLetterIndex !== null ? (letterColors[selectedLetterIndex] || singleColor) : singleColor); const effectStyle = getPrefixEffectStyle(selectedEffect, previewColors[0] || '#FFFFFF'); return (
{ /* Header */ } { page.localization.getImage(0) && } { page.localization.getText(0) &&
} { /* Live Preview */ }
{ selectedIcon && { selectedIcon } } {'{'} { hasMultiColor ? [ ...(prefixText || '...') ].map((char, i) => ( { char } )) : (prefixText || '...') } {'}'} Username
{ /* Text + Icon Row */ }
handleTextChange(e.target.value) } /> { prefixText.length }/15
{ selectedIcon && }
{ /* Emoji Picker (emoji-mart) - fixed overlay */ } { showIconPicker && ( <>
setShowIconPicker(false) } />
{ setSelectedIcon(emoji.native); setShowIconPicker(false); } } theme="dark" previewPosition="none" skinTonePosition="search" perLine={ 8 } maxFrequentRows={ 2 } emojiSize={ 22 } emojiButtonSize={ 30 } dynamicWidth={ false } set="native" />
) } { /* Effect Selector */ }
{ PRESET_PREFIX_EFFECTS.map(fx => ( )) }
{ /* Color Mode Toggle */ }
{ /* Per-Letter Selector */ } { colorMode === 'perLetter' && prefixText.length > 0 && (
{ LocalizeText('catalog.prefix.color.hint') }
{ [ ...prefixText ].map((char, i) => { const charColor = letterColors[i] || singleColor; const isSelected = selectedLetterIndex === i; return (
{ setSelectedLetterIndex(i); setCustomColorInput(charColor); } }> { char }
); }) }
) } { /* Color Palette */ }
{ colorMode === 'perLetter' && selectedLetterIndex !== null && { LocalizeText('catalog.prefix.color.selected') } "{ prefixText[selectedLetterIndex] || '' }" }
{ PRESET_COLORS.map((color, idx) => { const isActive = currentActiveColor === color; return (
handleColorSelect(color) } /> ); }) }
handleCustomColorChange(e.target.value) } />
{ /* Purchase Footer */ }
{ LocalizeText('catalog.prefix.price') } { LocalizeText('catalog.prefix.price.amount') }
); };