From cfa2eadec945659cb2e868b4475625a0ce16c804 Mon Sep 17 00:00:00 2001 From: Kerboul Date: Sun, 31 May 2026 23:57:00 +0200 Subject: [PATCH] feat: conformite enonce - explorer, favoris, stats perso, tests, slots Fonctionnel - Backend messages : GET /api/messages/:id (detail) + recherche (q), pagination par curseur (before/limit) avec enveloppe { items, nextCursor, hasMore } ; le flux temps reel garde l'ancien format quand aucun parametre. - Explorer (/explorer) : catalogue distant, recherche debouncee + annulable (AbortController), filtre, defilement infini, etat garde (keep-alive). - Details par id : /message/:id et /shop/p/:id (consomment route.params). - Favoris (/favoris) : liste perso persistee en localStorage, notation (note/rating/statut) via modale, refletee partout (bouton favori). - Mes stats (/mes-stats) : agregats derives des favoris (note moyenne, top pays/auteurs, statuts), auto-mis a jour, route gardee si liste vide. - Routeur : pages secondaires en lazy-load + repli, garde beforeEnter. Technique - Slots : PrefSection (slot defaut + slot nomme) enveloppe les 5 sections "Mes Persos" ; Modal (Teleport + slots). - v-model custom : SearchBox (defineModel + debounce). - Directive custom : v-click-outside. - Tests Vitest : 25 tests (etat, fonctions, composants), ~86% du code metier. - Retrait d'Ionic (inutilise). Script typecheck backend ; tsconfig @types/bun. - Correctif type : garde stockLimit nullable dans l'achat (catalog.ts). - README complet (URL, stack, run, tests, secrets, deploiement, mention IA). --- .env.prod.example | 28 +- .gitattributes | 6 +- .gitea/workflows/deploy.yml | 70 +- .gitignore | 16 +- DEPLOY.md | 128 +-- LICENSE | 22 +- README.md | 125 ++- backend/.env.example | 8 +- backend/.gitignore | 2 +- backend/Dockerfile | 44 +- backend/package.json | 41 +- .../20260529094703_init/migration.sql | 26 +- .../migration.sql | 224 +++--- backend/prisma/schema.prisma | 260 +++---- backend/prisma/seed.ts | 564 +++++++------- backend/src/index.ts | 114 +-- backend/src/lib/ads.ts | 142 ++-- backend/src/lib/catalog.ts | 628 +++++++-------- backend/src/lib/geo.ts | 190 ++--- backend/src/lib/ip.ts | 96 +-- backend/src/lib/perks.ts | 260 +++---- backend/src/lib/prisma.ts | 20 +- backend/src/lib/stats.ts | 414 +++++----- backend/src/lib/storage.ts | 96 +-- backend/src/lib/wallet.ts | 254 +++--- backend/src/realtime.ts | 294 +++---- backend/src/routes/ads.ts | 80 +- backend/src/routes/alert.ts | 134 ++-- backend/src/routes/messages.ts | 330 ++++---- backend/src/routes/perks.ts | 28 +- backend/src/routes/shop.ts | 172 ++-- backend/src/routes/uploads.ts | 186 ++--- backend/src/routes/wallet.ts | 44 +- backend/tsconfig.json | 24 +- bun.lock | 263 +++++-- deploy/nginx.conf | 84 +- docker-compose.prod.yml | 134 ++-- docker-compose.yml | 70 +- frontend/Dockerfile | 34 +- frontend/index.html | 24 +- frontend/package.json | 13 +- frontend/src/App.vue | 74 +- frontend/src/components/AdBand.vue | 326 ++++---- frontend/src/components/AnimatedNumber.vue | 100 +-- frontend/src/components/ChatHeader.vue | 266 +++---- frontend/src/components/FavButton.vue | 41 + frontend/src/components/InlineCasinoAd.vue | 304 ++++---- .../src/components/MessageAttachments.vue | 160 ++-- frontend/src/components/MessageItem.vue | 478 ++++++------ frontend/src/components/MessageItemBubble.vue | 313 ++++---- .../src/components/MessageItemCompact.vue | 2 + frontend/src/components/MessageList.vue | 196 ++--- frontend/src/components/Modal.vue | 64 ++ frontend/src/components/RichContent.vue | 216 ++--- frontend/src/components/SearchBox.spec.ts | 34 + frontend/src/components/SearchBox.vue | 83 ++ frontend/src/components/SendButton.vue | 194 ++--- frontend/src/components/StatsTicker.vue | 432 +++++----- frontend/src/components/StyleContextMenu.vue | 276 +++---- frontend/src/components/ThemePicker.spec.ts | 27 + frontend/src/components/ThemePicker.vue | 82 +- frontend/src/components/shop/PrefSection.vue | 16 + frontend/src/components/shop/ProductCard.vue | 661 ++++++++-------- .../components/shop/persos/BgPrefsSection.vue | 6 +- .../shop/persos/IpColorPrefsSection.vue | 12 +- .../shop/persos/PetsPrefsSection.vue | 12 +- .../shop/persos/SendButtonPrefsSection.vue | 12 +- .../shop/persos/SendSkinPrefsSection.vue | 12 +- frontend/src/composables/ipColor.spec.ts | 18 + frontend/src/composables/ipColor.ts | 66 +- frontend/src/composables/useAds.ts | 134 ++-- frontend/src/composables/useAlert.ts | 172 ++-- frontend/src/composables/useAttachments.ts | 86 +- frontend/src/composables/useContextMenu.ts | 120 +-- frontend/src/composables/useCustomStyles.ts | 160 ++-- frontend/src/composables/useDebounce.spec.ts | 26 + frontend/src/composables/useDebounce.ts | 33 + frontend/src/composables/useFavorites.spec.ts | 71 ++ frontend/src/composables/useFavorites.ts | 124 +++ frontend/src/composables/useMessageItem.ts | 124 +-- frontend/src/composables/useMessages.ts | 424 +++++----- frontend/src/composables/useMeta.spec.ts | 20 + frontend/src/composables/usePerks.spec.ts | 25 + frontend/src/composables/usePerks.ts | 86 +- frontend/src/composables/useRealtime.ts | 250 +++--- frontend/src/composables/useShop.ts | 266 +++---- frontend/src/composables/useWallet.spec.ts | 26 + frontend/src/composables/useWallet.ts | 144 ++-- frontend/src/directives/clickOutside.ts | 28 + frontend/src/main.ts | 26 +- frontend/src/style.css | 232 +++--- frontend/src/views/ExplorerPage.vue | 192 +++++ frontend/src/views/FavorisPage.vue | 162 ++++ frontend/src/views/HomePage.vue | 4 +- frontend/src/views/MesStatsPage.vue | 169 ++++ frontend/src/views/MessageDetailPage.vue | 125 +++ frontend/src/views/ProductDetailPage.vue | 78 ++ frontend/src/views/ShopPage.vue | 456 +++++------ frontend/src/vite-env.d.ts | 2 +- frontend/tsconfig.json | 48 +- frontend/tsconfig.node.json | 22 +- frontend/vitest.config.ts | 27 + maquettes/chat/v1.svg | 736 +++++++++--------- maquettes/chat/v2.svg | 374 ++++----- maquettes/chat/v3.svg | 478 ++++++------ maquettes/shop/shop1.svg | 494 ++++++------ maquettes/shop/shop2.svg | 394 +++++----- maquettes/shop/shop3-.svg | 322 ++++---- maquettes/shop/shop4.svg | 478 ++++++------ package.json | 20 +- scripts/dev-stack.ts | 176 ++--- 111 files changed, 9634 insertions(+), 7875 deletions(-) create mode 100644 frontend/src/components/FavButton.vue create mode 100644 frontend/src/components/Modal.vue create mode 100644 frontend/src/components/SearchBox.spec.ts create mode 100644 frontend/src/components/SearchBox.vue create mode 100644 frontend/src/components/ThemePicker.spec.ts create mode 100644 frontend/src/components/shop/PrefSection.vue create mode 100644 frontend/src/composables/ipColor.spec.ts create mode 100644 frontend/src/composables/useDebounce.spec.ts create mode 100644 frontend/src/composables/useDebounce.ts create mode 100644 frontend/src/composables/useFavorites.spec.ts create mode 100644 frontend/src/composables/useFavorites.ts create mode 100644 frontend/src/composables/useMeta.spec.ts create mode 100644 frontend/src/composables/usePerks.spec.ts create mode 100644 frontend/src/composables/useWallet.spec.ts create mode 100644 frontend/src/directives/clickOutside.ts create mode 100644 frontend/src/views/ExplorerPage.vue create mode 100644 frontend/src/views/FavorisPage.vue create mode 100644 frontend/src/views/MesStatsPage.vue create mode 100644 frontend/src/views/MessageDetailPage.vue create mode 100644 frontend/src/views/ProductDetailPage.vue create mode 100644 frontend/vitest.config.ts diff --git a/.env.prod.example b/.env.prod.example index 8ea6bbb..8463c89 100644 --- a/.env.prod.example +++ b/.env.prod.example @@ -1,14 +1,14 @@ -# Copy to `.env.prod` on the deploy host (CT502) and fill with real secrets. -# `.env.prod` is gitignored — never commit real credentials. - -# Database -POSTGRES_DB=xip -POSTGRES_USER=xip -POSTGRES_PASSWORD=change-me-to-a-strong-secret - -# Public origin (baked into the frontend build + used by the WS URL) -PUBLIC_URL=https://xip.kerboul.me - -# Paywall: "true" = open bar (everything free for everyone), "false" = paywall on -# (free only on localhost, per the README). -XIP_OPEN_BAR=true +# Copy to `.env.prod` on the deploy host (CT502) and fill with real secrets. +# `.env.prod` is gitignored — never commit real credentials. + +# Database +POSTGRES_DB=xip +POSTGRES_USER=xip +POSTGRES_PASSWORD=change-me-to-a-strong-secret + +# Public origin (baked into the frontend build + used by the WS URL) +PUBLIC_URL=https://xip.kerboul.me + +# Paywall: "true" = open bar (everything free for everyone), "false" = paywall on +# (free only on localhost, per the README). +XIP_OPEN_BAR=true diff --git a/.gitattributes b/.gitattributes index 23ef490..252ee15 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,3 @@ -# Shell scripts must keep LF endings or they break with "bad interpreter" on Linux. -*.sh text eol=lf -docker-entrypoint.sh text eol=lf +# Shell scripts must keep LF endings or they break with "bad interpreter" on Linux. +*.sh text eol=lf +docker-entrypoint.sh text eol=lf diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 0183fc2..1c41fe5 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -1,35 +1,35 @@ -name: Deploy XIP - -# Auto-deploy on every push to main. The runner SSHes into the xip-app CT -# (Echelon CT502) and runs scripts/deploy.sh, which pulls + rebuilds the stack. -on: - push: - branches: [main] - workflow_dispatch: - -# Serialize deploys: never run two deploys against the CT at the same time -# (concurrent `docker compose up --build` on the same project races and fails). -concurrency: - group: deploy-xip-prod - cancel-in-progress: false - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Deploy over SSH to xip-app - env: - # Secrets via env (not inlined in the script) so the multi-line key - # keeps its newlines and never breaks shell quoting. - DEPLOY_HOST: ${{ secrets.XIP_DEPLOY_HOST }} - DEPLOY_USER: ${{ secrets.XIP_DEPLOY_USER }} - DEPLOY_KEY: ${{ secrets.XIP_DEPLOY_KEY }} - run: | - set -e - command -v ssh >/dev/null 2>&1 || (apt-get update && apt-get install -y --no-install-recommends openssh-client) - mkdir -p ~/.ssh - printf '%s\n' "$DEPLOY_KEY" > ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - ssh -i ~/.ssh/id_ed25519 \ - -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "$DEPLOY_USER@$DEPLOY_HOST" 'bash /opt/xip/scripts/deploy.sh' +name: Deploy XIP + +# Auto-deploy on every push to main. The runner SSHes into the xip-app CT +# (Echelon CT502) and runs scripts/deploy.sh, which pulls + rebuilds the stack. +on: + push: + branches: [main] + workflow_dispatch: + +# Serialize deploys: never run two deploys against the CT at the same time +# (concurrent `docker compose up --build` on the same project races and fails). +concurrency: + group: deploy-xip-prod + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Deploy over SSH to xip-app + env: + # Secrets via env (not inlined in the script) so the multi-line key + # keeps its newlines and never breaks shell quoting. + DEPLOY_HOST: ${{ secrets.XIP_DEPLOY_HOST }} + DEPLOY_USER: ${{ secrets.XIP_DEPLOY_USER }} + DEPLOY_KEY: ${{ secrets.XIP_DEPLOY_KEY }} + run: | + set -e + command -v ssh >/dev/null 2>&1 || (apt-get update && apt-get install -y --no-install-recommends openssh-client) + mkdir -p ~/.ssh + printf '%s\n' "$DEPLOY_KEY" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh -i ~/.ssh/id_ed25519 \ + -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ + "$DEPLOY_USER@$DEPLOY_HOST" 'bash /opt/xip/scripts/deploy.sh' diff --git a/.gitignore b/.gitignore index 5dc5fe9..5aef152 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ -node_modules/ -dist/ -.env -.env.local -.env.prod -*.log -.DS_Store -Thumbs.db +node_modules/ +dist/ +.env +.env.local +.env.prod +*.log +.DS_Store +Thumbs.db diff --git a/DEPLOY.md b/DEPLOY.md index 65433eb..1cad01a 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -1,64 +1,64 @@ -# Déploiement XIP - -Production : **https://xip.kerboul.me** — déploiement continu sur push `main`. - -## Architecture (pattern Vireli, cluster SENTINEL) - -``` -Cloudflare (*.kerboul.me) ─► VPS WireGuard ─► Traefik (CT102, Cerberus) - │ Host(`xip.kerboul.me`) → http://192.168.1.242:80 - ▼ - CT502 « xip-app » (Echelon, Docker host) - ┌───────────────────────────────────────┐ - │ web (nginx:80) │ - │ ├── / → SPA Vue (statique) │ - │ ├── /api/ → backend:3000 │ - │ └── /ws → backend:3000 (WS) │ - │ backend (bun:3000, Hono + Prisma) │ - │ postgres:16 redis:7 │ - └───────────────────────────────────────┘ -``` - -Origine unique : le front (buildé avec `VITE_API_URL=https://xip.kerboul.me`) -appelle `/api` et `wss://xip.kerboul.me/ws`, nginx proxifie vers le backend. -Traefik termine le TLS (Let's Encrypt, DNS challenge Cloudflare). - -## CI/CD (Gitea Actions) - -`.gitea/workflows/deploy.yml` se déclenche sur push `main` (+ `workflow_dispatch`). -Le runner (CT121) se connecte en SSH au CT502 et exécute `scripts/deploy.sh` -(`git reset --hard origin/main` + `docker compose up -d --build`). - -Migrations Prisma + seed (idempotent) tournent au démarrage du conteneur backend -(`backend/docker-entrypoint.sh`). - -### Secrets du repo (Gitea → Settings → Actions → Secrets) -| Secret | Rôle | -|--------|------| -| `XIP_DEPLOY_HOST` | IP du CT502 (192.168.1.242) | -| `XIP_DEPLOY_USER` | utilisateur de déploiement (`deploy`) | -| `XIP_DEPLOY_KEY` | clé privée SSH autorisée sur le CT502 | - -## Fichiers - -| Fichier | Rôle | -|---------|------| -| `docker-compose.prod.yml` | stack prod (postgres, redis, backend, web) | -| `backend/Dockerfile` + `docker-entrypoint.sh` | image backend, migrate+seed au boot | -| `frontend/Dockerfile` | build Vite → nginx | -| `deploy/nginx.conf` | reverse proxy single-origin | -| `scripts/deploy.sh` | script de (re)déploiement sur le CT | -| `.env.prod` (non commité) | secrets : voir `.env.prod.example` | - -## Paywall - -`XIP_OPEN_BAR=true` (dans `.env.prod`) = **open bar** : toutes les fonctionnalités -payantes gratuites pour tout le monde. Mettre `false` pour réactiver le paywall -(gratuit uniquement en localhost). Logique centralisée dans `backend/src/lib/ip.ts` -(`isFree()`). - -## Redéploiement manuel - -```bash -ssh deploy@192.168.1.242 'bash /opt/xip/scripts/deploy.sh' -``` +# Déploiement XIP + +Production : **https://xip.kerboul.me** — déploiement continu sur push `main`. + +## Architecture (pattern Vireli, cluster SENTINEL) + +``` +Cloudflare (*.kerboul.me) ─► VPS WireGuard ─► Traefik (CT102, Cerberus) + │ Host(`xip.kerboul.me`) → http://192.168.1.242:80 + ▼ + CT502 « xip-app » (Echelon, Docker host) + ┌───────────────────────────────────────┐ + │ web (nginx:80) │ + │ ├── / → SPA Vue (statique) │ + │ ├── /api/ → backend:3000 │ + │ └── /ws → backend:3000 (WS) │ + │ backend (bun:3000, Hono + Prisma) │ + │ postgres:16 redis:7 │ + └───────────────────────────────────────┘ +``` + +Origine unique : le front (buildé avec `VITE_API_URL=https://xip.kerboul.me`) +appelle `/api` et `wss://xip.kerboul.me/ws`, nginx proxifie vers le backend. +Traefik termine le TLS (Let's Encrypt, DNS challenge Cloudflare). + +## CI/CD (Gitea Actions) + +`.gitea/workflows/deploy.yml` se déclenche sur push `main` (+ `workflow_dispatch`). +Le runner (CT121) se connecte en SSH au CT502 et exécute `scripts/deploy.sh` +(`git reset --hard origin/main` + `docker compose up -d --build`). + +Migrations Prisma + seed (idempotent) tournent au démarrage du conteneur backend +(`backend/docker-entrypoint.sh`). + +### Secrets du repo (Gitea → Settings → Actions → Secrets) +| Secret | Rôle | +|--------|------| +| `XIP_DEPLOY_HOST` | IP du CT502 (192.168.1.242) | +| `XIP_DEPLOY_USER` | utilisateur de déploiement (`deploy`) | +| `XIP_DEPLOY_KEY` | clé privée SSH autorisée sur le CT502 | + +## Fichiers + +| Fichier | Rôle | +|---------|------| +| `docker-compose.prod.yml` | stack prod (postgres, redis, backend, web) | +| `backend/Dockerfile` + `docker-entrypoint.sh` | image backend, migrate+seed au boot | +| `frontend/Dockerfile` | build Vite → nginx | +| `deploy/nginx.conf` | reverse proxy single-origin | +| `scripts/deploy.sh` | script de (re)déploiement sur le CT | +| `.env.prod` (non commité) | secrets : voir `.env.prod.example` | + +## Paywall + +`XIP_OPEN_BAR=true` (dans `.env.prod`) = **open bar** : toutes les fonctionnalités +payantes gratuites pour tout le monde. Mettre `false` pour réactiver le paywall +(gratuit uniquement en localhost). Logique centralisée dans `backend/src/lib/ip.ts` +(`isFree()`). + +## Redéploiement manuel + +```bash +ssh deploy@192.168.1.242 'bash /opt/xip/scripts/deploy.sh' +``` diff --git a/LICENSE b/LICENSE index 7a3094a..e007a57 100644 --- a/LICENSE +++ b/LICENSE @@ -1,11 +1,11 @@ -DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -Version 2, December 2004 - -Copyright (C) 2004 Sam Hocevar - -Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. - -DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. + +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.md b/README.md index c8b0394..9cc52ac 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,110 @@ -# XIP +# XIP — Réseau social « sans modération » -Réseau social à consommer sans modération +SPA satirique : un chat public en temps réel où **ton pseudo = ton adresse IP**, +noyé sous les pubs et le merchandising. Catalogue de messages distant, liste +perso de favoris, statistiques dérivées, marketplace à crédits fictifs, thèmes +(dont WhatsApp). -## Concept +🌐 **Application déployée : https://xip.kerboul.me** -Faire un réseau social open sans contrôles ni modération. -Pas de compte, Pseudo = IP. -Merchandising à fond. -Envahit par des Pubs. +--- + +## Stack + +| Couche | Technologies | +|--------|--------------| +| Frontend | Vue 3 (` - - + + + + + + XIP + + +
+ + + diff --git a/frontend/package.json b/frontend/package.json index c420332..1a0ccca 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,21 +4,24 @@ "private": true, "scripts": { "dev": "vite", - "build": "vite build", + "build": "vue-tsc && vite build", + "preview": "vite preview", "typecheck": "vue-tsc --noEmit", - "preview": "vite preview" + "test": "vitest run", + "test:cov": "vitest run --coverage" }, "dependencies": { - "@ionic/vue": "^8.3.0", - "@ionic/vue-router": "^8.3.0", - "ionicons": "^7.4.0", "vue": "^3.5.0", "vue-router": "^4.4.0" }, "devDependencies": { "@vitejs/plugin-vue": "^5.1.0", + "@vitest/coverage-v8": "^2.1.0", + "@vue/test-utils": "^2.4.6", + "happy-dom": "^15.0.0", "typescript": "^5.6.0", "vite": "^5.4.0", + "vitest": "^2.1.0", "vue-tsc": "^2.1.0" } } diff --git a/frontend/src/App.vue b/frontend/src/App.vue index d96ca47..48e3313 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,8 +1,72 @@ + - + diff --git a/frontend/src/components/AdBand.vue b/frontend/src/components/AdBand.vue index 3c73836..8592590 100644 --- a/frontend/src/components/AdBand.vue +++ b/frontend/src/components/AdBand.vue @@ -1,163 +1,163 @@ - - - - - - + + + + + + diff --git a/frontend/src/components/AnimatedNumber.vue b/frontend/src/components/AnimatedNumber.vue index 47ce285..04cd0b2 100644 --- a/frontend/src/components/AnimatedNumber.vue +++ b/frontend/src/components/AnimatedNumber.vue @@ -1,50 +1,50 @@ - - - - + + + + diff --git a/frontend/src/components/ChatHeader.vue b/frontend/src/components/ChatHeader.vue index 7baf082..3d96d82 100644 --- a/frontend/src/components/ChatHeader.vue +++ b/frontend/src/components/ChatHeader.vue @@ -1,133 +1,133 @@ - - - - - - + + + + + + diff --git a/frontend/src/components/FavButton.vue b/frontend/src/components/FavButton.vue new file mode 100644 index 0000000..104e125 --- /dev/null +++ b/frontend/src/components/FavButton.vue @@ -0,0 +1,41 @@ + + + + + + diff --git a/frontend/src/components/InlineCasinoAd.vue b/frontend/src/components/InlineCasinoAd.vue index af5db4b..1227618 100644 --- a/frontend/src/components/InlineCasinoAd.vue +++ b/frontend/src/components/InlineCasinoAd.vue @@ -1,152 +1,152 @@ - - - - - - + + + + + + diff --git a/frontend/src/components/MessageAttachments.vue b/frontend/src/components/MessageAttachments.vue index 439fc71..dafce69 100644 --- a/frontend/src/components/MessageAttachments.vue +++ b/frontend/src/components/MessageAttachments.vue @@ -1,80 +1,80 @@ - - - - - - + + + + + + diff --git a/frontend/src/components/MessageItem.vue b/frontend/src/components/MessageItem.vue index 0a578f4..247d5ad 100644 --- a/frontend/src/components/MessageItem.vue +++ b/frontend/src/components/MessageItem.vue @@ -1,238 +1,240 @@ - - - - - - + + + + + + diff --git a/frontend/src/components/MessageItemBubble.vue b/frontend/src/components/MessageItemBubble.vue index 7ca9cd3..47d7125 100644 --- a/frontend/src/components/MessageItemBubble.vue +++ b/frontend/src/components/MessageItemBubble.vue @@ -1,154 +1,159 @@ - - - - - - + + + + + + diff --git a/frontend/src/components/MessageItemCompact.vue b/frontend/src/components/MessageItemCompact.vue index c908b77..9985169 100644 --- a/frontend/src/components/MessageItemCompact.vue +++ b/frontend/src/components/MessageItemCompact.vue @@ -23,6 +23,7 @@ type="button" @click="$emit('reply', { id: message.id, authorIp: message.authorIp })" >↩ + @@ -50,6 +51,7 @@ import type { Message } from '@/composables/useMessages'; import { useMessageItem } from '@/composables/useMessageItem'; import RichContent from './RichContent.vue'; import MessageAttachments from './MessageAttachments.vue'; +import FavButton from './FavButton.vue'; defineProps<{ message: Message; myIp?: string }>(); defineEmits<{ reply: [payload: { id: string; authorIp: string }] }>(); diff --git a/frontend/src/components/MessageList.vue b/frontend/src/components/MessageList.vue index dd4dfa1..3ec9572 100644 --- a/frontend/src/components/MessageList.vue +++ b/frontend/src/components/MessageList.vue @@ -1,98 +1,98 @@ - - - - - - + + + + + + diff --git a/frontend/src/components/Modal.vue b/frontend/src/components/Modal.vue new file mode 100644 index 0000000..1e897cf --- /dev/null +++ b/frontend/src/components/Modal.vue @@ -0,0 +1,64 @@ + + + + + + diff --git a/frontend/src/components/RichContent.vue b/frontend/src/components/RichContent.vue index b9f5f2a..2058530 100644 --- a/frontend/src/components/RichContent.vue +++ b/frontend/src/components/RichContent.vue @@ -1,108 +1,108 @@ - -