Crews page
- "Near me" geolocation toggle with haversine distance sort + distance chips
- Crew count + cities social-proof stat header
- Deterministic per-crew vibe-colour accents (no more wall of teal)
- Activity pulse ("🔥 active this week") + OG/Verified/Established rep badges
- Rich empty state ("Claim your local scene") instead of dead "No crews yet"
Crew synergies
- Crew treasury: transparent, non-custodial NIP-75 zap goal (kind 9041) with
live progress bar tallied from zap receipts; funds settle to founder's own
Lightning wallet — no shared wallet or new wallet settings needed
- Crew-hosted events: host a jam/comp as the crew (event a-tag → crew coord),
"Hosted by [crew]" on event pages, Crew Events list on the crew page
- Crew ↔ Lab hiring pipeline: "Open Call / looking-for" flags (filmer, editor,
coach, builder, members) surfaced on the crew page
- Request to join: encrypted NIP-17 DM to founder + admins (no founder-push
bottleneck, no public join event)
- Reverse links: "Crews that skate here" on spot pages
Contacts (0xchat-style + Armada-style privacy)
- New /contacts page with Crew-mates / Follows / Recent DMs / Saved tabs
- Per-contact quick actions: message, zap, view profile, star
- Private encrypted contacts list (NIP-51 kind 30000, NIP-44 encrypted to self)
- Added to burger menu + desktop More menu (gated to logged-in users)
Docs: NIP.md updated with crew treasury, crew-hosted events, looking-for tags,
and the private encrypted contacts list.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
45 lines
1.4 KiB
TypeScript
45 lines
1.4 KiB
TypeScript
import { useMemo } from 'react';
|
|
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
|
import { useCrews } from '@/hooks/useCrews';
|
|
|
|
export interface Crewmate {
|
|
pubkey: string;
|
|
/** Crews (names) the person shares with the viewer. */
|
|
sharedCrews: string[];
|
|
}
|
|
|
|
/**
|
|
* The people in the viewer's crews — founder + members of every crew the
|
|
* logged-in user belongs to (as founder or listed member), minus themselves.
|
|
*
|
|
* Powers the "Crew-mates" tab in Contacts: the human network the user has
|
|
* already opted into by being in shared crews.
|
|
*/
|
|
export function useCrewmates() {
|
|
const { user } = useCurrentUser();
|
|
const { data: crews, isLoading } = useCrews(200);
|
|
|
|
const crewmates = useMemo<Crewmate[]>(() => {
|
|
if (!user || !crews) return [];
|
|
|
|
const myCrews = crews.filter(
|
|
(c) => c.pubkey === user.pubkey || c.members.includes(user.pubkey),
|
|
);
|
|
|
|
const map = new Map<string, Set<string>>();
|
|
for (const crew of myCrews) {
|
|
const people = [crew.pubkey, ...crew.members];
|
|
for (const pk of people) {
|
|
if (pk === user.pubkey) continue;
|
|
if (!map.has(pk)) map.set(pk, new Set());
|
|
map.get(pk)!.add(crew.name);
|
|
}
|
|
}
|
|
|
|
return Array.from(map.entries())
|
|
.map(([pubkey, names]) => ({ pubkey, sharedCrews: Array.from(names) }))
|
|
.sort((a, b) => b.sharedCrews.length - a.sharedCrews.length);
|
|
}, [user, crews]);
|
|
|
|
return { crewmates, isLoading };
|
|
}
|