chore(demo): replace isLoading using useTransition (#3775)

* fix: use `useTransition` for isLoading

* fixup! fix: use `useTransition` for isLoading
This commit is contained in:
Alex Yang
2025-08-04 11:11:20 -07:00
committed by Bereket Engida
parent f49a2fbf99
commit 3ee67fcca0
5 changed files with 95 additions and 95 deletions

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState } from "react"; import { useState, useTransition } from "react";
import { signIn, client } from "@/lib/auth-client"; import { signIn, client } from "@/lib/auth-client";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
@@ -19,33 +19,31 @@ import { Loader2 } from "lucide-react";
export default function ClientTest() { export default function ClientTest() {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false); const [loading, startTransition] = useTransition();
// Get the session data using the useSession hook // Get the session data using the useSession hook
const { data: session, isPending, error } = client.useSession(); const { data: session, isPending, error } = client.useSession();
const handleLogin = async () => { const handleLogin = async () => {
setLoading(true); startTransition(async () => {
await signIn.email( await signIn.email(
{ {
email, email,
password, password,
callbackURL: "/client-test", callbackURL: "/client-test",
},
{
onResponse: () => {
setLoading(false);
}, },
onError: (ctx) => { {
toast.error(ctx.error.message); onError: (ctx) => {
toast.error(ctx.error.message);
},
onSuccess: () => {
toast.success("Successfully logged in!");
setEmail("");
setPassword("");
},
}, },
onSuccess: () => { );
toast.success("Successfully logged in!"); });
setEmail("");
setPassword("");
},
},
);
}; };
return ( return (

View File

@@ -42,7 +42,7 @@ import {
} from "lucide-react"; } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useState } from "react"; import { useState, useTransition } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { UAParser } from "ua-parser-js"; import { UAParser } from "ua-parser-js";
import { import {
@@ -657,7 +657,7 @@ function EditUserDialog() {
} }
}; };
const [open, setOpen] = useState<boolean>(false); const [open, setOpen] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false); const [isLoading, startTransition] = useTransition();
return ( return (
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
@@ -720,25 +720,27 @@ function EditUserDialog() {
<Button <Button
disabled={isLoading} disabled={isLoading}
onClick={async () => { onClick={async () => {
setIsLoading(true); startTransition(async () => {
await client.updateUser({ await client.updateUser({
image: image ? await convertImageToBase64(image) : undefined, image: image ? await convertImageToBase64(image) : undefined,
name: name ? name : undefined, name: name ? name : undefined,
fetchOptions: { fetchOptions: {
onSuccess: () => { onSuccess: () => {
toast.success("User updated successfully"); toast.success("User updated successfully");
},
onError: (error) => {
toast.error(error.error.message);
},
}, },
onError: (error) => { });
toast.error(error.error.message); startTransition(() => {
}, setName("");
}, router.refresh();
setImage(null);
setImagePreview(null);
setOpen(false);
});
}); });
setName("");
router.refresh();
setImage(null);
setImagePreview(null);
setIsLoading(false);
setOpen(false);
}} }}
> >
{isLoading ? ( {isLoading ? (

View File

@@ -12,7 +12,7 @@ import {
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { useState } from "react"; import { useState, useTransition } from "react";
import { Loader2 } from "lucide-react"; import { Loader2 } from "lucide-react";
import { signIn } from "@/lib/auth-client"; import { signIn } from "@/lib/auth-client";
import Link from "next/link"; import Link from "next/link";
@@ -22,7 +22,7 @@ import { useRouter } from "next/navigation";
export default function SignIn() { export default function SignIn() {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false); const [loading, startTransition] = useTransition();
const [rememberMe, setRememberMe] = useState(false); const [rememberMe, setRememberMe] = useState(false);
const router = useRouter(); const router = useRouter();
@@ -83,16 +83,16 @@ export default function SignIn() {
className="w-full" className="w-full"
disabled={loading} disabled={loading}
onClick={async () => { onClick={async () => {
setLoading(true); startTransition(async () => {
await signIn.email( await signIn.email(
{ email, password, rememberMe: rememberMe ? true : false }, { email, password, rememberMe },
{ {
onSuccess(context) { onSuccess(context) {
router.push("/dashboard"); router.push("/dashboard");
},
}, },
}, );
); });
setLoading(false);
}} }}
> >
{loading ? <Loader2 size={16} className="animate-spin" /> : "Login"} {loading ? <Loader2 size={16} className="animate-spin" /> : "Login"}

View File

@@ -1,5 +1,5 @@
"use client"; "use client";
import { useState } from "react"; import { useState, useTransition } from "react";
import { import {
Check, Check,
Copy, Copy,
@@ -50,11 +50,10 @@ export function useCopyButton(
const cache = new Map<string, string>(); const cache = new Map<string, string>();
export function LLMCopyButton() { export function LLMCopyButton() {
const [isLoading, setLoading] = useState(false); const [isLoading, startTransition] = useTransition();
const [checked, onClick] = useCopyButton(async () => { const [checked, onClick] = useCopyButton(async () => {
setLoading(true); startTransition(async () => {
const url = window.location.pathname + ".mdx"; const url = window.location.pathname + ".mdx";
try {
const cached = cache.get(url); const cached = cache.get(url);
if (cached) { if (cached) {
@@ -71,9 +70,7 @@ export function LLMCopyButton() {
}), }),
]); ]);
} }
} finally { });
setLoading(false);
}
}); });
return ( return (

View File

@@ -1,7 +1,7 @@
"use client"; "use client";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useState } from "react"; import { useState, useTransition } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import * as z from "zod"; import * as z from "zod";
import { KJUR } from "jsrsasign"; import { KJUR } from "jsrsasign";
@@ -43,7 +43,7 @@ type AppleJwtFormValues = z.infer<typeof appleJwtSchema>;
export const GenerateAppleJwt = () => { export const GenerateAppleJwt = () => {
const [generatedJwt, setGeneratedJwt] = useState<string | null>(null); const [generatedJwt, setGeneratedJwt] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false); const [isLoading, startTransition] = useTransition();
const form = useForm<AppleJwtFormValues>({ const form = useForm<AppleJwtFormValues>({
resolver: zodResolver(appleJwtSchema), resolver: zodResolver(appleJwtSchema),
@@ -56,48 +56,51 @@ export const GenerateAppleJwt = () => {
}); });
const onSubmit = async (data: AppleJwtFormValues) => { const onSubmit = async (data: AppleJwtFormValues) => {
setIsLoading(true);
setGeneratedJwt(null); setGeneratedJwt(null);
setError(null); setError(null);
startTransition(() => {
try {
//normalize the private key by replacing \r\n with \n and trimming whitespace just incase lol
const normalizedKey = data.privateKey.replace(/\r\n/g, "\n").trim();
try { //since jose is not working with safari, we are using jsrsasign
//normalize the private key by replacing \r\n with \n and trimming whitespace just incase lol
const normalizedKey = data.privateKey.replace(/\r\n/g, "\n").trim();
//since jose is not working with safari, we are using jsrsasign const header = {
alg: "ES256",
kid: data.keyId,
typ: "JWT",
};
const header = { const issuedAtSeconds = Math.floor(Date.now() / 1000);
alg: "ES256", const expirationSeconds = issuedAtSeconds + 180 * 24 * 60 * 60; // 180 days. Should we let the user choose this ? MAX is 6 months
kid: data.keyId,
typ: "JWT",
};
const issuedAtSeconds = Math.floor(Date.now() / 1000); const payload = {
const expirationSeconds = issuedAtSeconds + 180 * 24 * 60 * 60; // 180 days. Should we let the user choose this ? MAX is 6 months iss: data.teamId, // Issuer (Team ID)
aud: "https://appleid.apple.com", // Audience
sub: data.clientId, // Subject (Client ID -> Service ID)
iat: issuedAtSeconds, // Issued At timestamp
exp: expirationSeconds, // Expiration timestamp
};
const payload = { const sHeader = JSON.stringify(header);
iss: data.teamId, // Issuer (Team ID) const sPayload = JSON.stringify(payload);
aud: "https://appleid.apple.com", // Audience
sub: data.clientId, // Subject (Client ID -> Service ID)
iat: issuedAtSeconds, // Issued At timestamp
exp: expirationSeconds, // Expiration timestamp
};
const sHeader = JSON.stringify(header); const jwt = KJUR.jws.JWS.sign(
const sPayload = JSON.stringify(payload); "ES256",
sHeader,
const jwt = KJUR.jws.JWS.sign("ES256", sHeader, sPayload, normalizedKey); sPayload,
setGeneratedJwt(jwt); normalizedKey,
} catch (err: any) { );
console.error("JWT Generation Error:", err); setGeneratedJwt(jwt);
setError( } catch (err: any) {
`Failed to generate JWT: ${ console.error("JWT Generation Error:", err);
err.message || "Unknown error" setError(
}. Check key format and details.`, `Failed to generate JWT: ${
); err.message || "Unknown error"
} finally { }. Check key format and details.`,
setIsLoading(false); );
} }
});
}; };
const copyToClipboard = () => { const copyToClipboard = () => {