Files
collective-lists/plan/fase-19-emoji-avatars.md
Oier Bravo Urtasun 427fe33c9c docs(plan): v1.0 cycle — fases 17–20 (versioning + list flow + emojis + euskera)
Four planned-fase docs for the first tagged release.

- Fase 17 — semver git tags + CHANGELOG.md (Keep-a-Changelog, [new]/
  [fix]/[tweak] prefixes), one big v0.0.0-beta backfill block covering
  Fases 0–16, About-modal in /settings rendering bundled CHANGELOG via
  ?raw import, deploy-script tag check + tag column in .deploys.log.
- Fase 18 — shopping list flow: required title (client + DB tighten),
  per-collective per-prefix #N auto-numbering parsed from typed title,
  collectives.default_list_title, collective_list_titles catalog + admin
  RPCs, autocomplete union (catalog + last-10). Migration 027.
- Fase 19 — EMOJI_CATALOG with faces/food/animals/objects categories
  (~280 codepoints, Unicode 14 baseline for iOS<17 compat), new
  EmojiPicker.svelte replacing the 8-emoji hardcoded selector at 4
  sites. No migration.
- Fase 20 — Euskera locale: messages/eu.json via machine-translate
  seed flagged with [needs review], users.language check constraint
  extended to ('en','es','eu'), accept-language parser + selector in
  /settings. Migration 028. v1.0.0 tag lands at the end of this fase.

README + CLAUDE.md updated with the v1.0 cycle pointers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 01:01:34 +02:00

