Webapp Architecture β
The TuneCamp webapp is a single-page application (SPA) built with React and Vite, optimized for the music listening experience and decentralized/federated interaction.
Tech Stack β
- UI Framework: React (TypeScript)
- Build Tool: Vite
- Data fetching/caching: TanStack Query (
@tanstack/react-query), seelib/queryClient.tsandhooks/queries.tsfor shared cache keys - Client State: Zustand
- Routing: React Router (routes declared in
App.tsx, guarded by role/module wrapper components) - Instance Discovery: HTTP Gossip REST API
- Wallet: Ethers.js v6 (EIP-1193 injected provider, e.g. MetaMask)
- i18n:
i18next/react-i18next, locale files underi18n/locales/{en,it}/
Code Organization (webapp/src/) β
1. Components (components/) β
Organized by functional domain:
player/: Global player βPlayerBar,PlayerCanvas,QueuePanel,LyricsPanel,Waveform.admin/: Admin panels and library management lists (users, releases, tracks, federation, storage, backups, radio, reports, system health, β¦).artist/: Artist-facing tools β Fediverse panel, events manager, Stripe Connect card.network/: Federated-network cards (peer sessions, peer tracks).layout/:MainLayout(shell) andSidebar(primary nav).modals/: All dialog windows (auth, setup, publishing, purchase/unlock, playlists, imports, reports, β¦).ui/: Small reusable pieces (cards, headers, switchers, pills, form cards). There is no separateauth/component directory β auth-related dialogs live inmodals/.- A few components live directly under
components/(not in a subfolder):Comments.tsx,RelatedTracks.tsx,GenreTags.tsx,MetadataMatchModal.tsx,AccountMigrationCard.tsx,UpdateBanner.tsx.
See project-overview.md for the full, per-file catalog.
2. Pages (pages/) β
Each file is generally a route target wired up in App.tsx. Some legacy paths (/tracks, /favorites, /playlists, /my-playlists) now redirect into the merged Library page rather than rendering a dedicated component β the standalone ContentSearch page has been removed and folded into Search.tsx. Routes are wrapped with guard components (AdminGuard, EditorGuard, RootAdminGuard, ManagerOrRootGuard, ModuleGuard) that gate access by role or by instance feature flag (hideLive, hideStore, hideSocial, hideNetwork, hideSamples, hideCollab).
3. Frontend Plugin System (core/plugins/, plugins/) β
Optional integrations (Telegram, OpenRouter/AI, metadata providers, YouTube/yt-dlp, β¦) register themselves as FrontendPlugin objects with a small PluginRegistry (core/plugins/registry.tsx) instead of being hardcoded into the admin UI:
core/plugins/index.tsinitializes the registry, then usesimport.meta.globto eagerly load everyplugins/*/index.{ts,tsx}folder. A white-label build can drop a provider by deleting its directory β no other file needs to change.- Each plugin can declare an icon, description, a
statusCheck(maps backend health/plugin status to online/offline), aconfigPanel(rendered insideAdminSettingsPanel/IntegrationsPanel), and acustomAction(e.g. "Upload Cookies" for YouTube). - Current plugin folders:
plugins/builtins/(Telegram, OpenRouter),plugins/metadata/(iTunes, MusicBrainz, Deezer, Bandcamp, Spotify, SoundCloud),plugins/youtube/. - This registry is presentation-only (status badges, config forms in the admin UI); it does not perform the searches/downloads itself β that logic lives in the backend.
4. State Stores (stores/) β
Zustand stores, one per concern:
useAuthStore: logged-in user, JWT/session state, role.useConfigStore: backend integration health (Soulseek, iTunes, MusicBrainz, Discogs, Telegram, OpenRouter, Stripe, MoonPay, Google Drive, YouTube, Spotify, β¦) used to drive plugin status badges.useSiteSettingsStore: public site settings and per-module visibility flags (hideLive,hideStore,hideSocial,hideNetwork,hideSamples,hideCollab) consumed byModuleGuard.usePlayerStore: playback state β current track, queue, shuffle/original queue, volume, progress (persisted).useNowPlayingStore: the user's "now listening" presence opt-in, kept in sync with the player heartbeat.useWalletStore: connected wallet (provider, signer, address, ETH/USDC balances).useUIStore: theme and sidebar open/collapsed state (persisted).useConfirmStore: promise-based confirmation dialog (replaceswindow.confirm).
5. Data fetching (hooks/, lib/) β
lib/queryClient.ts+hooks/queries.ts: shared TanStack Query client and query-key constants for catalog lists, so reads and cache invalidation after mutations stay in sync.hooks/useBoard.ts,hooks/useNowPlayingHeartbeat.ts,hooks/useOwnedNFTs.ts,hooks/usePurchases.ts,hooks/useVersionCheck.ts: feature-specific data hooks.
6. Services (services/) β
api.ts: singleAPIobject wrapping every REST call to the TuneCamp backend (auth, catalog, uploads, payments, admin, radio, dig, β¦).wallet.ts: browser wallet management (connection, signing, on-chain transactions via ethers).
7. Utilities (utils/) β
Formatting, sanitization, markdown rendering, permission checks (permissions.ts, roles.ts), URL helpers, theme/font helpers, and image-fallback logic used across components.
Navigation & Playback Flow β
- Navigation: User clicks an album card β
AlbumDetails.tsxfetches data viaapi.ts(through a TanStack Query hook) β data rendered withartist/andui/components. - Playback: Click "Play" β track pushed onto
usePlayerStore's queue βplayer/PlayerBar.tsxstreams from the backend (/api/tracks/:id/stream), falling back to the external source URL when no local file exists. - Now Listening: While a track plays and the user has opted in (
useNowPlayingStore),hooks/useNowPlayingHeartbeat.tsperiodically reports the current track to the presence endpoint. - Web3 Interaction: Connect wallet β
wallet.ts/useWalletStoremanage the account β purchase releases or unlock gated content on-chain. - Admin integrations:
AdminSettingsPanel/IntegrationsPanelrender one card per registeredFrontendPlugin, pulling live status fromuseConfigStoreand rendering each plugin's ownconfigPanel.