No description
Find a file
Заид Омар Медхат | Zaid Omar Medhat cf432bd803 Telegram-style UX: folders, deletion modes, profile, settings, mobile nav
Data model / backend (migration 1720000000007):
- video_note as a first-class MediaRef kind — round video messages are
  explicit, no filename heuristics; mime-validated, own push label.
- Delete for me (message_hidden tombstones) alongside delete-for-everyone;
  group owners can moderate-delete any message in their groups.
- Clear history / delete chat per user (cleared_up_to_seq + hidden_at on
  conversation_members); deleted chats return on new activity.
- Manual chat folders + per-folder pins (chat_folders, chat_folder_items,
  chat_pins; folderId null = "All" tab). Every mutation returns and
  broadcasts a full snapshot on the personal channel (folders.update),
  syncing devices. New events: message.hidden, conversation.cleared,
  conversation.hidden.
- Shared-media listing: GET /conversations/:id/media?tab=media|files|voice.

Web:
- Right-click context menus (shared ContextMenu/ConfirmDialog): messages get
  a reactions row + copy/edit/delete; chats get pin/unpin per folder scope,
  folder membership, clear, delete; folder tabs get rename/delete.
- Telegram-style editing in the composer (banner + prefill), hover toolbar
  removed; delete dialog offers for-me / for-everyone per permissions.
- Folder tabs in the rail with per-folder pinned-first ordering.
- Profile panel (avatar, name, last seen, username, Message button) with
  shared media tabs; back arrow on mobile.
- Settings page (avatar, display name edit, theme, log out); contacts as a
  separate page; Telegram-style mobile bottom nav (Contacts|Chats|Settings)
  with total-unread badge.
- Chat scroll: opens at newest (ResizeObserver keeps bottom pinned while
  media loads), per-chat position memory, jump-to-newest button.
- Responsive single-pane layout under 900px.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BFRyKxkKEjfAgpXygzoNiD
2026-07-11 01:53:34 +05:00
infra Phase 6.5: notifications service + in-app toasts; in-app voice/video messages; SVG icons 2026-07-10 19:05:39 +05:00
packages Telegram-style UX: folders, deletion modes, profile, settings, mobile nav 2026-07-11 01:53:34 +05:00
.dockerignore init 2026-07-10 13:31:12 +05:00
.editorconfig init 2026-07-10 13:31:12 +05:00
.env.example Phase 6.5: notifications service + in-app toasts; in-app voice/video messages; SVG icons 2026-07-10 19:05:39 +05:00
.gitignore init 2026-07-10 13:31:12 +05:00
.prettierrc.json init 2026-07-10 13:31:12 +05:00
claude.md init 2026-07-10 13:31:12 +05:00
docker-compose.override.yml Phase 6.5: notifications service + in-app toasts; in-app voice/video messages; SVG icons 2026-07-10 19:05:39 +05:00
docker-compose.prod.yml Phase 6.5: notifications service + in-app toasts; in-app voice/video messages; SVG icons 2026-07-10 19:05:39 +05:00
docker-compose.yml infra: self-heal nginx after unordered stack restarts 2026-07-11 01:53:15 +05:00
eslint.config.mjs init 2026-07-10 13:31:12 +05:00
package.json Phase 6.5: notifications service + in-app toasts; in-app voice/video messages; SVG icons 2026-07-10 19:05:39 +05:00
pnpm-lock.yaml Phase 6.5: notifications service + in-app toasts; in-app voice/video messages; SVG icons 2026-07-10 19:05:39 +05:00
pnpm-workspace.yaml init 2026-07-10 13:31:12 +05:00
README.md init presence 2026-07-10 16:54:40 +05:00
specs.md init 2026-07-10 13:31:12 +05:00
tsconfig.base.json init 2026-07-10 13:31:12 +05:00
turbo.json init 2026-07-10 13:31:12 +05:00

Altricade Messenger

A self-hosted, cross-platform realtime messenger (chat + later calls). TypeScript everywhere, pnpm monorepo. See specs.md for the full architecture brief.

Status: Phase 0 — skeleton + Docker. The full backend stack boots with one command; no product features yet. Phases are built one at a time (see specs.md §8).

Architecture in one paragraph

Centrifugo is a dumb, fast pipe; the backend is the brain. Clients send over REST and receive over the Centrifugo WebSocket. The backend authorizes, persists to Postgres (source of truth), then publishes to Centrifugo which fans out to subscribers. Media lives in MinIO; only references travel through channels. Redis backs Centrifugo history/recovery and clustering.

Monorepo layout

packages/
  core/      # THE KEYSTONE — pure TS, imported by all (types, events, channels, ...)
  backend/   # Fastify service (Docker) — imports core
  web/       # React + Vite — imports core (Feature-Sliced Design)
  mobile/    # React Native + Expo (Phase 7) — placeholder
  desktop/   # Tauri shell over web build (Phase 7) — placeholder
infra/       # centrifugo, nginx, postgres init
docker-compose*.yml

core is consumed as source across packages, so a type change surfaces as an immediate compile error everywhere. Builds go through bundlers (Vite for web, tsup for backend); tsc is used only for type-checking.

Prerequisites

  • Node.js ≥ 22 and pnpm (via corepack enable)
  • Docker + Docker Compose

Local development

# 1. Install the workspace
pnpm install

# 2. Configure env (copy the template, adjust if you like — dev defaults work)
cp .env.example .env

# 3. Bring up the whole backend stack (Postgres, Redis, MinIO, Centrifugo,
#    backend, nginx) with hot reload. The dev override is auto-loaded.
docker compose up --build

# 4. Run the web client (separate terminal)
pnpm --filter @altricade/web dev

Endpoints (via the dev override):

Testing the API with Swagger

Open the Swagger UI, then:

  1. POST /auth/register (or /auth/login) and copy tokens.accessToken from the response.
  2. Click Authorize (top right), paste the access token, and authorize.
  3. Protected endpoints (/me, /auth/sessions, /auth/centrifugo-token, …) now work from "Try it out".

Dev notes:

  • The backend runs in Docker with node_modules baked into the image. After changing backend dependencies, recreate the container so it picks them up: docker compose up -d --build --force-recreate backend.
  • nginx resolves the backend upstream once at startup, so after recreating the backend container, restart nginx too: docker compose restart nginx (otherwise you'll get 502s from a stale cached IP).

Quality gates (enforced mechanically — a violation fails the build)

pnpm typecheck   # strict TypeScript across all packages
pnpm lint        # ESLint: no any, no type assertions, no non-null !, FSD import boundaries
pnpm build       # bundle backend + build web
pnpm format      # prettier

Standards: strict TS, no any, no as type assertions, no !, functional iteration, and strict Feature-Sliced Design import boundaries (frontend) plus layered backend modules. Dependencies are always added via pnpm add (never hand-pinned) at their latest trusted versions.

Deploy (production)

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build

Only nginx is published to the host; all other services stay on the private network. Secrets come from the environment — never commit a real .env.