File size: 3,571 Bytes
e92d5c0 830bf88 e92d5c0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
import React, { useState, useEffect } from "react";
import { BACKEND_URL } from "../constants";
import { apiFetch } from "../utils/apiFetch";
interface UserInfo {
connected: boolean;
username: string | null;
}
const OAuthButton: React.FC = () => {
const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchUserInfo = async () => {
try {
setLoading(true);
const response = await apiFetch("/api/user");
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: UserInfo = await response.json();
setUserInfo(data);
setError(null);
} catch (err) {
setError(
err instanceof Error ? err.message : "Failed to fetch user info",
);
setUserInfo(null);
} finally {
setLoading(false);
}
};
fetchUserInfo();
}, []);
const handleLogin = () => {
const uri = `${BACKEND_URL}/oauth/huggingface/login`;
// Enable scrolling in parent window
window.parent?.postMessage({ type: "SET_SCROLLING", enabled: true }, "*");
// Redirect after a short delay
setTimeout(() => {
window.location.assign(uri + window.location.search);
}, 500);
};
const handleLogout = () => {
const uri = `${BACKEND_URL}/oauth/huggingface/logout`;
window.location.assign(uri);
};
// Loading state
if (loading) {
return (
<div className="flex items-center justify-center">
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-500"></div>
<span className="ml-2 text-gray-400 dark:text-gray-500 text-base">
Loading...
</span>
</div>
);
}
// Error state
if (error) {
return (
<div className="p-3 bg-red-900/20 dark:bg-red-100/20 border border-red-700 dark:border-red-300 rounded-lg">
<p className="text-red-400 dark:text-red-600 text-base">
Error: {error}
</p>
</div>
);
}
// Logged out state
if (!userInfo?.connected) {
return (
<button
onClick={handleLogin}
className="flex items-center justify-center space-x-3 px-4 py-2 bg-white dark:bg-[#0B0F19] hover:bg-gray-100 dark:hover:bg-[#1A1F2E] text-[#2C3236] dark:text-[#D6DAE2] rounded-full transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500/50 w-full h-12"
aria-label="Sign in with Hugging Face"
>
<img
src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
alt="Hugging Face"
className="h-5 w-5"
/>
<span className="font-medium text-base">Sign in with Hugging Face</span>
</button>
);
}
// Logged in state
return (
<button
onClick={handleLogout}
className="flex items-center justify-center space-x-3 px-4 py-2 bg-white dark:bg-[#0B0F19] hover:bg-gray-100 dark:hover:bg-[#1A1F2E] text-[#2C3236] dark:text-[#D6DAE2] rounded-full transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500/50 w-full h-12"
aria-label="Logout"
>
<img
src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg"
alt="Hugging Face"
className="h-5 w-5"
/>
<span className="font-medium text-base">
Logout ({userInfo.username || "User"})
</span>
</button>
);
};
export default OAuthButton;
|