Commit Graph

23 Commits

Author SHA1 Message Date
Mistral Vibe
b2d6b4d113 Refactor storage to provider-agnostic band-scoped model
Replaces per-member Nextcloud credentials with a BandStorage model that
supports multiple providers. Credentials are Fernet-encrypted at rest;
worker receives audio via an internal streaming endpoint instead of
direct storage access.

- Add BandStorage DB model with partial unique index (one active per band)
- Add migrations 0007 (create band_storage) and 0008 (drop old nc columns)
- Add StorageFactory that builds the correct StorageClient from BandStorage
- Add storage router: connect/nextcloud, OAuth2 authorize/callback, list, disconnect
- Add Fernet encryption helpers in security/encryption.py
- Rewrite watcher for per-band polling via internal API config endpoint
- Update worker to stream audio from API instead of accessing storage directly
- Update frontend: new storage API in bands.ts, rewritten StorageSection,
  simplified band creation modal (no storage step)
- Add STORAGE_ENCRYPTION_KEY to all docker-compose files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 23:22:36 +02:00
Mistral Vibe
4bab0a76f7 Build update 2026-04-10 12:23:27 +02:00
Mistral Vibe
efb16a096d feat(band): two-step create flow with Nextcloud storage setup
Band creation now starts with a Nextcloud credentials step when
storage is not yet configured. Users can save NC credentials
(or skip) before proceeding to band name/slug/folder entry.

- StorageStep: NC URL, username, app password; PATCH /auth/me/settings
- BandStep: name, slug (auto-generated), NC folder with warning when NC not set
- StepDots: animated pill indicators for current step
- Modal fetches /auth/me on open to determine starting step

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 09:26:25 +02:00
Mistral Vibe
1a29e6f492 feat(band): add Nextcloud folder field to band creation modal
The "New band" button in TopBandBar previously navigated to the HomePage
which immediately redirected back if any bands already existed, making it
impossible to create additional bands.

Replaced the navigation with an inline modal that:
- Opens directly from the "New band" button in the band switcher dropdown
- Fields: band name (with auto-slug), slug, Nextcloud folder path
- NC folder input shows placeholder based on current slug, links to
  Settings → Storage so the user knows where to configure Nextcloud
- Validates: disabled submit until name + slug are filled
- On success: invalidates band list cache and navigates to the new band
- Closes on backdrop click or Escape key

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 09:19:33 +02:00
Mistral Vibe
037881a821 feat(waveform): precompute and store peaks in DB for instant rendering
Store waveform peaks inline in audio_versions (JSONB columns) so WaveSurfer
can render the waveform immediately on page load without waiting for audio
decode. Adds a 100-point mini-waveform for version selector thumbnails.

Backend:
- Migration 0006: adds waveform_peaks and waveform_peaks_mini JSONB columns
- Worker generates both resolutions (500-pt full, 100-pt mini) during transcode
  and stores them directly in DB — replaces file-based waveform_url approach
- AudioVersionRead schema exposes both fields inline (no extra HTTP round-trip)
- GET /versions/{id}/waveform reads from DB; adds ?resolution=mini support

Frontend:
- audioService.initialize() accepts peaks and calls ws.load(url, Float32Array)
  so waveform renders instantly without audio decode
- useWaveform hook threads peaks option through to audioService
- PlayerPanel passes waveform_peaks from the active version to the hook
- New MiniWaveform SVG component (no WaveSurfer) renders mini peaks in the
  version selector buttons

