import React, { useState, useCallback } from 'react'; import { View, Text, TouchableOpacity, Image, ScrollView, StyleSheet, } from 'react-native'; import { useDubsTheme } from './theme'; const DICEBEAR_STYLES = [ 'adventurer', 'avataaars', 'fun-emoji', 'bottts', 'big-smile', 'thumbs', ]; const BG_COLORS = [ '1a1a2e', 'f43f5e', 'f97316', 'eab308', '22c55e', '3b82f6', '8b5cf6', 'ec4899', '06b6d4', '64748b', ]; export function generateSeed(): string { return Math.random().toString(36).slice(2, 10); } export function getAvatarUrl(style: string, seed: string, bg = '1a1a2e', size = 256): string { return `https://api.dicebear.com/9.x/${style}/png?seed=${seed}&backgroundColor=${bg}&size=${size}`; } export function parseAvatarUrl(url?: string | null): { style: string; seed: string; bg: string } { if (!url) return { style: 'adventurer', seed: generateSeed(), bg: '1a1a2e' }; try { const match = url.match(/\/\d+\.x\/([^/]+)\/(?:png|svg)\?seed=([^&]+)/); if (match) { const bgMatch = url.match(/backgroundColor=([^&]+)/); return { style: match[1], seed: match[2], bg: bgMatch?.[1] || '1a1a2e' }; } } catch {} return { style: 'adventurer', seed: generateSeed(), bg: '1a1a2e' }; } export interface AvatarEditorProps { style: string; seed: string; bg: string; onStyleChange: (style: string) => void; onSeedChange: (seed: string) => void; onBgChange: (bg: string) => void; disabled?: boolean; accentColor?: string; } export function AvatarEditor({ style: avatarStyle, seed: avatarSeed, bg: bgColor, onStyleChange, onSeedChange, onBgChange, disabled = false, accentColor, }: AvatarEditorProps) { const t = useDubsTheme(); const accent = accentColor || t.accent; return ( {/* Style picker */} {DICEBEAR_STYLES.map((st) => { const isSelected = st === avatarStyle; return ( onStyleChange(st)} activeOpacity={0.7} disabled={disabled} style={[ styles.styleTile, { borderColor: isSelected ? accent : t.border, borderWidth: isSelected ? 2 : 1, }, ]} > ); })} {/* Background color picker */} Background Color {BG_COLORS.map((color) => { const isSelected = color === bgColor; return ( onBgChange(color)} activeOpacity={0.7} disabled={disabled} style={[ styles.colorSwatch, { backgroundColor: `#${color}` }, isSelected && { borderColor: accent, borderWidth: 2.5 }, ]} /> ); })} ); } const styles = StyleSheet.create({ container: { gap: 10, }, row: { gap: 10, }, label: { fontSize: 14, fontWeight: '600', }, styleTile: { width: 72, height: 72, borderRadius: 16, overflow: 'hidden', }, styleTileImage: { width: '100%', height: '100%', }, colorSwatch: { width: 32, height: 32, borderRadius: 16, borderWidth: 1.5, borderColor: 'rgba(255,255,255,0.15)', }, });