Documentation Index
Fetch the complete documentation index at: https://docs.vibesflow.ai/llms.txt
Use this file to discover all available pages before exploring further.
Vibe Market
The Vibe Market is the discovery and playback interface for all stored vibestreams. It integrates with FilCDN for content delivery and implements subscription-based access control for Metis users.Architecture Overview
FilCDN Integration
Context Provider
The Vibe Market relies on the FilCDN context for vibestream data:// FilCDN context usage in VibeMarket.tsx
const {
vibestreams,
loading,
error,
isConnected,
networkType,
currentAddress,
refreshVibestreams
} = useFilCDN();
Data Loading
// Load vibestreams when connected
useEffect(() => {
if (isConnected || vibestreams.length === 0) {
refreshVibestreams();
}
}, [isConnected]);
URL Construction
FilCDN URLs are constructed with provider wallet addresses:// FilCDN URL construction in context/filcdn.tsx
const constructFilCDNUrl = (cid, providerAddress) => {
const address = providerAddress ||
process.env.EXPO_PUBLIC_FILCDN_PROVIDER_ADDRESS;
// Use calibration network
return `https://${address}.calibration.filcdn.io/${cid}`;
};
// Chunk transformation with FilCDN support
const transformedData = data.map((vibestream) => ({
...vibestream,
chunks_detail: vibestream.chunks_detail?.map((chunk) => {
if (!chunk.cid) return chunk;
const filcdnUrl = constructFilCDNUrl(chunk.cid);
return {
...chunk,
url: `${backendUrl}/api/proxy/${chunk.cid}`, // Backend fallback
filcdn_url: filcdnUrl, // Direct FilCDN URL
};
}) || []
}));
Subscription System
Network-Based Access Control
// Subscription status checking in VibeMarket.tsx
useEffect(() => {
const checkSubscriptionStatus = async () => {
if (!connected || !account) {
setSubscriptionStatus({
isSubscribed: false,
isLoading: false,
requiresSubscription: false
});
return;
}
const networkInfo = getNetworkInfo();
// Only check subscription for Metis Hyperion network
if (networkInfo?.type === 'metis-hyperion') {
setSubscriptionStatus(prev => ({
...prev,
isLoading: true,
requiresSubscription: true
}));
try {
const isSubscribed = await isUserSubscribed();
setSubscriptionStatus({
isSubscribed,
isLoading: false,
requiresSubscription: true
});
} catch (error) {
console.error('❌ Failed to check subscription status:', error);
setSubscriptionStatus({
isSubscribed: false,
isLoading: false,
requiresSubscription: true
});
}
} else {
// For non-Metis networks, no subscription required
setSubscriptionStatus({
isSubscribed: true, // Allow access
isLoading: false,
requiresSubscription: false
});
}
};
checkSubscriptionStatus();
}, [connected, account, isUserSubscribed, getNetworkInfo]);
Subscription Gate UI
// Subscription gate for Metis users
{subscriptionStatus.requiresSubscription && !subscriptionStatus.isSubscribed && (
<GlitchContainer intensity="medium" style={styles.subscriptionGate}>
<FontAwesome5 name="lock" size={32} color={COLORS.accent} />
<Text style={styles.gateTitle}>PREMIUM ACCESS REQUIRED</Text>
<Text style={styles.gateSubtext}>
Access all DJ sets ever created on Metis Hyperion for just 10 tMETIS per month!
</Text>
<TouchableOpacity
style={styles.subscribeButton}
onPress={() => setSubscribeModalVisible(true)}
>
<Text style={styles.subscribeButtonText}>SUBSCRIBE NOW</Text>
</TouchableOpacity>
</GlitchContainer>
)}
Preview System
Audio Streaming Service
// Preview playback using AudioStreamer service
const togglePreview = useCallback(async (stream) => {
try {
if (player.currentRTA === stream.rta_id && player.isPlaying) {
// Stop current preview
if (player.stopPreview) {
player.stopPreview();
}
setPlayer({
isPlaying: false,
currentRTA: null,
stopPreview: null
});
return;
}
// Stop any existing preview
if (player.stopPreview) {
player.stopPreview();
}
// Get first chunk for preview
const firstChunk = stream.chunks_detail?.[0];
if (!firstChunk) {
Alert.alert('No Audio', 'No chunks available for preview');
return;
}
console.log('🎵 Starting preview for:', stream.rta_id);
if (Platform.OS === 'web' && audioStreamer.current) {
// Use FilCDN URL if available, fallback to proxy
const audioUrl = firstChunk.filcdn_url || firstChunk.url;
// Start preview using AudioStreamer service
const stopPreview = await audioStreamer.current.playPreview(audioUrl, 0.6);
setPlayer({
isPlaying: true,
currentRTA: stream.rta_id,
stopPreview
});
// Auto-stop after 30 seconds
setTimeout(() => {
if (playerRef.current.currentRTA === stream.rta_id && playerRef.current.isPlaying) {
stopPreview();
setPlayer(prev => ({
...prev,
isPlaying: false,
currentRTA: null,
stopPreview: null
}));
}
}, 30000);
}
} catch (error) {
console.error('❌ Preview toggle failed:', error);
Alert.alert('Error', 'Failed to toggle preview');
}
}, [player]);
Preview Controls
// Preview button in vibestream cards
<TouchableOpacity
style={styles.previewButton}
onPress={(e) => {
e.stopPropagation();
togglePreview(stream);
}}
>
<FontAwesome5
name={isPlaying ? "stop" : "play"}
size={12}
color={COLORS.background}
style={!isPlaying ? { marginLeft: 1 } : {}}
/>
</TouchableOpacity>
Profile Integration
Creator Profile Loading
// Profile preloading in VibeMarket.tsx
useEffect(() => {
const loadCreatorProfiles = async () => {
if (vibestreams.length === 0) return;
const uniqueCreators = [...new Set(vibestreams.map(stream => stream.creator))];
// Use ProfileLoader's efficient preloading
await profileLoader.current.preloadProfiles(uniqueCreators);
};
loadCreatorProfiles();
}, [vibestreams]);
Display Name Resolution
// Creator display name helper using ProfileLoader cache
const getDisplayName = useCallback((creator) => {
// ProfileLoader handles caching internally
const cached = profileLoader.current.getDisplayName(creator);
if (cached) return cached;
// Fallback formatting
if (creator.startsWith('0x')) {
return `${creator.slice(0, 5)}...${creator.slice(-6)}`;
}
return creator;
}, []);
Data Processing Utilities
Network Detection
// Network detection from RTA ID
const getNetworkFromRtaId = (rtaId) => {
const upperRtaId = rtaId.toUpperCase();
if (upperRtaId.startsWith('METIS_') || upperRtaId.includes('METIS')) {
return 'metis';
}
return 'near';
};
Title Extraction
// Clean title extraction from RTA ID
const getVibestreamTitle = (stream) => {
const rtaId = stream.rta_id;
if (rtaId.toUpperCase().startsWith('METIS_VIBE_')) {
return rtaId.substring(11);
} else if (rtaId.toUpperCase().startsWith('METIS_')) {
return rtaId.substring(6);
} else if (rtaId.toUpperCase().startsWith('RTA_ID_')) {
return rtaId.substring(7);
} else if (rtaId.toUpperCase().startsWith('RTA_')) {
return rtaId.substring(4);
}
return rtaId.toUpperCase();
};
Date Formatting
// Timestamp formatting with fallbacks
const formatDate = (timestamp) => {
let date;
if (timestamp > 1e12) {
date = new Date(timestamp);
} else if (timestamp > 1e9) {
date = new Date(timestamp * 1000);
} else {
date = new Date();
}
const options = {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: false
};
return date.toLocaleDateString('en-US', options).toUpperCase();
};
Filtering System
Filter State
// Filter state management
const [filters, setFilters] = useState({
network: 'all',
creator: '',
date: 'all'
});
Filter Implementation
// Comprehensive filtering logic
const filteredVibestreams = vibestreams.filter(stream => {
// Network filter
if (filters.network !== 'all') {
const streamNetwork = getNetworkFromRtaId(stream.rta_id);
if (streamNetwork !== filters.network) return false;
}
// Creator filter
if (filters.creator && !stream.creator.toLowerCase().includes(filters.creator.toLowerCase())) {
return false;
}
// Date filter
if (filters.date !== 'all') {
const timestamp = stream.upload_timestamp;
let streamDate;
if (timestamp > 1e12) {
streamDate = new Date(timestamp);
} else if (timestamp > 1e9) {
streamDate = new Date(timestamp * 1000);
} else {
return true;
}
const now = new Date();
const diffMs = now.getTime() - streamDate.getTime();
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
switch (filters.date) {
case 'today':
if (diffDays > 0) return false;
break;
case 'week':
if (diffDays > 7) return false;
break;
case 'month':
if (diffDays > 30) return false;
break;
}
}
return true;
});
Filter UI
// Filter chips interface
<View style={styles.filtersContainer}>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
{/* Network filter */}
<TouchableOpacity
style={[styles.filterChip, filters.network !== 'all' && styles.activeFilterChip]}
onPress={() => setFilters(prev => ({
...prev,
network: prev.network === 'all' ? 'metis' : prev.network === 'metis' ? 'near' : 'all'
}))}
>
<Text style={[styles.filterText, filters.network !== 'all' && styles.activeFilterText]}>
{filters.network === 'all' ? 'ALL NETWORKS' : filters.network.toUpperCase()}
</Text>
</TouchableOpacity>
{/* Date filter */}
<TouchableOpacity
style={[styles.filterChip, filters.date !== 'all' && styles.activeFilterChip]}
onPress={() => setFilters(prev => ({
...prev,
date: prev.date === 'all' ? 'today' : prev.date === 'today' ? 'week' : prev.date === 'week' ? 'month' : 'all'
}))}
>
<Text style={[styles.filterText, filters.date !== 'all' && styles.activeFilterText]}>
{filters.date === 'all' ? 'ALL TIME' : filters.date.toUpperCase()}
</Text>
</TouchableOpacity>
</ScrollView>
</View>
Card Rendering
Vibestream Card
// Minimal vibestream card renderer
const renderVibestreamCard = (stream, index) => {
const isPlaying = player.currentRTA === stream.rta_id && player.isPlaying;
const network = getNetworkFromRtaId(stream.rta_id);
const title = getVibestreamTitle(stream);
const displayName = getDisplayName(stream.creator);
return (
<GlitchContainer key={stream.rta_id} intensity="low" style={styles.cardContainer}>
<TouchableOpacity
style={styles.card}
onPress={() => onOpenPlayback?.(stream.rta_id)}
>
{/* Network indicator */}
<View style={[styles.networkIndicator, {
backgroundColor: network === 'metis' ? COLORS.accent : COLORS.secondary
}]} />
{/* Main content */}
<View style={styles.cardContent}>
<View style={styles.cardHeader}>
<GlitchText text={title} style={styles.cardTitle} />
<TouchableOpacity
style={styles.previewButton}
onPress={(e) => {
e.stopPropagation();
togglePreview(stream);
}}
>
<FontAwesome5
name={isPlaying ? "stop" : "play"}
size={12}
color={COLORS.background}
/>
</TouchableOpacity>
</View>
<Text style={styles.creatorName}>{displayName}</Text>
<View style={styles.cardMeta}>
<Text style={styles.metaText}>{stream.chunks} CHUNKS</Text>
<Text style={styles.metaText}>{stream.rta_duration}</Text>
<Text style={styles.metaText}>{formatDate(stream.upload_timestamp)}</Text>
</View>
</View>
</TouchableOpacity>
</GlitchContainer>
);
};
Error States
Connection Warning
// Connection warning display
{!isConnected && (
<GlitchContainer intensity="medium" style={styles.warningContainer}>
<FontAwesome5 name="exclamation-triangle" size={24} color={COLORS.accent} />
<Text style={styles.warningText}>WALLET NOT CONNECTED</Text>
<Text style={styles.warningSubtext}>
Connect your wallet to access all features
</Text>
</GlitchContainer>
)}
Empty States
// Empty state when no vibestreams found
{filteredVibestreams.length === 0 ? (
<View style={styles.emptyState}>
<FontAwesome5 name="satellite-dish" size={32} color={COLORS.textTertiary} />
<Text style={styles.emptyText}>NO SIGNALS DETECTED</Text>
<Text style={styles.emptySubtext}>Adjust filters or check back later</Text>
</View>
) : (
<View style={styles.cardsList}>
{filteredVibestreams.map((stream, index) => renderVibestreamCard(stream, index))}
</View>
)}
Performance Optimizations
Cleanup Management
// Cleanup audio when component unmounts
useEffect(() => {
return () => {
if (player.stopPreview) {
player.stopPreview();
}
audioStreamer.current?.dispose();
};
}, [player.stopPreview]);
Subscription Success Handling
// Handle subscription success callback
const handleSubscriptionSuccess = useCallback(() => {
setSubscriptionStatus({
isSubscribed: true,
isLoading: false,
requiresSubscription: true
});
setSubscribeModalVisible(false);
}, []);
Next Steps
User Profiles
Learn about profile management
Playback System
Understand continuous playback