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:
2026-10-05 17:38:18 +03:30
parent 1025321b5f
commit af2e1f57dd
3 changed files with 4 additions and 47 deletions

View File

@@ -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` فقط وقتی سرور در حالتِ توسعه است برمی‌گردد.
* مهلتِ ۴۵ ثانیه، نه ۱۵ِ پیش‌فرض: بک‌اند تا ۱۰+۳۰ ثانیه منتظرِ کاوه‌نگار می‌ماند * مهلتِ ۴۵ ثانیه، نه ۱۵ِ پیش‌فرض: بک‌اند تا ۱۰+۳۰ ثانیه منتظرِ کاوه‌نگار می‌ماند

View File

@@ -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;
}

View File

@@ -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>