// social.jsx — Messages inbox, DM thread, member profile with follow/message const { useState: useStateS, useEffect: useEffectS, useRef: useRefS } = React; const { Card, Chip, CoverArt, Waveform, TopBar, IconBtn, DisplayHeading, Icon, Avatar } = window.LAUNCH_SCREENS_1; // ───────────────────────────────────────────────────────────── // Member directory — referenced from Feed + Messages // ───────────────────────────────────────────────────────────── const MEMBERS = { 'kai.park': { handle: 'kai.park', name: 'Kai Park', lbl: 'KP', color: '#C72820', pal: ['#C72820', '#0A0A0A'], bio: 'producer / engineer. brooklyn. dark synth, neon kicks. open to collabs.', tier: 'ORBIT', city: 'BROOKLYN', genres: ['ALT POP', 'SYNTH'], followers: 1240, following: 89, releases: 6, streak: 21, online: true, }, 'sasha.vee': { handle: 'sasha.vee', name: 'Sasha Vee', lbl: 'SV', color: '#CAFF33', pal: ['#CAFF33', '#7A5AE0'], bio: 'songwriter / lead vox. los angeles. just got on fresh finds ☁️', tier: 'STRATOSPHERE', city: 'LA', genres: ['SOUL', 'R&B'], followers: 8420, following: 142, releases: 11, streak: 64, online: true, }, 'theo.m': { handle: 'theo.m', name: 'Theo Marlow', lbl: 'TM', color: '#7A5AE0', pal: ['#7A5AE0', '#FBBF24'], bio: 'r&b producer & topliner. atlanta. low end specialist.', tier: 'ORBIT', city: 'ATL', genres: ['R&B', 'HIP HOP'], followers: 612, following: 203, releases: 3, streak: 8, online: false, }, 'isla.loop': { handle: 'isla.loop', name: 'Isla Loop', lbl: 'IL', color: '#FBBF24', pal: ['#FBBF24', '#7A5AE0'], bio: 'ambient / electronic. lisbon. sound design nerd. always demoing.', tier: 'LAUNCH PAD', city: 'LISBON', genres: ['AMBIENT', 'ELECTRONIC'], followers: 327, following: 88, releases: 4, streak: 15, online: false, }, }; // Conversations — sample data const THREADS = [ { id: 'kai.park', member: 'kai.park', unread: 2, lastTime: '12:42 PM', messages: [ { from: 'them', t: 'yo that mix from your assignment is FIRE', ts: '11:30 AM' }, { from: 'them', t: 'the way you sat the 808 in there 🤯', ts: '11:30 AM' }, { from: 'me', t: 'thank you bro!! took me forever lol', ts: '11:45 AM' }, { from: 'me', t: 'noah\'s lesson on bus comp changed my life fr', ts: '11:45 AM' }, { from: 'them', t: 'fr fr. yo i\'m working on a remix — would u send me your vocal stems?', ts: '12:38 PM' }, { from: 'them', t: 'i\'ll cut you in on splits ofc', ts: '12:42 PM', kind: 'voice' }, ], }, { id: 'sasha.vee', member: 'sasha.vee', unread: 0, lastTime: 'YESTERDAY', messages: [ { from: 'them', t: 'congrats on the streak 🔥', ts: 'yesterday' }, { from: 'me', t: 'thank u!! you\'re a huge inspo', ts: 'yesterday' }, ], }, { id: 'theo.m', member: 'theo.m', unread: 1, lastTime: 'TUE', messages: [ { from: 'them', t: 'pull up on the discord later? im sharing a vocal chain preset', ts: 'tue' }, ], }, { id: 'isla.loop', member: 'isla.loop', unread: 0, lastTime: 'MAR 28', messages: [ { from: 'them', t: 'thx for the feedback on low tide 🙏', ts: 'mar 28' }, ], }, ]; // Suggested-to-follow const SUGGESTIONS = ['theo.m', 'isla.loop']; // ───────────────────────────────────────────────────────────── // FollowButton — used on Feed + Member profile // ───────────────────────────────────────────────────────────── function FollowButton({ theme, following, onToggle, size = 'sm' }) { return ( ); } // ───────────────────────────────────────────────────────────── // MessagesScreen — inbox // ───────────────────────────────────────────────────────────── function MessagesScreen({ theme, onBack, onOpenThread, onOpenMember, threads, following, onToggleFollow, members = MEMBERS }) { const [tab, setTab] = useStateS('inbox'); // 'inbox' | 'requests' const memberOr = (h) => members[h] || { handle: h, name: h, lbl: (h || '?').slice(0, 2).toUpperCase(), color: '#888', online: false }; return (
{/* Top bar */}
{threads.reduce((a, t) => a + t.unread, 0)} UNREAD
MESSAGES
{/* Tabs */}
{[ { id: 'inbox', l: `INBOX · ${threads.length}` }, { id: 'requests', l: 'REQUESTS · 3' }, ].map(t => ( ))}
{tab === 'inbox' ? ( <> {/* Active now strip */}
● ACTIVE NOW
{Object.values(members).filter(m => m.online).map(m => ( ))}
{/* Threads */}
{threads.map((th, i) => { const m = memberOr(th.member); const last = th.messages[th.messages.length - 1] || { t: '', from: 'them' }; const lastText = last.kind === 'voice' ? '🎙 Voice note · 0:23' : last.t; return ( ); })}
{/* Suggestions to follow */}
SUGGESTED · BASED ON YOUR GENRE
{SUGGESTIONS.filter(handle => members[handle]).map(handle => { const m = members[handle]; return (
{m.handle}
{m.genres.join(' · ')} · {m.city}
onToggleFollow(handle)} />
); })}
) : (
{Icon.bell(theme.muted)}
3 PEOPLE WANT TO MESSAGE YOU
REQUESTS COMING SOON
)}
); } // ───────────────────────────────────────────────────────────── // ThreadScreen — DM conversation // ───────────────────────────────────────────────────────────── function ThreadScreen({ theme, threadId, threads, onBack, onSend, onOpenMember, following, onToggleFollow, members = MEMBERS }) { const thread = threads.find(t => t.id === threadId); if (!thread) return null; const m = members[thread.member] || { handle: thread.member, name: thread.member, lbl: (thread.member || '?').slice(0, 2).toUpperCase(), color: '#888', online: false }; const [input, setInput] = useStateS(''); const scrollRef = useRefS(null); useEffectS(() => { if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight; }, [thread.messages.length]); const send = () => { if (!input.trim()) return; onSend(threadId, input.trim()); setInput(''); }; return (
{/* Top bar */}
onToggleFollow(m.handle)} />
{/* Messages */}
{/* Day divider */}
★ TODAY ★
{thread.messages.map((msg, i) => { const mine = msg.from === 'me'; const prev = thread.messages[i - 1]; const grouped = prev && prev.from === msg.from; if (msg.kind === 'voice') { return (
0:23
); } return (
{msg.t}
); })} {/* Typing indicator */} {m.online && (
{[0, 1, 2].map(i => (
))}
)}
{/* Composer */}
setInput(e.target.value)} onKeyDown={e => e.key === 'Enter' && send()} placeholder="Drop a message…" style={{ flex: 1, padding: '11px 16px', borderRadius: 99, background: theme.surface, border: `1px solid ${theme.border}`, color: theme.text, fontFamily: theme.fontBody, fontSize: 14, outline: 'none', minWidth: 0, }} />
); } // ───────────────────────────────────────────────────────────── // MemberProfileScreen — tap an artist's name → their profile // ───────────────────────────────────────────────────────────── function MemberProfileScreen({ theme, handle, onBack, following, onToggleFollow, onMessage, members = MEMBERS }) { // Never blank out: if the member isn't in the loaded directory (e.g. not yet // synced, or it's you — you're excluded from the directory), show a minimal // profile built from the handle instead of a black screen. const m = members[handle] || { handle, name: handle || 'Member', lbl: (handle || '?').slice(0, 2).toUpperCase(), color: '#888', pal: ['#888', '#0A0A0A'], tier: 'MEMBER', city: '', bio: '', genres: [], followers: 0, following: 0, releases: 0, streak: 0, avatarUrl: null, }; const isFollowing = following.includes(handle); return (
{/* Top bar */}
{/* Identity (banner removed — it covered ~half the screen) */}
{m.tier} · {m.city}
{m.name.toUpperCase()}
@{m.handle.toUpperCase()}
{/* Bio */}
{m.bio}
{/* Genres */}
{m.genres.map(g => {g})}
{/* CTA row */}
{/* Stats */}
{[ { n: m.followers.toLocaleString(), l: 'FOLLOWERS' }, { n: m.following, l: 'FOLLOWING' }, { n: m.releases, l: 'RELEASES' }, { n: m.streak, l: 'STREAK' }, ].map((s, i) => (
{s.n}
{s.l}
))}
{/* Releases preview */}
RECENT RELEASES
{[ { lbl: 'NH', t: 'NEON HOURS' }, { lbl: 'BS', t: 'BLACK SAND' }, { lbl: 'GH', t: 'GHOSTLINE' }, ].map((r, i) => (
{r.t}
))}
{/* Mutuals */}
FOLLOWED BY · 3 OF YOUR SQUAD
{['SV', 'TM', 'IL'].map((lbl, i) => (
0 ? -10 : 0, border: `2px solid ${theme.surface}`, }}>{lbl}
))}
sasha.vee, theo.m and 1 other
); } window.LAUNCH_SCREENS_5 = { MessagesScreen, ThreadScreen, MemberProfileScreen, FollowButton, MEMBERS, THREADS };