diff --git a/src/components/_shared/more-actions/MoreActionsContent.tsx b/src/components/_shared/more-actions/MoreActionsContent.tsx index 95bbde2a..06dcfe1c 100644 --- a/src/components/_shared/more-actions/MoreActionsContent.tsx +++ b/src/components/_shared/more-actions/MoreActionsContent.tsx @@ -6,6 +6,7 @@ import { ReactComponent as LoginIcon } from '@/assets/icons/logout.svg'; import { ReactComponent as MoonIcon } from '@/assets/icons/moon.svg'; import { ReactComponent as SunIcon } from '@/assets/icons/sun.svg'; import CacheClearingDialog from '@/components/_shared/modal/CacheClearingDialog'; +import LogoutConfirm from '@/components/app/workspaces/LogoutConfirm'; import { AFConfigContext } from '@/components/main/app.hooks'; import { ThemeModeContext } from '@/components/main/useAppThemeMode'; import { openUrl } from '@/utils/url'; @@ -23,12 +24,21 @@ function MoreActionsContent({ const { t } = useTranslation(); const navigate = useNavigate(); - const handleLogin = useCallback(() => { + const handleLogout = useCallback(() => { clearRedirectTo(); // Clear stored redirect URL from previous user invalidToken(); navigate('/login?redirectTo=' + encodeURIComponent(window.location.href)); }, [navigate]); + + const handleLogin = useCallback(() => { + if (isAuthenticated) { + setOpenLogoutConfirm(true); + } else { + handleLogout(); + } + }, [isAuthenticated, handleLogout]); const [openConfirm, setOpenConfirm] = React.useState(false); + const [openLogoutConfirm, setOpenLogoutConfirm] = React.useState(false); const actions = useMemo(() => { const items = [ @@ -98,6 +108,11 @@ function MoreActionsContent({ <> {actionsContent} setOpenConfirm(false)} /> + setOpenLogoutConfirm(false)} + onConfirm={handleLogout} + /> ); } diff --git a/src/components/app/app.hooks.tsx b/src/components/app/app.hooks.tsx index 161fe28b..79121d88 100644 --- a/src/components/app/app.hooks.tsx +++ b/src/components/app/app.hooks.tsx @@ -32,7 +32,7 @@ import { AuthInternalContext } from './contexts/AuthInternalContext'; import { AppAuthLayer } from './layers/AppAuthLayer'; import { AppBusinessLayer } from './layers/AppBusinessLayer'; import { AppSyncLayer } from './layers/AppSyncLayer'; -import { WorkspaceLoadingAnimation } from './WorkspaceLoadingAnimation'; +import LoadingDots from '@/components/_shared/LoadingDots'; // Main AppContext interface - kept identical to maintain backward compatibility export interface AppContextType { @@ -101,7 +101,11 @@ const ConditionalWorkspaceLayers = ({ children }: { children: React.ReactNode }) // Show loading animation while workspace ID is being loaded if (!userWorkspaceInfo) { - return ; + return ( +
+ +
+ ); } return ( diff --git a/src/components/app/layers/AppAuthLayer.tsx b/src/components/app/layers/AppAuthLayer.tsx index dd848c40..a92601e4 100644 --- a/src/components/app/layers/AppAuthLayer.tsx +++ b/src/components/app/layers/AppAuthLayer.tsx @@ -18,7 +18,7 @@ export const AppAuthLayer: React.FC = ({ children }) => { const service = useService(); const navigate = useNavigate(); const params = useParams(); - + const [userWorkspaceInfo, setUserWorkspaceInfo] = useState(undefined); // Calculate current workspace ID from URL params or user info diff --git a/src/components/app/workspaces/LogoutConfirm.tsx b/src/components/app/workspaces/LogoutConfirm.tsx new file mode 100644 index 00000000..d6489f28 --- /dev/null +++ b/src/components/app/workspaces/LogoutConfirm.tsx @@ -0,0 +1,69 @@ +import { useTranslation } from 'react-i18next'; + +import { Button } from '@/components/ui/button'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys'; + +export function LogoutConfirm({ + open, + onClose, + onConfirm, +}: { + open: boolean; + onClose: () => void; + onConfirm: () => void; +}) { + const { t } = useTranslation(); + + const handleLogout = () => { + onConfirm(); + onClose(); + }; + + return ( + { + if (!status) { + onClose(); + } + }} + > + { + e.preventDefault(); + }} + onKeyDown={(e) => { + e.stopPropagation(); + if (createHotkey(HOT_KEY_NAME.ENTER)(e.nativeEvent)) { + handleLogout(); + } + }} + > + + {t('button.logout')} + + + {t('settings.menu.logoutPrompt')} + + + + + + + + ); +} + +export default LogoutConfirm; diff --git a/src/components/app/workspaces/Workspaces.tsx b/src/components/app/workspaces/Workspaces.tsx index a0952afd..6b03b52c 100644 --- a/src/components/app/workspaces/Workspaces.tsx +++ b/src/components/app/workspaces/Workspaces.tsx @@ -40,6 +40,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip import { openUrl } from '@/utils/url'; import { AccountSettings } from './AccountSettings'; +import LogoutConfirm from '@/components/app/workspaces/LogoutConfirm'; export function Workspaces() { const { t } = useTranslation(); @@ -66,6 +67,7 @@ export function Workspaces() { const [openRenameWorkspace, setOpenRenameWorkspace] = useState(null); const [openDeleteWorkspace, setOpenDeleteWorkspace] = useState(null); const [openLeaveWorkspace, setOpenLeaveWorkspace] = useState(null); + const [openLogoutConfirm, setOpenLogoutConfirm] = useState(false); const isOwner = currentWorkspace?.owner?.uid.toString() === currentUser?.uid.toString(); @@ -229,7 +231,7 @@ export function Workspaces() {
{t('web.accountSettings')}
- + setOpenLogoutConfirm(true)}> {t('button.logout')} @@ -323,6 +325,12 @@ export function Workspaces() { openOnChange={() => setOpenLeaveWorkspace(null)} /> )} + + setOpenLogoutConfirm(false)} + onConfirm={handleSignOut} + /> ); } diff --git a/src/components/login/LinearBuffer.tsx b/src/components/login/LinearBuffer.tsx index 528daa74..75a6d892 100644 --- a/src/components/login/LinearBuffer.tsx +++ b/src/components/login/LinearBuffer.tsx @@ -1,8 +1,8 @@ -import * as React from 'react'; import Box from '@mui/material/Box'; import LinearProgress from '@mui/material/LinearProgress'; +import * as React from 'react'; -export default function LinearBuffer () { +export default function LinearBuffer() { const [progress, setProgress] = React.useState(0); const [buffer, setBuffer] = React.useState(10); diff --git a/src/components/login/LoginAuth.tsx b/src/components/login/LoginAuth.tsx index b8af13ce..2ccebbfa 100644 --- a/src/components/login/LoginAuth.tsx +++ b/src/components/login/LoginAuth.tsx @@ -5,7 +5,7 @@ import { useContext, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { ReactComponent as ErrorIcon } from '@/assets/icons/error.svg'; -import { WorkspaceLoadingAnimation } from '@/components/app/WorkspaceLoadingAnimation'; +import LoadingDots from '@/components/_shared/LoadingDots'; function LoginAuth () { const service = useContext(AFConfigContext)?.service; @@ -36,7 +36,7 @@ function LoginAuth () { <> {loading ? (
- +
) : null}