Claude-skill-registry-data mixmi-user-flows
Step-by-step user journeys through the mixmi platform including onboarding, upload, remix creation, purchasing, and discovery
git clone https://github.com/majiayu000/claude-skill-registry-data
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry-data "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/mixmi-user-flows" ~/.claude/skills/majiayu000-claude-skill-registry-data-mixmi-user-flows && rm -rf "$T"
data/mixmi-user-flows/SKILL.mdmixmi User Flows
Complete step-by-step journeys showing how users interact with the mixmi platform.
Table of Contents
- Flow 1: Alpha User Onboarding
- Flow 2: Quick Upload (Minimal)
- Flow 3: Advanced Upload (Full Metadata)
- Flow 4: Creating a Remix (Planned)
- Flow 5: Purchasing Content
- Flow 6: Discovery via Globe
- Flow 7: Discovery via Search
- Flow 8: Browsing Creator Stores
- Flow 9: Exploring Artist Profiles
- Flow 10: Editing Your Profile
Flow 1: Alpha User Onboarding
Goal: Authenticate and gain upload access Duration: 1-2 minutes Authentication Methods: Stacks Wallet OR Alpha Invite Code
Path A: Stacks Wallet Authentication (Preferred)
Step 1: Navigate to Platform
URL:
https://mixmi-alpha.vercel.app/ (or localhost)
Page: Globe homepage (app/page.tsx)
State: User not authenticated
UI:
- Header shows "Sign In" button (top-right)
- Globe visible with existing content
- No upload button visible
Step 2: Click "Sign In"
Action: User clicks "Sign In" in header Component:
Header.tsx → opens SignInModal.tsx
Modal Displays:
- "Welcome to mixmi Alpha" header
- Two options:
- "Connect Wallet" button (cyan, primary)
- "Use Alpha Code" link (secondary, text link below)
Step 3: Click "Connect Wallet"
Action: User clicks "Connect Wallet" Library:
@stacks/connect (Stacks blockchain wallet integration)
What Happens:
import { showConnect } from '@stacks/connect'; showConnect({ appDetails: { name: 'mixmi Alpha', icon: window.location.origin + '/logo.png' }, onFinish: (data) => { // Wallet connection successful const walletAddress = data.userSession.loadUserData().profile.stxAddress.mainnet; // Store in AuthContext }, onCancel: () => { // User closed wallet popup } });
Wallet Popup Appears:
- Shows available wallets (Hiro, Xverse, Leather, etc.)
- User selects their wallet
- Wallet asks to approve connection
- User signs authentication message
Step 4: Alpha Whitelist Verification
Backend Call:
const response = await fetch('/api/auth/alpha-check', { method: 'POST', body: JSON.stringify({ walletAddress }) }); const { isApproved, alphaCode } = await response.json();
Database Query:
SELECT alpha_code, is_active FROM alpha_users WHERE wallet_address = 'SP...' AND is_active = true;
Two Outcomes:
✅ If Approved:
setsAuthContextisAuthenticated = true
stored in contextwalletAddress- Modal closes automatically
- Header now shows:
- "Upload" button appears
- Wallet address (truncated): "SP1ABC...XYZ"
- Cart icon
❌ If Not Approved:
- Error message: "Wallet not approved for alpha access"
- Option to request access (future)
- User must use alpha code instead
Path B: Alpha Code Authentication (Fallback)
Step 1-2: Same as Path A
Step 3: Click "Use Alpha Code"
UI Changes:
- "Connect Wallet" button dims/hides
- Text input appears: "Enter your alpha invite code"
- Placeholder: "mixmi-ABC123"
- "Submit" button
Step 4: Enter Alpha Code
User Types:
mixmi-ABC123
Action: Clicks "Submit"
Validation (Frontend):
const isAlphaCode = (code) => { return /^mixmi-[A-Z0-9]{6}$/.test(code); };
Backend Conversion:
// User entered: mixmi-ABC123 // Backend resolves to actual wallet const response = await fetch('/api/auth/resolve-wallet', { method: 'POST', body: JSON.stringify({ authIdentity: 'mixmi-ABC123' }) }); const { walletAddress } = await response.json(); // Returns: SP1ABC...XYZ
Why This Works:
- UI never shows "wallet address" (prevents security scanner warnings)
- Backend transparently converts alpha code → wallet
- Blockchain operations use real wallet address
- User-friendly authentication
Step 5: Authenticated State
AuthContext State:
{ isAuthenticated: true, walletAddress: 'SP1ABC...', alphaCode: 'mixmi-ABC123', // For display authMethod: 'alpha_code' // or 'wallet' }
UI Changes:
- Modal closes
- Header shows upload button
- User can now upload content
First Upload Incentive
After Authentication:
- Toast notification: "You're in! Ready to upload your first track?"
- Welcome page: "Sign In and Upload" button now says "Upload Your First Track"
Flow 2: Quick Upload (Minimal)
Goal: Upload a loop or song in ~60 seconds Prerequisites: User authenticated Duration: 1-2 minutes
Step 1: Open Upload Modal
Triggers:
- Click "Upload" in header
- Click "Sign In and Upload" on Welcome page (if authenticated)
- Click "+" in own store
Component:
IPTrackModal opens
Mode: Quick Upload (default, toggle enabled)
Step 2: Select Content Type
UI: 5 large buttons with icons
┌─────────┬─────────┬─────────┬─────────┬─────────┐ │ 🎵 Song │ 🔁 Loop │📦 Pack │ 💿 EP │ 🎚️ Mix │ └─────────┴─────────┴─────────┴─────────┴─────────┘
User Clicks: "🔁 Loop"
State Change:
setFormData({ ...formData, content_type: 'loop' });
UI Updates:
- BPM field becomes required (red asterisk)
- Loop category dropdown appears
Step 3: Upload Audio File
UI: File picker or drag zone (drag-and-drop for audio not yet implemented)
User Actions:
- Clicks "Choose Audio File"
- Selects
(8MB)trap-beat-140.wav
File Validation:
// Check format const validFormats = ['audio/wav', 'audio/mpeg', 'audio/mp4', 'audio/flac']; if (!validFormats.includes(file.type)) { throw new Error('Invalid audio format'); } // Check size if (file.size > 10 * 1024 * 1024) { throw new Error('File too large (max 10MB)'); }
Auto-Detection Starts:
// BPM detection setIsDetectingBPM(true); const detectedBPM = await detectBPM(file); // Result: 140 BPM // Duration detection const duration = await detectDuration(file); // Result: 32 seconds
UI Updates:
- Progress bar shows during detection
- "BPM: 140 (auto-detected)" appears
- User MUST verify/override this value
Step 4: Fill Basic Metadata
Required Fields (Quick Mode):
Title: [Trap Beat 140 ] * Artist: [DJ Example ] * BPM: [140 ] * (pre-filled, editable) Loop Category: [▼ Beats ] * (dropdown) Location: [Los Angeles, CA ] * (autocomplete)
BPM Verification:
⚠️ User clicks BPM field Modal: "Auto-detected 140 BPM. Is this correct?" [Yes, 140 is correct] [No, let me change it] If user changes to 142: ✓ BPM updated to 142 (whole number only)
Location Autocomplete:
User types: "Los" Suggestions appear: ┌──────────────────────────┐ │ 🏙️ Los Angeles, CA, USA │ │ 🏙️ Los Alamos, NM, USA │ │ 🏙️ Louisiana, USA │ │ 🏔️ Louisiana Nation │ └──────────────────────────┘ User selects: Los Angeles, CA, USA
Coordinates Stored:
{ location_name: "Los Angeles, CA, USA", location_lat: 34.052234, location_lng: -118.243685 }
Step 5: Upload Cover Image
UI: File picker or drag zone
Auto-Generation Option:
┌────────────────────────────┐ │ 📷 Upload Cover Image │ │ │ │ [Choose File] │ │ or │ │ [🎨 Auto-Generate] │ └────────────────────────────┘
If Auto-Generate:
// Creates visual from audio waveform const generateCover = async (audioFile) => { const waveformCanvas = await createWaveformCanvas(audioFile); const blob = await canvasToBlob(waveformCanvas); return blob; };
Manual Upload Validation:
// Check image dimensions const img = new Image(); img.src = URL.createObjectURL(file); if (img.width < 500 || img.height < 500) { alert('Image too small (min 500x500)'); } if (img.width !== img.height) { alert('Image should be square'); }
Step 6: Review and Submit
Pre-Submit Summary:
┌──────────────────────────────┐ │ Ready to Upload? │ ├──────────────────────────────┤ │ Type: Loop │ │ Title: Trap Beat 140 │ │ Artist: DJ Example │ │ BPM: 140 │ │ Location: Los Angeles, CA │ │ Files: ✅ Audio ✅ Cover │ │ │ │ [← Back] [Upload Track →] │ └──────────────────────────────┘
Upload Process:
// 1. Upload audio to Supabase Storage const audioPath = `${walletAddress}/${Date.now()}_audio.wav`; await supabase.storage .from('user-content') .upload(audioPath, audioFile); // 2. Upload cover image const imagePath = `${walletAddress}/${Date.now()}_cover.jpg`; await supabase.storage .from('user-content') .upload(imagePath, imageFile); // 3. Create database record const { data: track } = await supabase .from('ip_tracks') .insert({ title: formData.title, artist: formData.artist, content_type: 'loop', bpm: formData.bpm, audio_url: audioPath, cover_image_url: imagePath, location_name: formData.location_name, location_lat: formData.location_lat, location_lng: formData.location_lng, primary_uploader_wallet: walletAddress, created_at: new Date().toISOString() }) .select() .single(); // 4. Success! return track;
Step 7: Success State
Modal Updates:
┌──────────────────────────────┐ │ ✅ Upload Complete! │ ├──────────────────────────────┤ │ │ │ Your track "Trap Beat 140" │ │ is now live on mixmi! │ │ │ │ [View Track] [Upload Another]│ └──────────────────────────────┘
Actions:
- "View Track" → Opens TrackDetailsModal
- "Upload Another" → Resets form
- Close modal → Returns to previous page
Flow 3: Advanced Upload (Full Metadata)
Goal: Upload with complete IP attribution Prerequisites: User authenticated Duration: 3-5 minutes
Toggle to Advanced Mode
Location: Top of IPTrackModal
Quick Upload ○━━━━━● Advanced Upload ↑ User slides toggle
What Changes:
- Additional metadata fields appear
- IP splitting interface shows
- Referrer section enabled
- Full credits section available
Additional Fields in Advanced Mode
IP Splitting Configuration
UI:
┌──────────────────────────────┐ │ IP Splits │ ├──────────────────────────────┤ │ Producer: [You ] [60%] │ │ + Add Collaborator │ │ │ │ Platform Fee: 15% │ │ Available: 25% │ │ │ │ [+ Add Split Recipient] │ └──────────────────────────────┘
Adding Collaborator:
// User clicks "+ Add Collaborator" // New row appears: { role: "Vocalist", // Dropdown wallet: "SP2ABC...", // Text input percentage: 25 // Number input } // Validation if (totalPercentage > 85) { error: "Total cannot exceed 85% (15% platform fee)" }
Credits Section
Fields:
Credits (Optional) ├─ Vocalist: [Jane Doe ] ├─ Mixing Engineer: [John Mix ] ├─ Mastering: [Master Studios ] └─ + Add Credit Field
Referrer Attribution
Field:
Referred By (Optional) [SP2DEF...] or [mixmi-XYZ789] Help: Who told you about mixmi?
Validation:
// Check if valid wallet or alpha code const isValidReferrer = (input) => { return isWalletAddress(input) || isAlphaCode(input); };
Complete Upload Flow
All steps from Quick Upload, plus:
- IP splits configuration
- Credits attribution
- Referrer tracking
- Extended metadata
Flow 4: Creating a Remix (Planned)
Goal: Create and save a remix using the mixer Prerequisites: Authenticated, tracks loaded in mixer Status: Coming in Generation 1
Planned Implementation
Step 1: Load Tracks to Mixer
- Drag loops from Crate to Deck A/B
- Or click "Load to Mixer" from track card
Step 2: Create Mix
- Adjust BPM, apply FX
- Record live mixing session
- Mix auto-saves as WebM/Opus
Step 3: Save Recording
Planned Modal:
┌──────────────────────────────┐ │ Save Your Remix │ ├──────────────────────────────┤ │ Title: [Summer Mix 2025 ] │ │ Duration: 3:42 │ │ │ │ Source Tracks: │ │ • Loop 1 by Artist A (30%) │ │ • Loop 2 by Artist B (30%) │ │ Platform Fee: 15% │ │ Your Share: 25% │ │ │ │ [Cancel] [Save & Upload] │ └──────────────────────────────┘
Step 4: Auto-Calculate IP Splits
// Automatic attribution based on source loops const calculateRemixSplits = (sourceTracks) => { const remixerShare = 25; // Fixed for remixer const platformFee = 15; // Fixed platform fee const sourceShare = 60; // Divided among sources const perSource = sourceShare / sourceTracks.length; return sourceTracks.map(track => ({ recipient: track.primary_uploader_wallet, percentage: perSource, role: 'Original Creator' })); };
Flow 5: Purchasing Content
Goal: Purchase and download a track Prerequisites: None (can purchase without auth) Duration: 30 seconds - 2 minutes
Step 1: Find Track to Purchase
Entry Points:
- Globe page card hover → price badge
- Store page → track cards
- Search results → price badges
- Track details modal → purchase button
Step 2: Click Price Badge
UI Example:
Track card hover state: ┌─────────────┐ │ Cover │ │ Image │ │ [$2.99 STX] │ ← Click this └─────────────┘
Step 3: Add to Cart
Action: Click price badge Result: Track added to cart
Cart Updates:
- Cart icon shows item count: 🛒 (1)
- Toast notification: "Added to cart!"
- Price badge changes to "✓ In Cart"
Step 4: Open Cart
Click: Cart icon in header Modal: Shopping cart opens
┌──────────────────────────────┐ │ Shopping Cart [×] │ ├──────────────────────────────┤ │ │ │ 1. Trap Beat 140 │ │ by DJ Example │ │ Type: Loop │ │ $2.99 STX [🗑️] │ │ │ │ ───────────────────────── │ │ Total: $2.99 STX │ │ │ │ [Continue Shopping] │ │ [Proceed to Checkout →] │ └──────────────────────────────┘
Step 5: Checkout Process
If Not Authenticated:
┌──────────────────────────────┐ │ Sign In to Purchase │ ├──────────────────────────────┤ │ Connect your wallet to │ │ complete this purchase │ │ │ │ [Connect Wallet] │ │ [Use Alpha Code] │ └──────────────────────────────┘
If Authenticated:
┌──────────────────────────────┐ │ Confirm Purchase │ ├──────────────────────────────┤ │ Track: Trap Beat 140 │ │ Price: $2.99 STX │ │ From: Your Wallet │ │ To: Creator Wallet │ │ │ │ IP Splits: │ │ • Creator: 60% ($1.79) │ │ • Platform: 15% ($0.45) │ │ • Referrer: 10% ($0.30) │ │ • Other: 15% ($0.45) │ │ │ │ [Cancel] [Confirm Purchase] │ └──────────────────────────────┘
Step 6: Blockchain Transaction
Process:
// 1. Initialize Stacks transaction const transaction = { contractAddress: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', contractName: 'mixmi-payments', functionName: 'purchase-track', functionArgs: [ trackId, priceInMicroSTX, ipSplits ] }; // 2. User approves in wallet await openContractCall(transaction); // 3. Wait for confirmation // Shows loading state: "Processing payment..." // 4. Transaction confirmed
Step 7: Download Access
Success Modal:
┌──────────────────────────────┐ │ ✅ Purchase Complete! │ ├──────────────────────────────┤ │ │ │ Thank you for supporting │ │ DJ Example! │ │ │ │ [Download Track] │ │ [View in Library] │ │ [Continue Shopping] │ └──────────────────────────────┘
Download Process:
// Generate temporary download URL const { data: downloadUrl } = await supabase .from('purchases') .insert({ buyer_wallet: walletAddress, track_id: track.id, transaction_hash: txHash, purchased_at: new Date() }) .select('download_token') .single(); // Trigger download window.open(`/api/download/${downloadUrl.download_token}`);
Flow 6: Discovery via Globe
Goal: Discover new music through map interface Prerequisites: None Duration: Ongoing browsing
Globe Interaction
Default View:
- 3D rotating globe
- Glowing dots for track locations
- Auto-rotation at 0.001 radians/frame
Hover on Location Dot
Visual Feedback:
- Dot grows (1.5x scale)
- Glow intensifies
- Tooltip appears with city name
Click on Location
Action: Click glowing dot Result: Track cards appear
┌─────────────────────────────┐ │ Los Angeles, CA │ │ 12 tracks │ ├─────────────────────────────┤ │ [Track] [Track] [Track] │ │ [Track] [Track] [Track] │ │ Load More... │ └─────────────────────────────┘
Track Card Interactions
Hover State:
- Card scales to 1.05x
- Overlay with track details
- Action buttons appear
Available Actions:
- Play preview (▶️)
- Add to cart ($)
- View details (ℹ️)
- Load to mixer (🎛️) - if loop
Flow 7: Discovery via Search
Goal: Find specific tracks or artists Prerequisites: None Duration: 10-30 seconds
Access Search
Trigger: Click search icon in header Modal: Search interface opens
Enter Search Query
Search Input:
┌──────────────────────────────┐ │ 🔍 Search mixmi │ ├──────────────────────────────┤ │ [trap beats 140 bpm_____] │ │ │ │ Filter by: │ │ ○ All ● Tracks ○ Artists │ │ ○ Loops ○ Songs │ └──────────────────────────────┘
Real-time Results
As User Types:
// Debounced search (300ms) const debouncedSearch = debounce(async (query) => { const { data: results } = await supabase .from('ip_tracks') .select('*') .or(`title.ilike.%${query}%,artist.ilike.%${query}%`) .limit(20); setSearchResults(results); }, 300);
Results Display
Search Results:
┌──────────────────────────────┐ │ Results for "trap beats" │ ├──────────────────────────────┤ │ │ │ Tracks (8) │ │ ├─ Trap Beat 140 │ │ │ by DJ Example • Loop │ │ ├─ Dark Trap 150 │ │ │ by Producer X • Loop │ │ └─ ... │ │ │ │ Artists (3) │ │ ├─ Trap Lord │ │ ├─ Trap Queen │ │ └─ ... │ └──────────────────────────────┘
Select Result
Click Action:
- Track → Opens TrackDetailsModal
- Artist → Navigate to artist profile
Flow 8: Browsing Creator Stores
Goal: Explore a creator's catalog Prerequisites: None Duration: 2-5 minutes browsing
Navigate to Store
Entry Points:
- Click artist name on track card
- Click "View Store" in profile
- URL:
/store/[identifier]
Store Layout
Header Section:
┌──────────────────────────────┐ │ DJ Example's Store │ │ 12 tracks • Member since Oct│ │ │ │ [Follow] [Share] [Tip] │ └──────────────────────────────┘
Track Grid:
Filters: [All] [Loops] [Songs] [Packs] Sort: [Newest ▼] ┌────┬────┬────┬────┐ │Loop│Loop│Song│Pack│ ├────┼────┼────┼────┤ │Loop│EP │Loop│Song│ ├────┼────┼────┼────┤ │... │... │... │... │ └────┴────┴────┴────┘
Filter and Sort
Filter Options:
const filters = { all: null, loops: "content_type = 'loop'", songs: "content_type = 'full_song'", packs: "content_type IN ('loop_pack', 'ep')" };
Sort Options:
- Newest First (default)
- Oldest First
- Price: Low to High
- Price: High to Low
- Most Downloaded (future)
Flow 9: Exploring Artist Profiles
Goal: Learn about an artist Prerequisites: None Duration: 1-2 minutes
Navigate to Profile
Entry Points:
- Click artist avatar
- Click artist name (when underlined)
- URL:
/profile/[identifier]
Profile Sections
Header:
┌──────────────────────────────┐ │ [Avatar] │ │ DJ Example │ │ "Making beats daily" │ │ │ │ 📍 Los Angeles, CA │ │ 🎵 12 tracks │ │ 👥 234 followers (future) │ └──────────────────────────────┘
Bio Section:
About ───── Producer and DJ from LA, specializing in trap and hip-hop beats. Available for custom work and collaborations. [Instagram] [SoundCloud] [Twitter]
Recent Tracks:
Recent Uploads ────────────── [Track] [Track] [Track] [Track] View All →
Social Actions (Future)
Planned Features:
- Follow artist
- Send message
- Book for collaboration
- Tip directly (STX/BTC)
Flow 10: Editing Your Profile
Goal: Update profile information Prerequisites: Authenticated Duration: 2-3 minutes
Navigate to Own Profile
Access:
- Click avatar in header → "My Profile"
- URL:
redirects to/profile/me/profile/[wallet]
Edit Profile Button
Location: Top-right of profile when viewing own Button: "Edit Profile" (pencil icon)
Profile Edit Modal
Sections:
Basic Information
Display Name* [DJ Example_______________] Tagline (40 chars) [Making beats daily_______] Bio (350 chars) [Producer and DJ from LA, specializing in trap and hip-hop beats...] [Save Changes]
Avatar Upload
Profile Image ┌──────────┐ │ Current │ [Change Photo] │ Avatar │ └──────────┘ Drag & drop or choose file Max 5MB • Square recommended
Social Links (Future)
Social Links ├─ Instagram: [@djexample____] ├─ SoundCloud: [/djexample___] ├─ Twitter: [@djexample_____] └─ + Add Link
Save Changes
Process:
// Update profile in database await supabase .from('user_profiles') .upsert({ wallet_address: walletAddress, display_name: formData.name, tagline: formData.tagline, bio: formData.bio, avatar_url: uploadedImageUrl, social_links: formData.socialLinks, updated_at: new Date() }); // Update local ProfileContext updateProfile(newProfileData); // Close modal onClose();
Success:
- Changes reflect immediately
- No page refresh needed
- Toast: "Profile updated!"
Related Skills
- mixmi-component-library - UI components used in flows
- mixmi-design-patterns - Visual patterns for extending flows
- mixmi-payment-flow - Detailed payment implementation
- mixmi-database-schema - Data structures behind flows