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

6.6 KiB
Raw Blame History

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.

Fichero: nuevo apps/web/src/lib/data/emoji-catalog.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 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.