Remove redundant files
This commit is contained in:
@@ -1,69 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import { UserOutlined } from "@ant-design/icons";
|
||||
import { Button, Card, Flex, Popover, Space, Tooltip, Typography } from "antd";
|
||||
import AccountSettingsPopoverContent from "./AccountSettingsPopoverContent";
|
||||
import ThemeModeToggle from "./utils/ThemeModeToggle";
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
export default function TopNav({
|
||||
themeMode,
|
||||
onThemeChange,
|
||||
onHome,
|
||||
userEmail,
|
||||
onUserEmailChange,
|
||||
}: {
|
||||
themeMode: "light" | "dark";
|
||||
onThemeChange: (mode: "light" | "dark") => void;
|
||||
onHome: () => void;
|
||||
userEmail: string;
|
||||
onUserEmailChange: (nextUserEmail: string) => void | Promise<string>;
|
||||
}) {
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Card size="small">
|
||||
<Flex justify="space-between" align="center" wrap="wrap" gap={12}>
|
||||
<Button
|
||||
type="text"
|
||||
onClick={onHome}
|
||||
aria-label="Go to home"
|
||||
style={{ paddingInline: 0 }}
|
||||
>
|
||||
<Text style={{ fontSize: 18 }} strong>
|
||||
Lunchtime
|
||||
</Text>
|
||||
</Button>
|
||||
|
||||
<Space size={8} align="center">
|
||||
<ThemeModeToggle
|
||||
themeMode={themeMode}
|
||||
onThemeChange={onThemeChange}
|
||||
/>
|
||||
|
||||
<Popover
|
||||
trigger="click"
|
||||
placement="bottomRight"
|
||||
open={isPopoverOpen}
|
||||
onOpenChange={setIsPopoverOpen}
|
||||
content={
|
||||
<AccountSettingsPopoverContent
|
||||
userEmail={userEmail}
|
||||
onUserEmailChange={onUserEmailChange}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Tooltip title="Account settings">
|
||||
<Button
|
||||
type="text"
|
||||
shape="circle"
|
||||
icon={<UserOutlined />}
|
||||
aria-label="User settings"
|
||||
/>
|
||||
</Tooltip>
|
||||
</Popover>
|
||||
</Space>
|
||||
</Flex>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Alert, Button, Flex, Form, Input, Modal, Space, Typography } from "antd";
|
||||
import ThemeModeToggle from "./utils/ThemeModeToggle";
|
||||
|
||||
type ThemeMode = "light" | "dark";
|
||||
type EmailLookupState = "idle" | "checking" | "exists" | "new";
|
||||
|
||||
export default function WelcomeOnboardingModal({
|
||||
open,
|
||||
themeMode,
|
||||
onThemeChange,
|
||||
onCheckAccountExists,
|
||||
onCreateAccount,
|
||||
onMigrateAccount,
|
||||
}: {
|
||||
open: boolean;
|
||||
themeMode: ThemeMode;
|
||||
onThemeChange: (mode: ThemeMode) => void;
|
||||
onCheckAccountExists: (email: string) => Promise<boolean>;
|
||||
onCreateAccount: (email: string) => void | Promise<void>;
|
||||
onMigrateAccount: (email: string) => void | Promise<void>;
|
||||
}) {
|
||||
const [form] = Form.useForm<{ email: string }>();
|
||||
const [lookupState, setLookupState] = useState<EmailLookupState>("idle");
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [hasSentEmail, setHasSentEmail] = useState(false);
|
||||
const lookupTimerRef = useRef<number | null>(null);
|
||||
const lookupRequestIdRef = useRef(0);
|
||||
|
||||
const normalizeEmail = (email: string) => email.trim().toLowerCase();
|
||||
|
||||
const isValidEmail = (email: string) =>
|
||||
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
||||
|
||||
const resolveAccountExists = async (email: string) => {
|
||||
const normalized = normalizeEmail(email);
|
||||
if (!normalized || !isValidEmail(normalized)) {
|
||||
setLookupState("idle");
|
||||
return null;
|
||||
}
|
||||
|
||||
const requestId = ++lookupRequestIdRef.current;
|
||||
setLookupState("checking");
|
||||
|
||||
try {
|
||||
const exists = await onCheckAccountExists(normalized);
|
||||
if (requestId !== lookupRequestIdRef.current) {
|
||||
return null;
|
||||
}
|
||||
|
||||
setLookupState(exists ? "exists" : "new");
|
||||
return exists;
|
||||
} catch {
|
||||
if (requestId === lookupRequestIdRef.current) {
|
||||
setLookupState("idle");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
form.resetFields();
|
||||
setLookupState("idle");
|
||||
setIsSubmitting(false);
|
||||
setHasSentEmail(false);
|
||||
lookupRequestIdRef.current += 1;
|
||||
if (lookupTimerRef.current) {
|
||||
window.clearTimeout(lookupTimerRef.current);
|
||||
lookupTimerRef.current = null;
|
||||
}
|
||||
}
|
||||
}, [form, open]);
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
if (lookupTimerRef.current) {
|
||||
window.clearTimeout(lookupTimerRef.current);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const buttonLabel = useMemo(() => {
|
||||
if (hasSentEmail) {
|
||||
return "Email sent";
|
||||
}
|
||||
if (lookupState === "exists") {
|
||||
return "Migrate account";
|
||||
}
|
||||
if (lookupState === "checking") {
|
||||
return "Checking account...";
|
||||
}
|
||||
if (lookupState === "new") {
|
||||
return "Create new account";
|
||||
}
|
||||
return "Continue";
|
||||
}, [hasSentEmail, lookupState]);
|
||||
|
||||
const isLookupResolved = lookupState === "exists" || lookupState === "new";
|
||||
|
||||
const helperText = useMemo(() => {
|
||||
if (lookupState === "exists") {
|
||||
return "Log in using this email.";
|
||||
}
|
||||
if (lookupState === "new") {
|
||||
return "Register using this email.";
|
||||
}
|
||||
return "";
|
||||
}, [lookupState]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
maskClosable={false}
|
||||
keyboard={false}
|
||||
closable={false}
|
||||
footer={null}
|
||||
centered
|
||||
>
|
||||
<Space direction="vertical" size={14} style={{ width: "100%" }}>
|
||||
<Flex align="center" justify="space-between" style={{ width: "100%" }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
Welcome to Lunchtime
|
||||
</Typography.Title>
|
||||
<ThemeModeToggle themeMode={themeMode} onThemeChange={onThemeChange} />
|
||||
</Flex>
|
||||
|
||||
<Typography.Paragraph style={{ marginBottom: 6 }}>
|
||||
Enter your account email to continue. We'll automatically detect whether to create a new account or migrate an existing one.
|
||||
</Typography.Paragraph>
|
||||
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onValuesChange={(_changedValues, values) => {
|
||||
if (hasSentEmail) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentEmail = String(values.email || "");
|
||||
if (lookupTimerRef.current) {
|
||||
window.clearTimeout(lookupTimerRef.current);
|
||||
lookupTimerRef.current = null;
|
||||
}
|
||||
|
||||
lookupTimerRef.current = window.setTimeout(() => {
|
||||
void resolveAccountExists(currentEmail);
|
||||
}, 350);
|
||||
}}
|
||||
onFinish={async (values) => {
|
||||
if (hasSentEmail) {
|
||||
return;
|
||||
}
|
||||
|
||||
const normalizedEmail = normalizeEmail(values.email);
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
let exists = lookupState === "exists";
|
||||
if (lookupState !== "exists" && lookupState !== "new") {
|
||||
const checked = await resolveAccountExists(normalizedEmail);
|
||||
exists = checked === true;
|
||||
}
|
||||
|
||||
if (exists) {
|
||||
await onMigrateAccount(normalizedEmail);
|
||||
} else {
|
||||
await onCreateAccount(normalizedEmail);
|
||||
}
|
||||
|
||||
setHasSentEmail(true);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
label="Email"
|
||||
name="email"
|
||||
rules={[
|
||||
{ required: true, message: "Email cannot be empty." },
|
||||
{ type: "email", message: "Enter a valid email address." },
|
||||
]}
|
||||
extra={helperText || undefined}
|
||||
>
|
||||
<Input
|
||||
placeholder="alex@example.com"
|
||||
autoFocus
|
||||
maxLength={320}
|
||||
disabled={hasSentEmail}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{hasSentEmail ? (
|
||||
<Alert
|
||||
type="info"
|
||||
showIcon
|
||||
description="Email sent! Please check your inbox and spam folder for the confirmation email."
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{hasSentEmail ? (
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => {
|
||||
setHasSentEmail(false);
|
||||
setLookupState("idle");
|
||||
}}
|
||||
style={{ paddingInline: 0 }}
|
||||
>
|
||||
Change email
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
block
|
||||
loading={!hasSentEmail && (isSubmitting || lookupState === "checking")}
|
||||
disabled={hasSentEmail || !isLookupResolved}
|
||||
>
|
||||
{buttonLabel}
|
||||
</Button>
|
||||
</Form>
|
||||
</Space>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user