Implement registration invite code flow and admin invite codes
This commit is contained in:
@@ -59,6 +59,14 @@ type AuthStatus =
|
||||
|
||||
const FALLBACK_LOGIN_METHODS: AuthLoginMethod[] = ['password'];
|
||||
|
||||
function readInviteCodeFromLocation(): string {
|
||||
const params = new URLSearchParams(window.location.search || '');
|
||||
return (params.get('inviteCode') || params.get('invite_code') || '')
|
||||
.trim()
|
||||
.replace(/[^0-9a-z]/gi, '')
|
||||
.toUpperCase();
|
||||
}
|
||||
|
||||
function normalizeAvailableLoginMethods(
|
||||
methods: AuthLoginMethod[] | null | undefined,
|
||||
): AuthLoginMethod[] {
|
||||
@@ -83,6 +91,10 @@ export function AuthGate({ children }: AuthGateProps) {
|
||||
const [bindingPhone, setBindingPhone] = useState(false);
|
||||
const [wechatLoading, setWechatLoading] = useState(false);
|
||||
const [showLoginModal, setShowLoginModal] = useState(false);
|
||||
const [loginInitialMode, setLoginInitialMode] = useState<
|
||||
'login' | 'register'
|
||||
>('login');
|
||||
const [pendingInviteCode, setPendingInviteCode] = useState('');
|
||||
const [showSettingsModal, setShowSettingsModal] = useState(false);
|
||||
const [settingsEntryMode, setSettingsEntryMode] = useState<
|
||||
'settings' | 'account'
|
||||
@@ -102,6 +114,7 @@ export function AuthGate({ children }: AuthGateProps) {
|
||||
const [changePhoneCaptchaChallenge, setChangePhoneCaptchaChallenge] =
|
||||
useState<AuthCaptchaChallenge | null>(null);
|
||||
const pendingProtectedActionRef = useRef<(() => void) | null>(null);
|
||||
const autoOpenedInviteCodeRef = useRef<string | null>(null);
|
||||
const hasRenderedPlatformContentRef = useRef(false);
|
||||
const canKeepPlatformContentMounted =
|
||||
hasRenderedPlatformContentRef.current &&
|
||||
@@ -169,6 +182,8 @@ export function AuthGate({ children }: AuthGateProps) {
|
||||
const closeLoginModal = useCallback(() => {
|
||||
pendingProtectedActionRef.current = null;
|
||||
setShowLoginModal(false);
|
||||
setLoginInitialMode('login');
|
||||
setPendingInviteCode('');
|
||||
setLoginCaptchaChallenge(null);
|
||||
setError('');
|
||||
}, []);
|
||||
@@ -187,6 +202,8 @@ export function AuthGate({ children }: AuthGateProps) {
|
||||
}
|
||||
|
||||
pendingProtectedActionRef.current = postLoginAction ?? null;
|
||||
setLoginInitialMode('login');
|
||||
setPendingInviteCode('');
|
||||
setShowLoginModal(true);
|
||||
},
|
||||
[readyUser],
|
||||
@@ -224,6 +241,24 @@ export function AuthGate({ children }: AuthGateProps) {
|
||||
openLoginModal();
|
||||
}, [openLoginModal, readyUser]);
|
||||
|
||||
useEffect(() => {
|
||||
if (status !== 'unauthenticated' || readyUser || showLoginModal) {
|
||||
return;
|
||||
}
|
||||
const inviteCode = readInviteCodeFromLocation();
|
||||
if (!inviteCode) {
|
||||
return;
|
||||
}
|
||||
if (autoOpenedInviteCodeRef.current === inviteCode) {
|
||||
return;
|
||||
}
|
||||
autoOpenedInviteCodeRef.current = inviteCode;
|
||||
pendingProtectedActionRef.current = null;
|
||||
setPendingInviteCode(inviteCode);
|
||||
setLoginInitialMode('register');
|
||||
setShowLoginModal(true);
|
||||
}, [readyUser, showLoginModal, status]);
|
||||
|
||||
useEffect(() => {
|
||||
let isActive = true;
|
||||
|
||||
@@ -703,6 +738,8 @@ export function AuthGate({ children }: AuthGateProps) {
|
||||
wechatLoading={wechatLoading}
|
||||
error={error}
|
||||
captchaChallenge={loginCaptchaChallenge}
|
||||
initialMode={loginInitialMode}
|
||||
initialInviteCode={pendingInviteCode}
|
||||
onClose={closeLoginModal}
|
||||
onSendCode={async (phone, scene, captcha) => {
|
||||
setSendingCode(true);
|
||||
@@ -727,14 +764,21 @@ export function AuthGate({ children }: AuthGateProps) {
|
||||
setSendingCode(false);
|
||||
}
|
||||
}}
|
||||
onPhoneSubmit={async (phone, code) => {
|
||||
onPhoneSubmit={async (phone, code, inviteCode) => {
|
||||
setLoggingIn(true);
|
||||
setError('');
|
||||
try {
|
||||
const nextUser = await loginWithPhoneCode(phone, code);
|
||||
const response = await loginWithPhoneCode(
|
||||
phone,
|
||||
code,
|
||||
inviteCode,
|
||||
);
|
||||
setStoredLastLoginPhone(phone);
|
||||
setLoginCaptchaChallenge(null);
|
||||
activateReadyUser(nextUser);
|
||||
if (response.referral && !response.referral.ok) {
|
||||
setError(response.referral.message || '邀请码未绑定');
|
||||
}
|
||||
activateReadyUser(response.user);
|
||||
} catch (loginError) {
|
||||
setError(
|
||||
loginError instanceof Error
|
||||
|
||||
Reference in New Issue
Block a user