"use client"; import { useActionState, useOptimistic, useState, useTransition } from "react"; import { Button, Checkbox, Field, Popover, Rule, Select, Toggle, useToast } from "@unlocalhosted/metalui"; import { createListAction, setLikedAction, setSavedAction } from "@/app/actions/lists"; import type { FormState } from "@/app/actions/types"; import { BookmarkIcon, ChevronDownIcon, HeartIcon, LockIcon, PlusIcon } from "./icons"; import { ButtonLink } from "./ui"; /** The repository's Like key: latched (a filled heart) while the viewer likes it. Signed out, it leads to signing in. */ export function LikeButton({ owner, name, liked, signedIn }: { owner: string; name: string; liked: boolean; signedIn: boolean }) { const toast = useToast(); const [pressed, setPressed] = useOptimistic(liked); const [, startTransition] = useTransition(); if (!signedIn) { return ( }> Like ); } return ( startTransition(async () => { setPressed(next); const result = await setLikedAction(owner, name, next); if (result?.error) toast.show({ title: result.error, tone: "error" }); }) } > {pressed ? "Liked" : "Like"} ); } export type SaveTarget = { id: string; owner: string; name: string; visibility: "public" | "private"; saved: boolean }; /** * The repository's Save key: a popover with the lists the viewer may change (theirs and their * organizations'), ticked where the repository is in them, and a field to make a new list with it. */ export function SaveButton({ repoId, lists, owners, viewer }: { repoId: string; lists: SaveTarget[]; owners: string[]; viewer: string }) { const toast = useToast(); const [shown, setSaved] = useOptimistic(lists, (state, change: { id: string; saved: boolean }) => state.map((l) => (l.id === change.id ? { ...l, saved: change.saved } : l)), ); const [, startTransition] = useTransition(); const anySaved = shown.some((l) => l.saved); const byOwner = owners.map((owner) => ({ owner, lists: shown.filter((l) => l.owner === owner) })).filter((g) => g.lists.length > 0); const toggle = (list: SaveTarget, saved: boolean) => startTransition(async () => { setSaved({ id: list.id, saved }); const result = await setSavedAction(list.id, repoId, saved); if (result?.error) toast.show({ title: result.error, tone: "error" }); }); return ( Save to a list {byOwner.length === 0 &&

Lists gather repositories you want to find again or share. Make your first one below.

} {byOwner.map((group) => (
{owners.length > 1 &&

{group.owner === viewer ? "Your lists" : group.owner}

}
    {group.lists.map((list) => (
  • ))}
))}
); } /** Makes a list with the repository in it, without leaving the page. */ function NewListForm({ repoId, owners, viewer }: { repoId: string; owners: string[]; viewer: string }) { const toast = useToast(); const [created, setCreated] = useState(0); const [, action, pending] = useActionState(async (prev: FormState, formData: FormData) => { const result = await createListAction(prev, formData); if (result?.error) toast.show({ title: result.error, tone: "error" }); else { if (result?.success) toast.show({ title: result.success, tone: "success" }); setCreated((n) => n + 1); } return result; }, undefined); return ( // A new key empties the fields once the list is made.
{owners.length > 1 ? ( )}
); }