115 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
### Fase 19 — Emoji avatars expansion (food + animal categories)
**v1.0 · 3/4.** Tercera fase del ciclo v1.0.
**Estado: 📋 Planificada.** Petición concreta: el avatar picker actual (`/settings`, también en colective-onboarding y `CreateCollectiveModal`) ofrece 8 emojis hardcoded. Ampliar con **todos los emojis oficiales** de food + animals según Unicode CLDR.
**Objetivo: agrupar emojis en categorías navegables (no scroll infinito flat), añadir food + animals completos (~270 glyphs combinados según Unicode 15), mantener performance del picker.**
**Decisiones clave:**
- Fuente de verdad: lista hardcoded en `apps/web/src/lib/data/emoji-catalog.ts`, no fetched. Generada una sola vez desde Unicode CLDR; commit del JSON. Tamaño ~510KB.
- Categorías visibles: **Caras** (mantiene los 8 actuales + ~30 más comunes), **Comida** (todos los food/drink), **Animales** (todos los animal/nature), **Objetos** (un pequeño set útil). El picker abre por defecto en "Caras"; pestañas en la cabecera.
- Sin emoji search en MVP de la fase — solo navegación por pestaña. Search si crece.
- Variantes de skin tone parked.
---
#### 19.1 Catálogo
**Fichero:** nuevo `apps/web/src/lib/data/emoji-catalog.ts`.
```ts
export type EmojiCategory = 'faces' | 'food' | 'animals' | 'objects';
export const EMOJI_CATALOG: Record<EmojiCategory, readonly string[]> = {
faces: ['😀', '😎', '🥳', '🤓', '👻', '🤖', '👽', '🥸', /* ~30 más */],
food: ['🍎', '🍐', '🍊', '🍋', '🍌', '🍉', '🍇', '🍓', /* ~85 más, todo Unicode "Food & Drink" */],
animals: ['🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼', /* ~150 más, todo "Animals & Nature" */],
objects: ['🏠', '🏡', '🌳', '🌻', '⭐', '🔥', '🎁', '🎈', /* ~20 más */],
} as const;
export const CATEGORY_LABELS: Record<EmojiCategory, string> = {
faces: 'Caras',
food: 'Comida',
animals: 'Animales',
objects: 'Objetos',
};
```
**Tareas:**
- [ ] **19.1.1** Generar los arrays. Fuente recomendada: [unicode-emoji-json](https://github.com/muan/unicode-emoji-json) `data-by-group.json`. Copiar el bloque "Food & Drink" + "Animals & Nature". Filtrar variantes (eliminar items con ZWJ secuencias raras como `🐕‍🦺` si Vite no las renderiza consistente). Mantener Unicode 15 baseline (sin Unicode 16 que iOS aún no incluye).
- [ ] **19.1.2** Tamaño final esperado: ~280 strings, ~10KB del bundle. Verificar.
- [ ] **19.1.3** Generador deterministic — un script `scripts/build-emoji-catalog.mjs` ejecutable manualmente que rebuild el `.ts` desde una copia local del JSON. No corre en CI; solo cuando Unicode se actualiza.
---
#### 19.2 Componente EmojiPicker
**Fichero:** nuevo `apps/web/src/lib/components/EmojiPicker.svelte`.
- [ ] **19.2.1** Props: `selected: string | null`, `onSelect: (emoji: string) => void`, `onClose?: () => void`.
- [ ] **19.2.2** Layout: header con pestañas (4 botones), body grid `grid-cols-8` (responsive `sm:grid-cols-10`), cada celda 32×32 con `text-2xl`. Click selecciona + dispara `onSelect`.
- [ ] **19.2.3** Categoría seleccionada en `$state`, default `'faces'`. Switch instantáneo (no animación).
- [ ] **19.2.4** Lazy render: solo monta el grid de la categoría activa (no las 4 al mismo tiempo) — evita coste de ~280 nodos vacíos.
- [ ] **19.2.5** A11y: pestañas son `role="tab"`, grid `role="grid"`, cada celda `role="gridcell"` + aria-label con el codepoint name. Keyboard nav (arrow keys) entre celdas.
---
#### 19.3 Integraciones
**Ficheros:**
- `apps/web/src/routes/(app)/settings/+page.svelte` (avatar del usuario).
- `apps/web/src/routes/(app)/onboarding/+page.svelte` (emoji inicial del colectivo).
- `apps/web/src/lib/components/CreateCollectiveModal.svelte` (emoji al crear).
- `apps/web/src/routes/(app)/collective/manage/+page.svelte` (emoji al renombrar — Fase 10.2).
- [ ] **19.3.1** Reemplazar los selectores hardcoded de emoji por `<EmojiPicker selected={...} onSelect={...} />`. Mantener el mismo padding/border del contenedor para no romper layouts.
- [ ] **19.3.2** Verificar que `users.avatar_emoji` y `collectives.avatar_emoji` siguen aceptando los nuevos glyphs (text column — sí).
---
#### 19.4 Tests
- [ ] **19.4.1** Vitest unit `apps/web/src/lib/components/EmojiPicker.test.ts`:
- EP-U-01 4 pestañas presentes.
- EP-U-02 default category = 'faces'.
- EP-U-03 click en una celda dispara `onSelect` con el emoji correcto.
- EP-U-04 cambiar pestaña re-renderiza el grid con la categoría correcta.
- EP-U-05 arrow keys mueven foco entre celdas.
- [ ] **19.4.2** Vitest unit `emoji-catalog.test.ts`:
- EC-U-01 `EMOJI_CATALOG.food.length >= 85`.
- EC-U-02 `EMOJI_CATALOG.animals.length >= 130`.
- EC-U-03 cada categoría no contiene duplicados.
- EC-U-04 no overlap entre categorías (un emoji solo en una).
- [ ] **19.4.3** Playwright `tests/e2e/emoji-picker.test.ts`:
- **EP-E-01** Ana en `/settings`, abre el picker, cambia a "Comida", selecciona 🍎, guarda → `users.avatar_emoji` actualizado, avatar renderiza 🍎.
- **EP-E-02** Onboarding: Eva (nuevo usuario) crea colectivo con emoji 🐱 desde "Animales".
---
#### 19.Z Verificación + cierre
- [ ] **19.Z.1** `just test-all` verde. Suma esperada: +9 unit + 2 e2e = +11.
- [ ] **19.Z.2** Smoke en iOS Safari + Android Chrome — verificar que TODOS los emojis del catálogo renderizan (no tofu □). Si algunos no, eliminar de la lista.
- [ ] **19.Z.3** Bundle-size check: bundle gz total no debe subir más de ~10KB respecto al baseline.
- [ ] **19.Z.4** Entrada en CHANGELOG: `[new] Emoji picker expanded with full food + animals categories (Fase 19).`
- [ ] **19.Z.5** Documentar en `docs/history/fase-19-emoji-avatars.md`.
---
### Riesgos / notas
- **iOS vs Android emoji rendering**: algunos Unicode 15 codepoints (🩷, 🩵, 🪿) aún no están en iOS 16 (sí en 17.4+). Filtrar a Unicode 14 si la base de usuarios incluye iOS < 17. Documentar la decisión.
- **CLDR shortName as aria-label**: para a11y, necesitamos el nombre del emoji ("red apple"). Si añadir el JSON completo de nombres infla el bundle, fallback a `aria-label={emoji}` (lee como "ÄPFel" en VoiceOver — feo pero funcional).
- **ZWJ sequences**: 👨‍👩‍👧 (familia) o 🏳️‍🌈 (rainbow flag) son secuencias múltiples. Los excluimos del catálogo en MVP de la fase (variantes de skin tone tampoco).
- **Performance del grid grande**: ~280 nodos en el DOM si renderizamos todas las pestañas. Por eso lazy-render por pestaña activa.
### Scope explícito fuera
- Buscar emoji por nombre.
- Skin tone variants.
- Recently-used (tracking de los últimos emojis del usuario).
- Custom emojis subidos por el colectivo.
- Sincronización del emoji con el sistema operativo del usuario.