diff --git a/app/client/modules/auth/routes/login.tsx b/app/client/modules/auth/routes/login.tsx index c855a1ac..30a261e3 100644 --- a/app/client/modules/auth/routes/login.tsx +++ b/app/client/modules/auth/routes/login.tsx @@ -51,14 +51,15 @@ export function LoginPage({ error }: LoginPageProps = {}) { const errorDescription = errorCode ? getLoginErrorDescription(errorCode) : null; useEffect(() => { - if ( - !PublicKeyCredential.isConditionalMediationAvailable || - !PublicKeyCredential.isConditionalMediationAvailable() - ) { - return; - } - const autoSignIn = async () => { + if ( + typeof PublicKeyCredential === "undefined" || + !PublicKeyCredential.isConditionalMediationAvailable || + !(await PublicKeyCredential.isConditionalMediationAvailable()) + ) { + return; + } + await authClient.signIn.passkey({ autoFill: true, fetchOptions: { diff --git a/app/client/modules/settings/components/passkeys-section.tsx b/app/client/modules/settings/components/passkeys-section.tsx index 1a4f3938..bffbaa2c 100644 --- a/app/client/modules/settings/components/passkeys-section.tsx +++ b/app/client/modules/settings/components/passkeys-section.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQuery } from "@tanstack/react-query"; import { Fingerprint, Plus, Trash2, Pencil } from "lucide-react"; import { toast } from "sonner"; import { Button } from "~/client/components/ui/button"; @@ -27,6 +27,7 @@ import { Label } from "~/client/components/ui/label"; import { authClient } from "~/client/lib/auth-client"; import { logger } from "~/client/lib/logger"; import { useTimeFormat } from "~/client/lib/datetime"; +import { cn } from "~/client/lib/utils"; type PasskeyEntry = { id: string; @@ -37,8 +38,16 @@ type PasskeyEntry = { export function PasskeysSection() { const { formatDateTime } = useTimeFormat(); - const { data: passkeys, isPending } = authClient.useListPasskeys(); + const { data: passkeys, isPending } = useQuery({ + queryKey: ["passkeys"], + queryFn: async () => { + const { data, error } = await authClient.passkey.listUserPasskeys(); + if (error) throw error; + return data; + }, + }); + const [deletePasskeyOpen, setDeletePasskeyOpen] = useState(false); const [addDialogOpen, setAddDialogOpen] = useState(false); const [newPasskeyName, setNewPasskeyName] = useState(""); @@ -84,12 +93,12 @@ export function PasskeysSection() { const deletePasskeyMutation = useMutation({ mutationFn: async (id: string) => { - const { error } = await authClient.$fetch("/passkey/delete-passkey", { - method: "POST", - body: { id }, - }); + const { error } = await authClient.passkey.deletePasskey({ id }); if (error) throw error; }, + onMutate: () => { + setDeletePasskeyOpen(false); + }, onSuccess: () => { toast.success("Passkey deleted"); setDeleteTarget(null); @@ -100,13 +109,13 @@ export function PasskeysSection() { }, }); - const handleAddPasskey = (e: React.FormEvent) => { + const handleAddPasskey = (e: React.ChangeEvent) => { e.preventDefault(); const name = newPasskeyName.trim() || undefined; addPasskeyMutation.mutate(name); }; - const handleRename = (e: React.FormEvent) => { + const handleRename = (e: React.ChangeEvent) => { e.preventDefault(); if (!renameTarget) return; const name = renameValue.trim(); @@ -122,8 +131,6 @@ export function PasskeysSection() { deletePasskeyMutation.mutate(deleteTarget.id); }; - const list = (passkeys ?? []) as PasskeyEntry[]; - return ( <>
Loading passkeys...
- ) : list.length === 0 ? ( -- No passkeys yet. Add one to enable passwordless sign-in. -
- ) : ( -- {p.name?.trim() || "Unnamed passkey"} -
-- Added {formatDateTime(new Date(p.createdAt))} -
-Loading passkeys...
+0 })}> + No passkeys yet. Add one to enable passwordless sign-in. +
+{p.name?.trim() || "Unnamed passkey"}
++ Added {formatDateTime(new Date(p.createdAt))} +
+