fix: Qibla not detecting device compass on iOS and Android

Two real bugs, both explaining 'didn't detect compass':

1. iOS: DeviceOrientationEvent.requestPermission() was called *after*
   the async geolocation round-trip resolved. Safari's user-activation
   grant for sensor permissions expires almost immediately, so by the
   time a GPS fix comes back (often several seconds), the permission
   call silently fails with no prompt ever shown. Now requested
   synchronously inside the button's click handler, before geolocation
   starts.

2. Android: the heading was only accepted when e.absolute === true, but
   many Android browsers fire plain 'deviceorientation' with
   absolute:false even though alpha is a perfectly usable heading —
   compass detection silently never succeeded on those devices at all.
   Now accepts alpha regardless of the absolute flag, falling back to
   it when webkitCompassHeading (iOS) isn't present.

Also fixed a duplicate-listener bug: clicking 'Refresh location'
repeatedly re-registered deviceorientation listeners without removing
the previous ones, stacking duplicates on every retry.

Verified live: dispatched a synthetic non-absolute deviceorientation
event (the exact Android failure shape) against the deployed app —
compass detection now activates correctly where it previously stayed
silently stuck on the no-sensor fallback. Added as a permanent
regression assertion in e2e-khairat-lifestyle.cjs (16/16).
This commit is contained in:
2026-08-14 15:23:17 +08:00
parent 23f32872a5
commit 9cb0793347
2 changed files with 56 additions and 16 deletions
+16
View File
@@ -60,6 +60,22 @@ async function main() {
const distanceVisible = await page.locator('.distance-note', { hasText: 'km to Makkah' }).isVisible().catch(() => false);
record('Qibla: shows distance to Makkah', distanceVisible);
// Regression guard: many Android browsers fire plain 'deviceorientation'
// with absolute:false even though alpha is a usable heading — a prior
// version required absolute:true and silently never detected the compass
// on those devices. Simulate exactly that event shape.
const staticNoteBeforeSensor = await page.locator('.static-note').isVisible().catch(() => false);
record('Qibla: shows the no-sensor fallback note before any orientation event', staticNoteBeforeSensor);
await page.evaluate(() => {
const evt = new Event('deviceorientation');
Object.defineProperty(evt, 'alpha', { value: 90 });
Object.defineProperty(evt, 'absolute', { value: false });
window.dispatchEvent(evt);
});
await page.waitForTimeout(500);
const staticNoteAfterSensor = await page.locator('.static-note').isVisible().catch(() => false);
record('Qibla: accepts a non-absolute deviceorientation event as a valid compass reading (Android)', !staticNoteAfterSensor);
// ── Prayer Times ──
await gotoTab(page, 'Prayer Times');
await page.waitForTimeout(500);