fix(auth): enable dev email login testing
This commit is contained in:
@@ -23,7 +23,13 @@ import { AuthOtherOptionsDialog } from "./auth-other-options-dialog";
|
|||||||
import { AuthProviderIcon } from "./auth-provider-icon";
|
import { AuthProviderIcon } from "./auth-provider-icon";
|
||||||
import styles from "./auth-facebook-panel.module.css";
|
import styles from "./auth-facebook-panel.module.css";
|
||||||
|
|
||||||
export function AuthFacebookPanel() {
|
export interface AuthFacebookPanelProps {
|
||||||
|
onSwitchToEmail: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AuthFacebookPanel({
|
||||||
|
onSwitchToEmail,
|
||||||
|
}: AuthFacebookPanelProps) {
|
||||||
const [showOptions, setShowOptions] = useState(false);
|
const [showOptions, setShowOptions] = useState(false);
|
||||||
const { isLoading, errorMessage } = useAuthState();
|
const { isLoading, errorMessage } = useAuthState();
|
||||||
const authDispatch = useAuthDispatch();
|
const authDispatch = useAuthDispatch();
|
||||||
@@ -93,6 +99,7 @@ export function AuthFacebookPanel() {
|
|||||||
mode="facebook"
|
mode="facebook"
|
||||||
onFacebook={() => handleFacebook()}
|
onFacebook={() => handleFacebook()}
|
||||||
onGoogle={() => handleGoogle()}
|
onGoogle={() => handleGoogle()}
|
||||||
|
onEmail={onSwitchToEmail}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,21 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
/**
|
|
||||||
* "其他登录方式" 底部弹层
|
|
||||||
*
|
|
||||||
* 原始 Dart: lib/ui/auth/widgets/auth_other_options_dialog.dart
|
|
||||||
* (showModalBottomSheet:白卡 + 顶部圆角 radius28 + drag handle)
|
|
||||||
*
|
|
||||||
* 视觉规格(与 Dart 对齐):
|
|
||||||
* - 固定底部、宽 100%(≤500px)、顶部圆角 28px
|
|
||||||
* - drag handle 40×4 灰胶囊
|
|
||||||
* - 标题 "Other Sign-in Options" 24px bold 深色
|
|
||||||
* - email 模式下渲染 Facebook 按钮(40px 白胶囊 + 边框 + 图标)
|
|
||||||
* - 始终渲染 Google 按钮
|
|
||||||
* - Cancel 文本按钮
|
|
||||||
*/
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
import { MdEmail } from "react-icons/md";
|
||||||
|
|
||||||
import { BottomSheet } from "@/app/_components/core/bottom-sheet";
|
import { BottomSheet } from "@/app/_components/core/bottom-sheet";
|
||||||
|
import { AppEnvUtil } from "@/utils";
|
||||||
|
|
||||||
import { AuthProviderIcon } from "./auth-provider-icon";
|
import { AuthProviderIcon } from "./auth-provider-icon";
|
||||||
import { AuthSocialButton } from "./auth-social-button";
|
import { AuthSocialButton } from "./auth-social-button";
|
||||||
@@ -26,6 +14,7 @@ export interface AuthOtherOptionsDialogProps {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onFacebook: () => void;
|
onFacebook: () => void;
|
||||||
onGoogle: () => void;
|
onGoogle: () => void;
|
||||||
|
onEmail?: () => void;
|
||||||
/** 当前面板模式:facebook 模式下显示 Email + Google;email 模式下显示 Facebook + Google。 */
|
/** 当前面板模式:facebook 模式下显示 Email + Google;email 模式下显示 Facebook + Google。 */
|
||||||
mode: "facebook" | "email";
|
mode: "facebook" | "email";
|
||||||
/** 自定义子节点(如嵌入 GIS 按钮) */
|
/** 自定义子节点(如嵌入 GIS 按钮) */
|
||||||
@@ -37,9 +26,13 @@ export function AuthOtherOptionsDialog({
|
|||||||
onClose,
|
onClose,
|
||||||
onFacebook,
|
onFacebook,
|
||||||
onGoogle,
|
onGoogle,
|
||||||
|
onEmail,
|
||||||
mode,
|
mode,
|
||||||
googleSlot,
|
googleSlot,
|
||||||
}: AuthOtherOptionsDialogProps) {
|
}: AuthOtherOptionsDialogProps) {
|
||||||
|
const showDevEmailOption =
|
||||||
|
AppEnvUtil.isDevelopment() && mode === "facebook" && onEmail;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomSheet
|
<BottomSheet
|
||||||
open={open}
|
open={open}
|
||||||
@@ -62,6 +55,18 @@ export function AuthOtherOptionsDialog({
|
|||||||
</AuthSocialButton>
|
</AuthSocialButton>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
{showDevEmailOption ? (
|
||||||
|
<AuthSocialButton
|
||||||
|
icon={<MdEmail size={20} color="var(--color-auth-text-primary)" />}
|
||||||
|
onClick={() => {
|
||||||
|
onClose();
|
||||||
|
onEmail();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Email Sign In
|
||||||
|
</AuthSocialButton>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{googleSlot ? (
|
{googleSlot ? (
|
||||||
<div className={styles.googleSlot}>{googleSlot}</div>
|
<div className={styles.googleSlot}>{googleSlot}</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export function AuthPanel() {
|
|||||||
|
|
||||||
const switchToFacebook = () =>
|
const switchToFacebook = () =>
|
||||||
dispatch({ type: "AuthPanelModeChanged", mode: "facebook" });
|
dispatch({ type: "AuthPanelModeChanged", mode: "facebook" });
|
||||||
|
const switchToEmail = () =>
|
||||||
|
dispatch({ type: "AuthPanelModeChanged", mode: "email" });
|
||||||
|
|
||||||
const handleBack = () => {
|
const handleBack = () => {
|
||||||
if (state.authPanelMode === "email") {
|
if (state.authPanelMode === "email") {
|
||||||
@@ -32,7 +34,7 @@ export function AuthPanel() {
|
|||||||
<AuthBackButton onClick={handleBack} />
|
<AuthBackButton onClick={handleBack} />
|
||||||
|
|
||||||
{state.authPanelMode === "facebook" ? (
|
{state.authPanelMode === "facebook" ? (
|
||||||
<AuthFacebookPanel />
|
<AuthFacebookPanel onSwitchToEmail={switchToEmail} />
|
||||||
) : (
|
) : (
|
||||||
<AuthEmailPanel
|
<AuthEmailPanel
|
||||||
onSwitchToFacebook={() => {
|
onSwitchToFacebook={() => {
|
||||||
|
|||||||
@@ -14,9 +14,11 @@ import { AuthTextField } from "./auth-text-field";
|
|||||||
import { AuthPrimaryButton } from "./auth-primary-button";
|
import { AuthPrimaryButton } from "./auth-primary-button";
|
||||||
import { AuthErrorMessage } from "./auth-error-message";
|
import { AuthErrorMessage } from "./auth-error-message";
|
||||||
import styles from "./email-form.module.css";
|
import styles from "./email-form.module.css";
|
||||||
import { Logger } from "@/utils";
|
import { AppEnvUtil, Logger } from "@/utils";
|
||||||
|
|
||||||
const log = new Logger("AppAuthComponentsEmailLoginForm");
|
const log = new Logger("AppAuthComponentsEmailLoginForm");
|
||||||
|
const DEV_DEFAULT_EMAIL = "chanwillian0@gmail.com";
|
||||||
|
const DEV_DEFAULT_PASSWORD = "12345678";
|
||||||
|
|
||||||
export interface EmailLoginFormProps {
|
export interface EmailLoginFormProps {
|
||||||
globalError?: string | null;
|
globalError?: string | null;
|
||||||
@@ -28,8 +30,12 @@ export function EmailLoginForm({
|
|||||||
isLoading,
|
isLoading,
|
||||||
}: EmailLoginFormProps) {
|
}: EmailLoginFormProps) {
|
||||||
const dispatch = useAuthDispatch();
|
const dispatch = useAuthDispatch();
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState(() =>
|
||||||
const [password, setPassword] = useState("");
|
AppEnvUtil.isDevelopment() ? DEV_DEFAULT_EMAIL : "",
|
||||||
|
);
|
||||||
|
const [password, setPassword] = useState(() =>
|
||||||
|
AppEnvUtil.isDevelopment() ? DEV_DEFAULT_PASSWORD : "",
|
||||||
|
);
|
||||||
// 本地校验错误(client-side)—— 校验失败时展示第一个非 null 的错误
|
// 本地校验错误(client-side)—— 校验失败时展示第一个非 null 的错误
|
||||||
// 优先级:local validationError > server globalError
|
// 优先级:local validationError > server globalError
|
||||||
const [validationError, setValidationError] = useState<string | null>(null);
|
const [validationError, setValidationError] = useState<string | null>(null);
|
||||||
|
|||||||
@@ -1,21 +1,12 @@
|
|||||||
import { SubscriptionScreen } from "@/app/subscription/subscription-screen";
|
import { Suspense } from "react";
|
||||||
|
|
||||||
import type { SubscriptionType } from "./subscription-screen";
|
import { SubscriptionPageClient } from "./subscription-page-client";
|
||||||
|
import { SubscriptionScreen } from "./subscription-screen";
|
||||||
|
|
||||||
interface SubscriptionPageProps {
|
export default function SubscriptionPage() {
|
||||||
searchParams: Promise<{
|
return (
|
||||||
type?: string | string[];
|
<Suspense fallback={<SubscriptionScreen subscriptionType="vip" />}>
|
||||||
}>;
|
<SubscriptionPageClient />
|
||||||
}
|
</Suspense>
|
||||||
|
);
|
||||||
function toSubscriptionType(value: string | string[] | undefined): SubscriptionType {
|
|
||||||
const type = Array.isArray(value) ? value[0] : value;
|
|
||||||
return type === "voice" ? "voice" : "vip";
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function SubscriptionPage({
|
|
||||||
searchParams,
|
|
||||||
}: SubscriptionPageProps) {
|
|
||||||
const params = await searchParams;
|
|
||||||
return <SubscriptionScreen subscriptionType={toSubscriptionType(params.type)} />;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
|
||||||
|
import {
|
||||||
|
SubscriptionScreen,
|
||||||
|
type SubscriptionType,
|
||||||
|
} from "./subscription-screen";
|
||||||
|
|
||||||
|
function toSubscriptionType(value: string | null): SubscriptionType {
|
||||||
|
return value === "voice" ? "voice" : "vip";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SubscriptionPageClient() {
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const subscriptionType = toSubscriptionType(searchParams.get("type"));
|
||||||
|
|
||||||
|
return <SubscriptionScreen subscriptionType={subscriptionType} />;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user