Session, navigation, payment and resilience fixes found in a behaviour review

- http: a request whose 401 arrived after another request's refresh had finished refreshed again;
  it now resends with the new token. A refresh finishing after logout no longer restores tokens.
- logout sends the refresh token current at send time (after a 401 -> refresh it had sent the
  rotated-out one and left the new session alive on the server for 30 days).
- Sign-out/sign-in in another browser tab is followed (storage event).
- Navigator removes its history entries when the main app unmounts (Back did nothing after logout).
- Login: +98 / 0098 / 9xxxxxxxxx phone input (autofill, typing, paste) normalised; incomplete OTP
  is not sent (each wrong guess burns one of five).
- Quiz resumes at the same question after hearts refill instead of restarting; the no-hearts
  screen re-checks while due (phone clock ahead of the server left it stuck at zero).
- Payment return: a pending order is re-checked for about a minute instead of shown as failed;
  the pay button is reset when the page comes back from bfcache.
- Tutor: retry keeps the quiz question id; a network error no longer forgets the conversation;
  daily quota detected by code ai_daily_quota.
- Root error boundary and vite:preloadError reload instead of a white screen; deploy.sh keeps
  hashed assets of earlier builds for 30 days so open apps can still load their chunks.
- Sheet: labelled dialog, focus moves in and back, Escape closes only the top sheet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-18 17:25:40 +03:30
parent f7009b11e8
commit b75127c237
14 changed files with 253 additions and 41 deletions

View File

@@ -41,6 +41,13 @@ if [ -f dist/config.js ] && ! cmp -s dist/config.js public/config.js; then
printf ' config.jsِ ویرایش‌شده‌ی سرور نگه داشته شد.\n'
fi
# فایل‌های buildهای قبلی (نامشان hash دارد و با هم برخورد نمی‌کنند) تا ۳۰ روز می‌مانند: گوشی‌ای که اپ از قبل
# در آن باز است هنوز index.htmlِ قبلی را دارد و با باز کردنِ صفحه‌ای که بار نشده همان نام‌ها را می‌خواهد؛ بی‌این
# ۴۰۴ و صفحه‌ی سفید می‌گرفت. cp -p زمانِ فایل را نگه می‌دارد، پس هر فایل ۳۰ روز بعد از buildِ خودش کنار می‌رود.
if [ -d dist/assets ]; then
find dist/assets -type f -mtime -30 -exec cp -pn {} dist.next/assets/ \;
fi
step "جابه‌جایی"
rm -rf dist.prev
[ -d dist ] && mv dist dist.prev