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>
115 lines
6.6 KiB
Markdown
115 lines
6.6 KiB
Markdown
### 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 ~5–10KB.
|
||
- 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.
|