The original bug was that the WebOTP effect had [subView, triggerVerifyOtp]
as its dependency array. triggerVerifyOtp is a useCallback that depends on
[fetchProfile, onClose] - both of which can change reference during normal
re-renders. Each reference change caused React to re-run the effect, which
called AbortController.abort() on the still-pending OTP Promise. The user
would see the 'Allow' modal, click Allow, but the Promise was already aborted
so nothing happened.
Fix: use triggerVerifyOtpRef (a stable ref) so the WebOTP effect only depends
on [subView] / [step]. The ref is kept up-to-date via a dedicated sync effect.
Additional improvements:
- 400ms delay before auto-verify so user can see the filled boxes
- toEnglishDigits on otp.code to handle Persian numerals in the response
- Same fix applied to LoginModal (handleVerifyOtpWithCode -> verifyOtpRef)
- ClientLayout: implement proper manual scroll restoration using module-level
_wasPopNavigation flag and sessionStorage. Fixes scroll jumping to top/bottom
on Back navigation. Scroll position saved on scroll (debounced) and cleanup,
restored via double-rAF on popstate navigation.
- AuthModal/LoginModal: fix Android back button to push only ONE history sentinel
on modal open (not on every subView/step change). Prevents stacking of extra
history entries. Back in sub-views re-pushes sentinel for next back press.
- AuthModal/LoginModal: remove navigator.credentials.get (WebOTP API) which was
showing an intrusive permission modal. The native autoComplete=one-time-code
on the first OTP input provides the suggestion bar above the keyboard instead.
- AuthModal/LoginModal: add isVerifyingRef guard to prevent double toast on
successful login. Both onComplete callback and form submit were calling
triggerVerifyOtp/handleVerifyOtpWithCode simultaneously.
- AuthModal: remove triggerVerifyOtp from handleOtpChange to eliminate the second
duplicate call (only onComplete now triggers verification).
- OtpInput5: remove maxLength=1 restriction from first input so browser can
inject the full 5-digit OTP code. Multi-char input is already handled by
handleDigitChange which distributes digits across all boxes.
- ProductPage: fix swipe gesture direction in media gallery. Swipe right -> next
item, swipe left -> previous item (matches RTL intuition for Persian users).
- Fix seed-products.ts TS error (implicit any) and BOM handling
- Re-run full seed to restore correct Persian encoding in DB
- Fix productService query→search param mismatch
- Fix IngredientWiki hardcoded port 4000→use settingsStore
- Restore seed-products-data.json from git after accidental corruption