)}
@@ -330,7 +331,7 @@ function Section({
function TokenError() {
const { t } = useTranslation();
return (
-
+
{t("setup.token.title")}
{t("setup.token.body")}
diff --git a/frontend/src/components/ShareDialog.tsx b/frontend/src/components/ShareDialog.tsx
index 0c1cc10c..c85df0bd 100644
--- a/frontend/src/components/ShareDialog.tsx
+++ b/frontend/src/components/ShareDialog.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useMemo, useRef, useState } from "react";
+import { useEffect, useRef, useState } from "react";
import { qk } from "../lib/queryKeys";
import { useTranslation } from "react-i18next";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
@@ -12,9 +12,10 @@ import { formatDate } from "../lib/format";
import { api, type DownloadJob, type ShareLink, type ShareRecipient } from "../lib/api";
import { Button } from "./ui/Button";
import { IconButton } from "./ui/IconButton";
-import { Pressable } from "./ui/Pressable";
import { Input, InnerInput } from "./ui/Input";
import { CheckRow } from "./ui/NativeControls";
+import { SURFACE } from "./ui/surfaces";
+import UserPicker from "./UserPicker";
const EXPIRY_DAYS = [null, 1, 7, 30] as const;
// --- share with a registered user (ACL) -----------------------------------------------------
@@ -25,27 +26,13 @@ const NO_RECIPIENTS: ShareRecipient[] = [];
function UserShare({ job }: { job: DownloadJob }) {
const { t } = useTranslation();
- const [q, setQ] = useState("");
- const [open, setOpen] = useState(false);
const recipients = useQuery({ queryKey: qk.shareRecipients(), queryFn: api.shareRecipients });
const list = recipients.data ?? NO_RECIPIENTS;
- const filtered = useMemo(() => {
- const s = q.trim().toLowerCase();
- if (!s) return list.slice(0, 8);
- return list
- .filter(
- (u) =>
- u.email.toLowerCase().includes(s) || (u.display_name ?? "").toLowerCase().includes(s),
- )
- .slice(0, 8);
- }, [q, list]);
const share = useMutation({
mutationFn: (email: string) => api.shareDownload(job.id, email),
onSuccess: (r) => {
notify({ level: "success", message: t("downloads.share.done", { email: r.shared_with }) });
- setQ("");
- setOpen(false);
},
onError: (e) => notifyActionError(e, t("downloads.share.failed")),
});
@@ -56,36 +43,15 @@ function UserShare({ job }: { job: DownloadJob }) {
{t("downloads.share.userSection")}
-
{
- setQ(e.target.value);
- setOpen(true);
- }}
- onFocus={() => setOpen(true)}
- // Close on blur, but after a tick so a click on a dropdown row still registers.
- onBlur={() => setTimeout(() => setOpen(false), 150)}
- placeholder={t("downloads.share.pickPlaceholder")}
- />
- {open && filtered.length > 0 && (
-
- {filtered.map((u) => (
-
share.mutate(u.email)}
- disabled={share.isPending}
- className="w-full px-3 py-2 text-sm flex flex-col"
- >
- {u.display_name || u.email}
- {u.display_name && {u.email} }
-
- ))}
-
- )}
-