refactor: reference human id instead of email (#73)

* refactor: update api and client to reference humanIds

* fix: prevent deletion of network member

This may cause various side effects if there is data in other services
which reference this member
This commit was merged in pull request #73.
This commit is contained in:
Arjun Patel
2026-03-24 19:48:02 -07:00
committed by GitHub
parent 3bf3f16be7
commit bf0147d542
24 changed files with 666 additions and 416 deletions
+9 -9
View File
@@ -42,7 +42,7 @@ export function ComposeOverlay({
const [reviewMimeType, setReviewMimeType] = useState<string | null>(null);
const recordingMode = useMediaSettingsStore((s) => s.recordingMode);
const userEmail = useAuthStore((s) => s.user?.email);
const userId = useAuthStore((s) => s.user?.id);
const createParticle = useCreateParticle();
const createStream = useCreateStreamParticle();
@@ -113,7 +113,7 @@ export function ComposeOverlay({
const createChildParticle = useCallback(
async (path: ParticlePath) => {
if (!userEmail) return;
if (!userId) return;
let particleId = '';
if (textContent.trim()) {
@@ -121,7 +121,7 @@ export function ComposeOverlay({
path,
type: "text",
properties: { content: textContent },
createdByEmail: userEmail,
createdByHumanId: userId,
});
} else if (reviewBlob && reviewMimeType) {
const { object_id, size_bytes } = await uploadMedia(
@@ -138,14 +138,14 @@ export function ComposeOverlay({
duration_ms: reviewDurationMs,
size_bytes,
},
createdByEmail: userEmail,
createdByHumanId: userId,
});
}
onParticleCreated?.(particleId);
},
[
userEmail,
userId,
textContent,
reviewBlob,
reviewMimeType,
@@ -158,7 +158,7 @@ export function ComposeOverlay({
// Reply mode: create particle directly under targetPath
const onSubmitReply = useEffectEvent(async () => {
if (!targetPath || !userEmail || stepRef.current === "submitting") return;
if (!targetPath || !userId || stepRef.current === "submitting") return;
setStepSync("submitting");
await createChildParticle(targetPath);
cancel();
@@ -167,7 +167,7 @@ export function ComposeOverlay({
// New stream mode: create stream + first child
const handleStreamSubmit = useCallback(
async (streamName: string, visibleTo: string[]) => {
if (!userEmail || stepRef.current === "submitting") return;
if (!userId || stepRef.current === "submitting") return;
setStepSync("submitting");
const streamId = await createStream.mutateAsync({
@@ -176,7 +176,7 @@ export function ComposeOverlay({
name: streamName,
status: "open",
},
createdByEmail: userEmail,
createdByHumanId: userId,
visibleTo,
});
@@ -185,7 +185,7 @@ export function ComposeOverlay({
cancel();
},
[networkId, userEmail, createParticle, createChildParticle, cancel],
[networkId, userId, createParticle, createChildParticle, cancel],
);
// --- Keyboard handling ---
@@ -24,16 +24,16 @@ export function ConfigureStreamStep({
const [name, setName] = useState(() => generateRandomName());
const [everyone, setEveryone] = useState(true);
const userEmail = useAuthStore((s) => s.user?.email);
const [selectedEmails, setSelectedEmails] = useState<Set<string>>(new Set());
const userId = useAuthStore((s) => s.user?.id);
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
const members = (network?.humans ?? []).filter((h) => h.email !== userEmail);
const members = (network?.humans ?? []).filter((h) => h.id !== userId);
const toggleMember = useCallback((email: string) => {
setSelectedEmails((prev) => {
const toggleMember = useCallback((id: string) => {
setSelectedIds((prev) => {
const next = new Set(prev);
if (next.has(email)) next.delete(email);
else next.add(email);
if (next.has(id)) next.delete(id);
else next.add(id);
return next;
});
}, []);
@@ -41,8 +41,8 @@ export function ConfigureStreamStep({
const buildVisibleTo = useCallback((): string[] => {
if (everyone && networkId) return [`network:${networkId}`];
return Array.from(removeDuplicates([...selectedEmails, userEmail])).map((e) => `human:${e}`);
}, [everyone, networkId, selectedEmails, userEmail]);
return Array.from(removeDuplicates([...selectedIds, userId].filter(Boolean) as string[])).map((id) => `human:${id}`);
}, [everyone, networkId, selectedIds, userId]);
const handleSubmit = useCallback(() => {
if (!name.trim() || !networkId) return;
@@ -117,16 +117,16 @@ export function ConfigureStreamStep({
<ScrollArea className="max-h-48">
<div className="space-y-0.5 p-1">
{members.map((member, index) => {
const isSelected = selectedEmails.has(member.email);
const isSelected = selectedIds.has(member.id);
const initials = member.email_prefix
.slice(0, 2)
.toUpperCase();
return (
<div
key={member.email}
key={member.id}
role="button"
onClick={() => toggleMember(member.email)}
onClick={() => toggleMember(member.id)}
className={cn(
"flex w-full cursor-pointer items-center gap-2.5 rounded px-3 py-1.5 text-left text-sm transition-colors",
"text-white/70 hover:bg-white/5",