Skip to content

Commit ee1de97

Browse files
committed
feat: add login requirement for chat and star count to Github link
1 parent 4f454b6 commit ee1de97

4 files changed

Lines changed: 74 additions & 3 deletions

File tree

frontend/src/modules/Home/components/HomeContestChat.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import {
3030
CreateContestDialog,
3131
type CreateContestDialogInitialValues,
3232
} from '@/modules/Contests/components/CreateContestDialog';
33+
import { useSession } from '@/lib/auth-client';
34+
import PleaseLogin from './PleaseLogin';
3335
import type {
3436
AiContestDraft,
3537
AiContestNextResponse,
@@ -56,13 +58,16 @@ function toChatMessages(history: ChatMessage[]) {
5658
export function HomeContestChat() {
5759
const router = useRouter();
5860
const scrollRef = useRef<HTMLDivElement | null>(null);
61+
const { data: session, isPending: isSessionPending } = useSession();
62+
const isLoggedIn = Boolean(session);
5963

6064
const [history, setHistory] = useState<ChatMessage[]>([]);
6165
const [draft, setDraft] = useState<AiContestDraft>({});
6266
const [languagePref, setLanguagePref] = useState<'javascript' | 'typescript'>('typescript');
6367
const [current, setCurrent] = useState<AiContestNextResponse['question'] | null>(null);
6468
const [input, setInput] = useState('');
6569
const [pendingSelection, setPendingSelection] = useState<PendingSelection>(null);
70+
const [loginOpen, setLoginOpen] = useState(false);
6671

6772
const { mutateAsync: nextStep, isPending: isThinking } = useAiContestNext();
6873
const { mutateAsync: previewContest, isPending: isPreviewing } = useAiContestPreview();
@@ -129,6 +134,12 @@ export function HomeContestChat() {
129134
const submitText = async () => {
130135
const text = input.trim();
131136
if (!text) return;
137+
138+
if (!isSessionPending && !isLoggedIn) {
139+
setLoginOpen(true);
140+
return;
141+
}
142+
132143
setInput('');
133144
pushUser(text);
134145

@@ -153,6 +164,11 @@ export function HomeContestChat() {
153164
return;
154165
}
155166

167+
if (!isSessionPending && !isLoggedIn) {
168+
setLoginOpen(true);
169+
return;
170+
}
171+
156172
const chosen = pendingSelection.values;
157173
const labelById =
158174
current.kind === 'single' || current.kind === 'multi'
@@ -208,7 +224,7 @@ export function HomeContestChat() {
208224
}
209225
className={cn(
210226
'w-full resize-none bg-transparent px-5 py-4 text-sm text-foreground placeholder:text-muted-foreground',
211-
'outline-none focus-visible:ring-0 disabled:opacity-60 border-0 pb-14',
227+
'outline-none focus-visible:ring-0 disabled:opacity-60 border-0 pb-20',
212228
)}
213229
/>
214230

@@ -238,7 +254,6 @@ export function HomeContestChat() {
238254
>
239255
<SelectTrigger className="h-7 w-auto rounded-full border border-border/80 bg-muted/30 px-3 py-1 text-xs text-muted-foreground">
240256
<SelectValue />
241-
<CaretDownIcon className="ml-1 size-3.5 opacity-70" />
242257
</SelectTrigger>
243258
<SelectContent>
244259
<SelectItem value="javascript">JavaScript</SelectItem>
@@ -370,6 +385,12 @@ export function HomeContestChat() {
370385
router.push(`/contests/${contestId}`);
371386
}}
372387
/>
388+
389+
<PleaseLogin
390+
open={loginOpen}
391+
onClose={() => setLoginOpen(false)}
392+
onLogin={() => router.push('/login?redirect=/')}
393+
/>
373394
</div>
374395
);
375396
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use client';
2+
3+
import { Button } from '@/components/ui/button';
4+
import {
5+
Dialog,
6+
DialogContent,
7+
DialogDescription,
8+
DialogFooter,
9+
DialogHeader,
10+
DialogTitle,
11+
} from '@/components/ui/dialog';
12+
13+
const PleaseLogin = ({
14+
open = true,
15+
onLogin,
16+
onClose,
17+
}: {
18+
open?: boolean;
19+
onLogin?: () => void;
20+
onClose?: () => void;
21+
}) => {
22+
return (
23+
<Dialog open={open} onOpenChange={(v) => (!v ? onClose?.() : undefined)}>
24+
<DialogContent className="sm:max-w-md">
25+
<DialogHeader>
26+
<DialogTitle>Login required</DialogTitle>
27+
<DialogDescription>
28+
You need to be logged in to send a message to the AI agent. Please log in to continue.
29+
</DialogDescription>
30+
</DialogHeader>
31+
<DialogFooter>
32+
<Button type="button" variant="outline" onClick={onClose}>
33+
Not now
34+
</Button>
35+
<Button type="button" onClick={onLogin} autoFocus>
36+
Login
37+
</Button>
38+
</DialogFooter>
39+
</DialogContent>
40+
</Dialog>
41+
);
42+
};
43+
44+
export default PleaseLogin;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ async function hasSession(req: NextRequest) {
4040
}
4141
}
4242

43-
export async function middleware(req: NextRequest) {
43+
export default async function proxy(req: NextRequest) {
4444
const { pathname } = req.nextUrl;
4545

4646
// Only enforce auth on protected app routes.

frontend/src/shared/Navbar/Navbar.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { BookOpenIcon, GithubLogoIcon, TrophyIcon, UserIcon } from '@phosphor-ic
55
import Section from '../Section/Section';
66
import Profile from './Profile';
77
import { useSession } from '@/lib/auth-client';
8+
import { Star } from 'lucide-react';
89

910
const Navbar = () => {
1011
const { data: session } = useSession();
@@ -53,6 +54,11 @@ const Navbar = () => {
5354
<Button variant="outline" className="w-full">
5455
<link.icon className="size-4" />
5556
{link.label}
57+
{link.label === 'Github' && (
58+
<span className="text-xs text-muted-foreground flex items-center gap-1">
59+
<Star className="size-4 text-yellow-400 fill-yellow-400" />
60+
</span>
61+
)}
5662
</Button>
5763
</Link>
5864
))}

0 commit comments

Comments
 (0)