Fix: docker-compose.dev.yml now runs alembic upgrade head before starting
the API server, so a fresh volume gets the full schema automatically.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 09:16:00 +02:00
Mistral Vibe
312f3dd161 feat(theme): replace purple accent with teal/turquoise color scheme
Swaps violet (#8b5cf6) for teal (#14b8a6/#0d9488) across all components
and updates dark backgrounds to have a green-tinted hue instead of blue-navy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 08:20:25 +02:00
Mistral Vibe
b9a83c39cd refactor(layout): replace two-pane split with single-pane navigation
Library and PlayerPanel now display one at a time on all screen sizes.
Selecting a song navigates to the player; the back button returns to
the library. Removes isMobile breakpoint logic and fixed 340px panel width.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 08:14:56 +02:00
Mistral Vibe
8ea114755a view v2 update 2026-04-10 00:34:09 +02:00
Mistral Vibe
d73377ec2f feat(ui): implement v2 three-panel layout
- Collapsible sidebar (68px icons / 230px expanded, toggle via logo)
- LibraryPanel: sessions expand inline to show tracks, search + filter chips
- PlayerPanel: extracted from SongPage, used as embeddable panel
- BandPage: Library + Player side by side; song selection via ?song= URL param
- SongPage: thin wrapper around PlayerPanel (kept for direct deep-links)
- CSS palette updated to v2 violet/cyan/emerald scheme
- Mobile (<900px): BandPage shows library or player, never both

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 23:40:37 +02:00
Mistral Vibe
d4c0e9d776 refactor(audio): Phase 2 — simplify AudioService to thin WaveSurfer wrapper
audioService.ts rewritten from ~850 lines to ~130:
- Remove custom logging system with throttle that suppressed ERROR logs
- Remove AudioContext management entirely (initializeAudioContext,
  handleAudioContextResume, setupAudioContext, shareAudioContextWithWaveSurfer,
  ensureAudioContext). WaveSurfer v7 owns its AudioContext; fighting it caused
  prod/dev divergence and silent failures.
- Replace 5-state InitializationState machine + split promise with a single
  isReady boolean set in the 'ready' event handler
- Remove retry/debounce logic from play() — these are UI concerns
- Remove dead methods: canPlayAudio (always returned true), getWaveSurferVersion,
  updatePlayerState, getAudioContextState, setLogLevel
- Extract destroyWaveSurfer() helper so cleanup is one place
- MiniPlayer now passes songId/bandId to play() (was calling with no args)
- SongPage spacebar handler simplified: just checks isReady from hook
- SongPage no longer imports audioService directly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 20:47:10 +02:00
Mistral Vibe
a0769721d6 fix: connect MiniPlayer controls and improve playback synchronization
- Connect MiniPlayer play/pause buttons to audioService
- Improve audio context management with fallback creation
- Fix state synchronization with interval-based readiness checks
- Add error handling and user feedback for playback issues
- Enhance mobile browser support with better audio context handling

Fixes playback issues in SongView where controls were not working and
state synchronization between UI and player was unreliable.
2026-04-08 15:19:30 +02:00
Mistral Vibe
b5c84ec58c WIP: Working on player 2026-04-08 15:10:52 +02:00
Mistral Vibe
d654ad5987 WIP Working on player 2026-04-08 08:12:05 +00:00
Mistral Vibe
9a09798100 Remove Player icon from mobile bottom navigation
The Player icon was removed from the BottomNavBar component since
player functionality stops when switching screens, making the navigation
item non-functional and confusing for users.

Changes:
- Removed IconPlay component
- Removed Player NavItem from BottomNavBar
- Removed isPlayer state calculation
- Updated component to only show Library, Members, and Settings icons

This improves UX by removing a non-functional navigation option.
2026-04-07 13:35:09 +00:00
Mistral Vibe
6f0e2636d0 feat(mobile): Implement responsive mobile menu with band context preservation
This commit implements a comprehensive mobile menu solution that:

1. **Mobile Menu Components**:
   - Created TopBar.tsx with circular band switcher (mobile only)
   - Enhanced BottomNavBar.tsx with band-context-aware navigation
   - Updated ResponsiveLayout.tsx to integrate TopBar for mobile views

2. **Band Context Preservation**:
   - Fixed black screen issue by preserving band context via React Router state
   - Implemented dual context detection (URL params + location state)
   - Added graceful fallback handling for missing context

3. **Visual Improvements**:
   - Changed band display from square+text to perfect circle with initials only
   - Updated dropdown items to use consistent circular format
   - Improved mobile space utilization

4. **Debugging & Testing**:
   - Added comprehensive debug logging for issue tracking
   - Created test plans and documentation
   - Ensured all static checks pass (TypeScript + ESLint)

5. **Shared Utilities**:
   - Created utils.ts with shared getInitials() function
   - Reduced code duplication across components

Key Features:
- Mobile (<768px): TopBar + BottomNavBar + Main Content
- Desktop (≥768px): Sidebar (unchanged)
- Band context preserved across all mobile navigation
- Graceful error handling and fallbacks
- Comprehensive debug logging (can be removed in production)

Files Changed:
- web/src/utils.ts (new)
- web/src/components/TopBar.tsx (new)
- web/src/components/BottomNavBar.tsx (modified)
- web/src/components/ResponsiveLayout.tsx (modified)
- web/src/components/Sidebar.tsx (modified)

Documentation Added:
- implementation_summary.md
- refinement_summary.md
- black_screen_fix_summary.md
- test_plan_mobile_menu_fix.md
- test_plan_refinement.md
- testing_guide.md
- black_screen_debug.md

Resolves:
- Mobile menu band context loss
- Black screen on Library navigation
- Inconsistent band display format
- Missing mobile band switching capability

Breaking Changes: None
Backward Compatibility: Fully maintained

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-07 13:26:33 +00:00
Mistral Vibe
21c1673fcc WIP: Mobile optimizations - responsive layout with bottom nav 2026-04-07 12:27:32 +00:00
Mistral Vibe
16bfdd2e90 Move band management into dedicated settings pages
- Add BandSettingsPage (/bands/:id/settings/:panel) with Members,
  Storage, and Band Settings panels matching the mockup design
- Strip members list, invite controls, and NC folder config from
  BandPage — library view now focuses purely on recordings workflow
- Add band-scoped nav section to AppShell sidebar (Members, Storage,
  Band Settings) with correct per-panel active states
- Fix amAdmin bug: was checking if any member is admin; now correctly
  checks if the current user holds the admin role
- Add 31 vitest tests covering BandPage cleanliness, routing, access
  control (admin vs member), and per-panel mutation behaviour
- Add test:web, test:api:unit, test:feature (post-feature pipeline),
  and ci tasks to Taskfile; frontend tests run via podman node:20-alpine
- Add README with architecture overview, setup guide, and test docs
- Add @testing-library/dom and @testing-library/jest-dom to package.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 14:55:10 +02:00
Mistral Vibe
5d7c613317 Component for invitations 2026-04-01 12:12:26 +02:00
Mistral Vibe
d61772207e Fix remaining TS6133 errors
- invites.ts: Remove unused bandId from listNonMemberUsers
- InviteManagement.tsx: Remove unused code (useEffect, queryClient, isRefreshing)

All TypeScript errors resolved!

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 11:55:38 +02:00
Mistral Vibe
1280020f83 Temporarily remove UserSearch - needs backend support
UserSearch component disabled until backend endpoint for listing non-member users is implemented.

Currently MVP includes:
- Backend APIs: list invites, revoke invites, get invite info 
- Frontend: InviteManagement component (list + revoke) 
- Note: UserSearch (admin search) disabled - needs backend support

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 11:53:30 +02:00
Mistral Vibe
2aa8ec8c59 Fix TypeScript build errors
- Remove unused imports in invites.ts
- Fix InviteManagement component (remove unused props, unneeded code)
- Fix BandPage.tsx (remove currentMemberId, remove UserSearch for now)
- Remove unused imports in types/invite.ts

Build errors resolved:
- TS6133: unused variables
- TS2304: missing variables
- TS2307: module not found

Note: UserSearch temporarily disabled - needs backend support for listing non-members

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 11:53:00 +02:00
Mistral Vibe
81c90222d5 Phase 2 frontend: Add React components for band invite management
Components created:
- InviteManagement.tsx: List pending invites, revoke functionality, copy links
- UserSearch.tsx: Search users to invite, role selection
- web/src/api/invites.ts: API wrappers for new endpoints
- web/src/types/invites.ts: TypeScript interfaces

UI enhancements:
- BandPage.tsx: Integrated new components, admin-only sections
- Members section now includes invite management for admins
- Search component for finding users to invite

Features:
- Admin can list, view, and revoke pending invites
- Copy invite links to clipboard
- Search existing users to invite (excluding current members)
- Real-time invite status (pending/expired/used)

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 11:48:14 +02:00
Mistral Vibe
d9035acdff feat: app shell with sidebar + bug fixes
UI:
- Add persistent sidebar (210px) with band switcher dropdown, Library/Player/Settings nav, user avatar row, and sign-out button
- Align design system CSS vars to CLAUDE.md spec (#0f0f12 bg, #e8a22a amber accent, rgba borders/text)
- Remove light mode toggle (no light mode in v1)
- Homepage auto-redirects to first band; shows create-band form only when no bands exist
- Strip full-page wrappers from all pages (shell owns layout)
- Remove debug console.log statements from SongPage

Bug fixes:
- nginx: trailing slash on `location ^~ /api/v1/bands/` caused 301 redirect on POST, dropping the request body — removed trailing slash
- API: _member_from_request (used by nc-scan stream) only accepted Bearer token, not httpOnly cookie — add rh_token cookie fallback
- API: internal_secret config field now has a dev default so the service starts without INTERNAL_SECRET env var set

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:43:47 +02:00