Skip to content

Commit 6be1305

Browse files
committed
feat: integrate authentication modal into ExcalidrawWrapper
- Added authentication handling to ExcalidrawWrapper, displaying AuthModal when the user is not authenticated. - Removed AuthModal state management from AuthGate, simplifying its structure. - Updated AuthModal to use the Dialog component for improved UI consistency.
1 parent cc47d58 commit 6be1305

File tree

4 files changed

+56
-53
lines changed

4 files changed

+56
-53
lines changed

src/frontend/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ export default function App({
125125
setExcalidrawAPI={setExcalidrawAPI}
126126
onChange={debouncedLogChange}
127127
MainMenu={MainMenu}
128+
isAuthenticated={isAuthenticated}
129+
isAuthLoading={isAuthLoading}
128130
>
129131
{children}
130132
</ExcalidrawWrapper>

src/frontend/src/AuthGate.tsx

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, { useEffect, useRef, useState } from "react";
22
import { useAuthCheck } from "./api/hooks";
3-
import AuthModal from "./ui/AuthModal";
43

54
/**
65
* If unauthenticated, it shows the AuthModal as an overlay, but still renders the app behind it.
@@ -50,37 +49,6 @@ export default function AuthGate({ children }: { children: React.ReactNode }) {
5049
// eslint-disable-next-line react-hooks/exhaustive-deps
5150
}, [isAuthenticated, coderAuthDone]);
5251

53-
// State to control modal visibility and exit animation
54-
const [showAuthModal, setShowAuthModal] = useState(false);
55-
const [isExiting, setIsExiting] = useState(false);
56-
57-
// Update showAuthModal when authentication status changes
58-
useEffect(() => {
59-
if (isAuthenticated === false) {
60-
setShowAuthModal(true);
61-
setIsExiting(false);
62-
} else if (isAuthenticated === true && showAuthModal) {
63-
// Start exit animation when user becomes authenticated
64-
setIsExiting(true);
65-
// Modal will be removed after animation completes via onExitComplete
66-
}
67-
}, [isAuthenticated, showAuthModal]);
68-
69-
// Handle exit animation completion
70-
const handleExitComplete = () => {
71-
setShowAuthModal(false);
72-
};
73-
74-
// Always render children; overlay AuthModal if not authenticated
75-
return (
76-
<>
77-
{children}
78-
{showAuthModal && (
79-
<AuthModal
80-
isExiting={isExiting}
81-
onExitComplete={handleExitComplete}
82-
/>
83-
)}
84-
</>
85-
);
52+
// Just render children - AuthModal is now handled by ExcalidrawWrapper
53+
return <>{children}</>;
8654
}

src/frontend/src/ExcalidrawWrapper.tsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import React, { Children, cloneElement } from 'react';
1+
import React, { Children, cloneElement, useState, useEffect } from 'react';
22
import DiscordButton from './ui/DiscordButton';
33
import FeedbackButton from './ui/FeedbackButton';
44
import type { ExcalidrawImperativeAPI } from '@atyrode/excalidraw/types';
55
import type { NonDeletedExcalidrawElement } from '@atyrode/excalidraw/element/types';
66
import type { AppState } from '@atyrode/excalidraw/types';
77
import { MainMenuConfig } from './ui/MainMenu';
88
import { renderCustomEmbeddable } from './CustomEmbeddableRenderer';
9+
import AuthModal from './ui/AuthModal';
910

1011
const defaultInitialData = {
1112
elements: [],
@@ -25,6 +26,8 @@ interface ExcalidrawWrapperProps {
2526
onChange: (elements: NonDeletedExcalidrawElement[], state: AppState) => void;
2627
MainMenu: any;
2728
renderTopRightUI?: () => React.ReactNode;
29+
isAuthenticated?: boolean | null;
30+
isAuthLoading?: boolean;
2831
}
2932

3033
export const ExcalidrawWrapper: React.FC<ExcalidrawWrapperProps> = ({
@@ -35,7 +38,19 @@ export const ExcalidrawWrapper: React.FC<ExcalidrawWrapperProps> = ({
3538
onChange,
3639
MainMenu,
3740
renderTopRightUI,
41+
isAuthenticated = null,
42+
isAuthLoading = false,
3843
}) => {
44+
// Add state for modal animation
45+
const [isExiting, setIsExiting] = useState(false);
46+
47+
// Handle auth state changes
48+
useEffect(() => {
49+
if (isAuthenticated === true) {
50+
setIsExiting(true);
51+
}
52+
}, [isAuthenticated]);
53+
3954
const renderExcalidraw = (children: React.ReactNode) => {
4055
const Excalidraw = Children.toArray(children).find(
4156
(child: any) =>
@@ -65,7 +80,15 @@ export const ExcalidrawWrapper: React.FC<ExcalidrawWrapperProps> = ({
6580
</div>
6681
)),
6782
},
68-
<MainMenuConfig MainMenu={MainMenu} excalidrawAPI={excalidrawAPI} />
83+
<>
84+
<MainMenuConfig MainMenu={MainMenu} excalidrawAPI={excalidrawAPI} />
85+
{!isAuthLoading && isAuthenticated === false && (
86+
<AuthModal
87+
isExiting={isExiting}
88+
onCloseRequest={() => {}}
89+
/>
90+
)}
91+
</>
6992
);
7093
};
7194

src/frontend/src/ui/AuthModal.tsx

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@ import React, { useState, useEffect } from "react";
22
import { capture } from "../utils/posthog";
33
import { Mail } from "lucide-react";
44
import { queryClient } from "../api/queryClient";
5-
import Modal from "./Modal";
5+
66
import "../styles/AuthModal.scss";
7+
import { Dialog } from "@atyrode/excalidraw";
78

89
interface AuthModalProps {
910
description?: React.ReactNode;
1011
warningText?: string;
11-
onExitComplete?: () => void;
12+
onCloseRequest: () => void;
1213
isExiting?: boolean;
1314
}
1415

1516
const AuthModal: React.FC<AuthModalProps> = ({
1617
description = <>Welcome to your <strong className="highlight">whiteboard IDE</strong>. Open <strong className="highlight">terminals</strong> and start coding right away in your own <strong className="highlight">Ubuntu VM</strong>!</>,
1718
warningText = "🚧 This is a beta. Make backups! 🚧",
18-
onExitComplete,
19-
isExiting = false,
19+
onCloseRequest,
2020
}) => {
2121
const [isMounted, setIsMounted] = useState(false);
2222

@@ -44,18 +44,9 @@ const AuthModal: React.FC<AuthModalProps> = ({
4444

4545
if (!isMounted) return null;
4646

47-
return (
48-
<Modal
49-
logoSrc="/assets/images/favicon.png"
50-
logoAlt="pad.ws logo"
51-
className="auth-modal"
52-
isExiting={isExiting}
53-
onExitComplete={onExitComplete}
54-
>
55-
<div className="auth-modal__content">
56-
<div id="modal-title" className="auth-modal__title-container">
57-
<h2 className="auth-modal__title">pad<span className="auth-modal__title-dot">.ws</span></h2>
58-
</div>
47+
// Prepare the content for the Dialog
48+
const dialogContent = (
49+
<div className="auth-modal__content">
5950
<div className="auth-modal__separator" />
6051

6152
<p className="auth-modal__description">{description}</p>
@@ -172,7 +163,26 @@ const AuthModal: React.FC<AuthModalProps> = ({
172163
{warningText}
173164
</div>
174165
</div>
175-
</Modal>
166+
);
167+
168+
return (
169+
<Dialog
170+
className="auth-modal"
171+
size="regular"
172+
onCloseRequest={onCloseRequest}
173+
title={
174+
<div id="modal-title" className="auth-modal__title-container">
175+
<img
176+
src="/assets/images/favicon.png"
177+
alt="pad.ws logo"
178+
className="auth-modal__logo"
179+
/>
180+
<h2 className="auth-modal__title">pad<span className="auth-modal__title-dot">.ws</span></h2>
181+
</div>
182+
}
183+
closeOnClickOutside={false}
184+
children={dialogContent}
185+
/>
176186
);
177187
};
178188

0 commit comments

Comments
 (0)