"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 (
}>
{anySaved ? "Saved" : "Save"}
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.
);
}