Files
Arnaud 77c739960d feat: followers-only recipe visibility
Adds a fourth visibility tier alongside private/unlisted/public:
"followers" — visible to the author and anyone who follows them,
excluded from public search/explore/profile listings and from the
anonymous /r/[id] share route, included in the Following feed. The
in-app recipe detail gate and the public share route both check
follow status directly against the DB rather than the union type
alone, since neither previously had any per-viewer access logic for
a non-public/non-owner case.

Requires the generated migration (0041) to run against a live DB —
not applied in this sandbox (no Docker here); run `pnpm db:migrate`.

v0.41.0
2026-07-17 17:05:10 +02:00

40 lines
1.2 KiB
TypeScript

"use client";
import { useTranslations } from "next-intl";
import { Share2 } from "lucide-react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
export function ShareRecipeButton({ recipeId, visibility }: { recipeId: string; visibility: string }) {
const t = useTranslations("recipe");
async function handleShare() {
const url = `${window.location.origin}/r/${recipeId}`;
try {
await navigator.clipboard.writeText(url);
} catch {
toast.error(t("shareCopyFailed"));
return;
}
if (visibility === "private" || visibility === "followers") {
toast.info(t("shareNotPublicNotice"));
} else {
toast.success(t("shareLinkCopied"));
}
}
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger render={
<Button variant="ghost" size="icon" onClick={() => void handleShare()} aria-label={t("share")}>
<Share2 className="h-4 w-4" />
</Button>
} />
<TooltipContent>{t("share")}</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}