Spaces:
Sleeping
Sleeping
| 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; | |