forked from Tadrisino/MathApp_frontend
Login: no guest option; every user signs in with a phone number
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -29,10 +29,6 @@ export const api = {
|
|||||||
auth: {
|
auth: {
|
||||||
isSignedIn: () => Boolean(http.storage.get()?.token),
|
isSignedIn: () => Boolean(http.storage.get()?.token),
|
||||||
|
|
||||||
/** مهمان: فقط با شناسهی دستگاه. refreshToken هم میگیرد، پس بعد از یک ساعت نمیافتد. */
|
|
||||||
guest: (deviceId: string) =>
|
|
||||||
http.post<T.AuthResponse>('/auth/session', { type: 'guest', deviceId }).then(saveTokens),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* گامِ ۱: پیامکِ کد. `devOtp` فقط وقتی سرور در حالتِ توسعه است برمیگردد.
|
* گامِ ۱: پیامکِ کد. `devOtp` فقط وقتی سرور در حالتِ توسعه است برمیگردد.
|
||||||
* مهلتِ ۴۵ ثانیه، نه ۱۵ِ پیشفرض: بکاند تا ۱۰+۳۰ ثانیه منتظرِ کاوهنگار میماند
|
* مهلتِ ۴۵ ثانیه، نه ۱۵ِ پیشفرض: بکاند تا ۱۰+۳۰ ثانیه منتظرِ کاوهنگار میماند
|
||||||
|
|||||||
@@ -24,21 +24,8 @@ export const store = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const KEYS = {
|
export const KEYS = {
|
||||||
deviceId: 'deviceId',
|
|
||||||
onboarded: 'tadrisino.onboarded',
|
onboarded: 'tadrisino.onboarded',
|
||||||
conversationId: 'tadrisino.conversationId',
|
conversationId: 'tadrisino.conversationId',
|
||||||
sound: 'tadrisino.sound',
|
sound: 'tadrisino.sound',
|
||||||
referralCode: 'tadrisino.referralCode',
|
referralCode: 'tadrisino.referralCode',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
/** شناسهی ثابتِ این مرورگر برای ورودِ مهمان. */
|
|
||||||
export function getDeviceId(): string {
|
|
||||||
let id = store.get(KEYS.deviceId);
|
|
||||||
if (!id) {
|
|
||||||
id = globalThis.crypto && 'randomUUID' in crypto
|
|
||||||
? crypto.randomUUID()
|
|
||||||
: `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
||||||
store.set(KEYS.deviceId, id);
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,17 +5,16 @@ import { InlineError, PrimaryButton } from '@/components/ui';
|
|||||||
import { SeyarMascot } from '@/mascots/SeyarMascot';
|
import { SeyarMascot } from '@/mascots/SeyarMascot';
|
||||||
import { friendlyError } from '@/lib/errors';
|
import { friendlyError } from '@/lib/errors';
|
||||||
import { digitsOnly, toFa } from '@/lib/format';
|
import { digitsOnly, toFa } from '@/lib/format';
|
||||||
import { getDeviceId } from '@/lib/storage';
|
|
||||||
|
|
||||||
const OTP_LENGTH = 6;
|
const OTP_LENGTH = 6;
|
||||||
/** همان cooldownِ سرور (OTP_REQUEST_PHONE_COOLDOWN_SECONDS). */
|
/** همان cooldownِ سرور (OTP_REQUEST_PHONE_COOLDOWN_SECONDS). */
|
||||||
const RESEND_SECONDS = 120;
|
const RESEND_SECONDS = 120;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ورود با شماره (OTP) یا بهعنوانِ مهمان.
|
* ورود با شماره (OTP). ورودِ مهمان نداریم: هر کاربر حداقل با شماره ثبتنام میکند.
|
||||||
*
|
*
|
||||||
* `upgrade` برای مهمانی است که از صفحهی «من» میخواهد شماره ثبت کند؛ آنجا
|
* `upgrade` برای حسابِ مهمانِ قدیمی است (دیگر ساخته نمیشود) که از صفحهی «من»
|
||||||
* گزینهی مهمان نیست و سرور همان کاربر را نگه میدارد.
|
* شماره ثبت میکند؛ سرور همان کاربر را نگه میدارد.
|
||||||
*/
|
*/
|
||||||
export function LoginScreen({
|
export function LoginScreen({
|
||||||
onDone,
|
onDone,
|
||||||
@@ -30,7 +29,7 @@ export function LoginScreen({
|
|||||||
const [phone, setPhone] = useState('');
|
const [phone, setPhone] = useState('');
|
||||||
const [otp, setOtp] = useState('');
|
const [otp, setOtp] = useState('');
|
||||||
const [devOtp, setDevOtp] = useState<string | null>(null);
|
const [devOtp, setDevOtp] = useState<string | null>(null);
|
||||||
const [loading, setLoading] = useState<'login' | 'guest' | null>(null);
|
const [loading, setLoading] = useState<'login' | null>(null);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const [resendIn, setResendIn] = useState(0);
|
const [resendIn, setResendIn] = useState(0);
|
||||||
/** درخواستِ پیامک هنوز جواب نداده؛ جدا از `loading` تا واردکردنِ کد قفل نشود. */
|
/** درخواستِ پیامک هنوز جواب نداده؛ جدا از `loading` تا واردکردنِ کد قفل نشود. */
|
||||||
@@ -101,21 +100,6 @@ export function LoginScreen({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function continueAsGuest() {
|
|
||||||
setError('');
|
|
||||||
setLoading('guest');
|
|
||||||
try {
|
|
||||||
await api.auth.guest(getDeviceId());
|
|
||||||
onDone();
|
|
||||||
} catch (err) {
|
|
||||||
// قبلاً اینجا یک «مهمانِ محلی» بدونِ توکن ساخته میشد و بعد همهی
|
|
||||||
// درخواستها بیصدا شکست میخوردند. حالا همینجا میگوییم چه شده.
|
|
||||||
setError(friendlyError(err, 'ورودِ مهمان انجام نشد، دوباره امتحان کن'));
|
|
||||||
} finally {
|
|
||||||
setLoading(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOtpChange(value: string) {
|
function onOtpChange(value: string) {
|
||||||
const code = digitsOnly(value, OTP_LENGTH);
|
const code = digitsOnly(value, OTP_LENGTH);
|
||||||
setOtp(code);
|
setOtp(code);
|
||||||
@@ -216,16 +200,6 @@ export function LoginScreen({
|
|||||||
>
|
>
|
||||||
{step === 'phone' ? 'دریافتِ کدِ تأیید' : 'ورود'}
|
{step === 'phone' ? 'دریافتِ کدِ تأیید' : 'ورود'}
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
{!upgrade && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => void continueAsGuest()}
|
|
||||||
disabled={loading !== null}
|
|
||||||
className="py-2 text-center text-sm font-bold text-muted disabled:opacity-40"
|
|
||||||
>
|
|
||||||
{loading === 'guest' ? 'در حالِ ورود…' : 'فعلاً مهمان باشم'}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user