Przebudowa - code review aplikaacji, podział na moduły.
This commit is contained in:
@@ -0,0 +1,639 @@
|
||||
import { endpoints, geoBypassHosts } from "./config.js";
|
||||
import { trackEvent } from "./analytics.js";
|
||||
import {
|
||||
runPendingProtectedAction,
|
||||
setGeoGateHandler,
|
||||
showBottomNav,
|
||||
switchTabInternal,
|
||||
updateNavAccessState,
|
||||
} from "./access.js";
|
||||
import {
|
||||
prefetchOpenBills,
|
||||
refreshGuestPendingActions,
|
||||
startGuestPendingPoll,
|
||||
} from "./queue.js";
|
||||
import { fetchOrders, resolveTableLabel, updateUI } from "./orders.js";
|
||||
import {
|
||||
getAppAccessLevel,
|
||||
setAppAccessLevel,
|
||||
setPendingProtectedAction,
|
||||
} from "./state.js";
|
||||
|
||||
// USER PROFILE LOGIC
|
||||
const userProfileKey = "karczma_user_profile";
|
||||
const USER_PROFILE_EXPIRE_MS = 180 * 24 * 60 * 60 * 1000; // ~6 months
|
||||
|
||||
export function initUserProfile() {
|
||||
return; // Funkcja tymczasowo wyłączona
|
||||
try {
|
||||
const raw = localStorage.getItem(userProfileKey);
|
||||
let profile = null;
|
||||
if (raw) {
|
||||
profile = JSON.parse(raw);
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
// Check if profile exists and is valid
|
||||
if (profile) {
|
||||
if (profile.declined) {
|
||||
// User declined in the past, don't ask again.
|
||||
return;
|
||||
}
|
||||
|
||||
// If expired, maybe we want to ask again or we could just keep it if they didn't decline.
|
||||
// The user said: "trzymamy przez wiele miesięcy (odnawiamy datę przy każdej wizycie)"
|
||||
if (now - profile.lastVisit > USER_PROFILE_EXPIRE_MS) {
|
||||
// Profile expired. Ask again.
|
||||
showNameDialog();
|
||||
} else {
|
||||
// Profile valid, renew date and show greeting
|
||||
profile.lastVisit = now;
|
||||
localStorage.setItem(userProfileKey, JSON.stringify(profile));
|
||||
showGreeting(profile.name, profile.firstVisit || profile.lastVisit);
|
||||
}
|
||||
} else {
|
||||
// No profile, ask for name
|
||||
showNameDialog();
|
||||
}
|
||||
} catch (err) {
|
||||
// If error parsing, ask again
|
||||
showNameDialog();
|
||||
}
|
||||
}
|
||||
|
||||
function showNameDialog() {
|
||||
const modal = document.getElementById("nameModal");
|
||||
if (modal) {
|
||||
modal.classList.add("active");
|
||||
document.body.style.overflow = "hidden";
|
||||
}
|
||||
}
|
||||
|
||||
function hideNameDialog() {
|
||||
const modal = document.getElementById("nameModal");
|
||||
if (modal) {
|
||||
modal.classList.remove("active");
|
||||
document.body.style.overflow = "";
|
||||
}
|
||||
}
|
||||
|
||||
export function saveUserName() {
|
||||
const input = document.getElementById("userNameInput").value.trim();
|
||||
if (input) {
|
||||
const now = Date.now();
|
||||
const profile = { name: input, firstVisit: now, lastVisit: now, declined: false };
|
||||
localStorage.setItem(userProfileKey, JSON.stringify(profile));
|
||||
hideNameDialog();
|
||||
showGreeting(profile.name, profile.firstVisit);
|
||||
}
|
||||
}
|
||||
|
||||
export function declineUserName() {
|
||||
const profile = { name: null, firstVisit: Date.now(), lastVisit: Date.now(), declined: true };
|
||||
localStorage.setItem(userProfileKey, JSON.stringify(profile));
|
||||
hideNameDialog();
|
||||
}
|
||||
|
||||
function showGreeting(name, firstVisitTime) {
|
||||
const banner = document.getElementById("greetingBanner");
|
||||
if (banner && name) {
|
||||
const isToday = new Date(firstVisitTime).toDateString() === new Date().toDateString();
|
||||
|
||||
if (isToday) {
|
||||
banner.innerHTML = `Cześć ${name}, życzymy pysznego posiłku!`;
|
||||
} else {
|
||||
banner.innerHTML = `Witaj ${name}, super że do nas wracasz!`;
|
||||
}
|
||||
banner.style.display = "block";
|
||||
}
|
||||
}
|
||||
|
||||
function isIOSDevice() {
|
||||
return (
|
||||
/iPad|iPhone|iPod/.test(navigator.userAgent) ||
|
||||
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)
|
||||
);
|
||||
}
|
||||
|
||||
const GEO_GATE_LABELS = {
|
||||
status: "status zamówienia",
|
||||
waiter: "wezwanie kelnera",
|
||||
bill: "prośbę o rachunek",
|
||||
};
|
||||
|
||||
const GEO_DEFAULT_LEAD =
|
||||
"Przeglądaj menu od razu — albo potwierdź, że jesteś u nas, aby wezwać kelnera, śledzić zamówienie i poprosić o rachunek.";
|
||||
|
||||
function setGeoLead(html) {
|
||||
const el = document.getElementById("geoLead");
|
||||
if (el) el.innerHTML = html;
|
||||
}
|
||||
|
||||
function setGeoStatus(html, { error = false, info = false } = {}) {
|
||||
const el = document.getElementById("geoMsg");
|
||||
if (!el) return;
|
||||
el.innerHTML = html || "";
|
||||
el.classList.toggle("is-error", error);
|
||||
el.classList.toggle("is-info", info);
|
||||
}
|
||||
|
||||
function showGeoInstructions(html) {
|
||||
const el = document.getElementById("geoInstructions");
|
||||
if (!el) return;
|
||||
el.innerHTML = html || "";
|
||||
el.classList.toggle("hidden", !html);
|
||||
}
|
||||
|
||||
function hideGeoInstructions() {
|
||||
showGeoInstructions("");
|
||||
}
|
||||
|
||||
function setGeoActionBusy(busy) {
|
||||
const btn = document.getElementById("geoActionBtn");
|
||||
if (!btn) return;
|
||||
btn.disabled = false;
|
||||
btn.setAttribute("aria-busy", busy ? "true" : "false");
|
||||
if (busy) {
|
||||
setGeoActionLabel("Sprawdzanie…");
|
||||
}
|
||||
}
|
||||
|
||||
function isGeoPermissionDenied(error) {
|
||||
return Number(error?.code) === 1;
|
||||
}
|
||||
|
||||
function setGeoActionLabel(text) {
|
||||
const btn = document.getElementById("geoActionBtn");
|
||||
if (!btn) return;
|
||||
const main = btn.querySelector(".geo-btn-main");
|
||||
if (main) main.textContent = text;
|
||||
else btn.textContent = text;
|
||||
}
|
||||
|
||||
function getGeoPermissionInstructions() {
|
||||
if (isIOSDevice()) {
|
||||
return `<b>iPhone (Safari):</b><br>
|
||||
1. Kliknij <b>aA</b> po lewej stronie paska adresu.<br>
|
||||
2. Wybierz <b>Ustawienia witryny</b>.<br>
|
||||
3. Ustaw <b>Położenie</b> na „Zapytaj” lub „Pozwalaj”.<br>
|
||||
4. Odśwież stronę.<br><br>
|
||||
<i>Lokalizacja działa tylko przez bezpieczne <b>https://</b>.</i>`;
|
||||
}
|
||||
|
||||
return `<b>Android / Chrome:</b><br>
|
||||
1. Kliknij ikonę <b>kłódki</b> obok adresu strony.<br>
|
||||
2. W <b>Uprawnieniach</b> zmień Lokalizację na „Zezwalaj”.<br>
|
||||
3. Odśwież stronę.`;
|
||||
}
|
||||
|
||||
let geoMenuButtonMode = "menu_only";
|
||||
|
||||
export function handleGeoMenuClick() {
|
||||
if (geoMenuButtonMode === "back_to_menu") {
|
||||
document.getElementById("geoScreen")?.classList.add("hidden");
|
||||
return;
|
||||
}
|
||||
enterMenuOnlyMode();
|
||||
}
|
||||
|
||||
export function retryGeolocation() {
|
||||
if (shouldBypassGeolocationHost()) {
|
||||
bypassGeolocation("trusted_host", { host: window.location.hostname });
|
||||
return;
|
||||
}
|
||||
|
||||
initGeolocationAfterBypassChecks({ userInitiated: true }).catch((err) => {
|
||||
console.error("[GEO] retry failed", err);
|
||||
showGeoPermissionBlockedState();
|
||||
});
|
||||
}
|
||||
|
||||
export function bindGeoScreenButtons() {
|
||||
document.getElementById("geoMenuOnlyBtn")?.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
handleGeoMenuClick();
|
||||
});
|
||||
|
||||
document.getElementById("geoActionBtn")?.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
retryGeolocation();
|
||||
});
|
||||
}
|
||||
|
||||
function configureGeoSecondaryButton(mode) {
|
||||
const menuOnlyBtn = document.getElementById("geoMenuOnlyBtn");
|
||||
if (!menuOnlyBtn) return;
|
||||
|
||||
geoMenuButtonMode = mode;
|
||||
const mainEl = menuOnlyBtn.querySelector(".geo-btn-main");
|
||||
const subEl = menuOnlyBtn.querySelector(".geo-btn-sub");
|
||||
|
||||
if (mode === "back_to_menu") {
|
||||
menuOnlyBtn.style.display = "";
|
||||
if (mainEl) mainEl.textContent = "Wróć do menu";
|
||||
if (subEl) {
|
||||
subEl.textContent = "";
|
||||
subEl.style.display = "none";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
menuOnlyBtn.style.display = "";
|
||||
if (mainEl) mainEl.textContent = "Przejdź do menu";
|
||||
if (subEl) {
|
||||
subEl.textContent = "bez lokalizacji";
|
||||
subEl.style.display = "";
|
||||
}
|
||||
}
|
||||
|
||||
export function showGeoGateForAction(action) {
|
||||
const geoScreen = document.getElementById("geoScreen");
|
||||
const loadingScreen = document.getElementById("loadingScreen");
|
||||
const geoActionBtn = document.getElementById("geoActionBtn");
|
||||
|
||||
if (loadingScreen) loadingScreen.classList.add("hidden");
|
||||
if (geoScreen) geoScreen.classList.remove("hidden");
|
||||
|
||||
const feature = GEO_GATE_LABELS[action] || "tę funkcję";
|
||||
setGeoLead(`Menu masz już otwarte. Aby skorzystać z <b>${feature}</b>, potwierdź krótko, że jesteś w restauracji.`);
|
||||
setGeoStatus("");
|
||||
hideGeoInstructions();
|
||||
if (geoActionBtn) {
|
||||
setGeoActionBusy(false);
|
||||
setGeoActionLabel("Sprawdź lokalizację");
|
||||
}
|
||||
configureGeoSecondaryButton(getAppAccessLevel() === "menu" ? "back_to_menu" : "menu_only");
|
||||
}
|
||||
|
||||
setGeoGateHandler(showGeoGateForAction);
|
||||
|
||||
export function unlockFullApp() {
|
||||
setAppAccessLevel("full");
|
||||
updateNavAccessState();
|
||||
document.getElementById("geoScreen")?.classList.add("hidden");
|
||||
|
||||
const loaderVisible = !document.getElementById("loadingScreen")?.classList.contains("hidden");
|
||||
const alreadyStarted = !!window.ordersInterval;
|
||||
|
||||
if (alreadyStarted && !loaderVisible) {
|
||||
trackEvent("session_start", { flow: "unlock_from_menu" });
|
||||
initUserProfile();
|
||||
fetchOrders();
|
||||
prefetchOpenBills();
|
||||
refreshGuestPendingActions();
|
||||
startGuestPendingPoll();
|
||||
runPendingProtectedAction();
|
||||
return;
|
||||
}
|
||||
|
||||
startApp();
|
||||
}
|
||||
|
||||
export function enterMenuOnlyMode() {
|
||||
trackEvent("menu_only_entered");
|
||||
setAppAccessLevel("menu");
|
||||
setPendingProtectedAction(null);
|
||||
|
||||
document.getElementById("geoScreen")?.classList.add("hidden");
|
||||
document.getElementById("loadingScreen")?.classList.add("hidden");
|
||||
showBottomNav();
|
||||
resolveTableLabel();
|
||||
switchTabInternal("menu");
|
||||
}
|
||||
|
||||
// --- GEOLOCATION LOGIC ---
|
||||
// Dwa punkty odniesienia: OSM (adres budynku) i pin Google Maps (z nim porównują goście w Maps).
|
||||
const RESTAURANT_LOCATIONS = [
|
||||
{ lat: 50.5622609, lng: 22.0606303, source: "osm" },
|
||||
{ lat: 50.567953, lng: 22.061045, source: "google_maps" },
|
||||
];
|
||||
const MAX_DISTANCE_METERS = 300;
|
||||
const MAX_ACCURACY_BONUS = 150;
|
||||
|
||||
function haversineDistance(lat1, lon1, lat2, lon2) {
|
||||
const R = 6371e3;
|
||||
const p1 = (lat1 * Math.PI) / 180;
|
||||
const p2 = (lat2 * Math.PI) / 180;
|
||||
const dp = ((lat2 - lat1) * Math.PI) / 180;
|
||||
const dl = ((lon2 - lon1) * Math.PI) / 180;
|
||||
|
||||
const a =
|
||||
Math.sin(dp / 2) * Math.sin(dp / 2) +
|
||||
Math.cos(p1) * Math.cos(p2) * Math.sin(dl / 2) * Math.sin(dl / 2);
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
return R * c;
|
||||
}
|
||||
|
||||
function distanceToRestaurant(lat, lng) {
|
||||
return Math.min(
|
||||
...RESTAURANT_LOCATIONS.map(({ lat: rLat, lng: rLng }) => haversineDistance(rLat, rLng, lat, lng))
|
||||
);
|
||||
}
|
||||
|
||||
function isInsideRestaurantGeofence(distanceMeters, accuracyMeters) {
|
||||
const accuracyBonus = Math.min(Math.max(Number(accuracyMeters) || 0, 0), MAX_ACCURACY_BONUS);
|
||||
return distanceMeters <= MAX_DISTANCE_METERS + accuracyBonus;
|
||||
}
|
||||
|
||||
function requestRestaurantGeolocation() {
|
||||
const geoOptions = {
|
||||
enableHighAccuracy: true,
|
||||
timeout: 15000,
|
||||
maximumAge: 0,
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let bestSample = null;
|
||||
let watchId = null;
|
||||
let settled = false;
|
||||
|
||||
const finish = (result) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
if (watchId != null) navigator.geolocation.clearWatch(watchId);
|
||||
clearTimeout(timeoutId);
|
||||
resolve(result);
|
||||
};
|
||||
|
||||
const fail = (error) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
if (watchId != null) navigator.geolocation.clearWatch(watchId);
|
||||
clearTimeout(timeoutId);
|
||||
reject(error);
|
||||
};
|
||||
|
||||
const handlePosition = (position) => {
|
||||
const dist = distanceToRestaurant(position.coords.latitude, position.coords.longitude);
|
||||
const accuracy = position.coords.accuracy;
|
||||
const sample = { position, dist, accuracy };
|
||||
|
||||
if (!bestSample || dist < bestSample.dist) {
|
||||
bestSample = sample;
|
||||
}
|
||||
|
||||
if (isInsideRestaurantGeofence(dist, accuracy)) {
|
||||
finish({ passed: true, ...sample });
|
||||
}
|
||||
};
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
if (bestSample) {
|
||||
finish({
|
||||
passed: isInsideRestaurantGeofence(bestSample.dist, bestSample.accuracy),
|
||||
...bestSample,
|
||||
});
|
||||
return;
|
||||
}
|
||||
fail({ code: 3, message: "Geolocation timeout" });
|
||||
}, 12000);
|
||||
|
||||
watchId = navigator.geolocation.watchPosition(handlePosition, fail, geoOptions);
|
||||
});
|
||||
}
|
||||
|
||||
export function startApp() {
|
||||
setAppAccessLevel("full");
|
||||
updateNavAccessState();
|
||||
document.getElementById("geoScreen").classList.add("hidden");
|
||||
document.getElementById("loadingScreen").classList.remove("hidden");
|
||||
trackEvent("session_start", { flow: "start_app" });
|
||||
|
||||
initUserProfile();
|
||||
fetchOrders();
|
||||
prefetchOpenBills();
|
||||
refreshGuestPendingActions();
|
||||
startGuestPendingPoll();
|
||||
if (!window.ordersInterval) {
|
||||
window.ordersInterval = setInterval(fetchOrders, 10000);
|
||||
}
|
||||
|
||||
// Fallback: If no data after 25s, show empty state anyway
|
||||
setTimeout(() => {
|
||||
if (!document.getElementById("loadingScreen").classList.contains("hidden")) {
|
||||
updateUI([]);
|
||||
}
|
||||
}, 25000);
|
||||
}
|
||||
|
||||
function showGeoConsentScreen() {
|
||||
const geoScreen = document.getElementById("geoScreen");
|
||||
const loadingScreen = document.getElementById("loadingScreen");
|
||||
const geoActionBtn = document.getElementById("geoActionBtn");
|
||||
|
||||
loadingScreen.classList.add("hidden");
|
||||
geoScreen.classList.remove("hidden");
|
||||
|
||||
setGeoLead(GEO_DEFAULT_LEAD);
|
||||
setGeoStatus("");
|
||||
hideGeoInstructions();
|
||||
|
||||
if (geoActionBtn) {
|
||||
setGeoActionBusy(false);
|
||||
setGeoActionLabel("Zgoda, sprawdź lokalizację");
|
||||
}
|
||||
configureGeoSecondaryButton("menu_only");
|
||||
}
|
||||
|
||||
function showGeoPermissionBlockedState() {
|
||||
setGeoStatus("Przeglądarka zablokowała dostęp do lokalizacji.", { error: true });
|
||||
showGeoInstructions(
|
||||
`${getGeoPermissionInstructions()}<br><br>Po zmianie ustawień <b>odśwież stronę</b>, a potem kliknij „Spróbuj ponownie”.`
|
||||
);
|
||||
setGeoActionBusy(false);
|
||||
setGeoActionLabel("Spróbuj ponownie");
|
||||
configureGeoSecondaryButton(getAppAccessLevel() === "menu" ? "back_to_menu" : "menu_only");
|
||||
}
|
||||
|
||||
export function shouldBypassGeolocationHost() {
|
||||
return geoBypassHosts.includes(window.location.hostname);
|
||||
}
|
||||
|
||||
async function checkGeoBypassByClientIp() {
|
||||
try {
|
||||
const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
||||
const timeoutId = controller ? setTimeout(() => controller.abort(), 4000) : null;
|
||||
|
||||
const res = await fetch(endpoints.geoBypass, {
|
||||
credentials: "same-origin",
|
||||
cache: "no-store",
|
||||
signal: controller?.signal,
|
||||
});
|
||||
|
||||
if (timeoutId) clearTimeout(timeoutId);
|
||||
|
||||
const data = await res.json();
|
||||
return data.status === "success" && data.bypassGeo === true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function bypassGeolocation(reason, extra = {}) {
|
||||
trackEvent("geo_bypass_host", { reason, ...extra });
|
||||
if (getAppAccessLevel() === "menu") {
|
||||
unlockFullApp();
|
||||
} else {
|
||||
startApp();
|
||||
}
|
||||
}
|
||||
|
||||
async function queryGeolocationPermissionState() {
|
||||
if (!navigator.permissions?.query) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
try {
|
||||
const status = await navigator.permissions.query({ name: "geolocation" });
|
||||
return status.state;
|
||||
} catch {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
export async function bootstrapGeolocation() {
|
||||
if (shouldBypassGeolocationHost()) {
|
||||
bypassGeolocation("trusted_host", { host: window.location.hostname });
|
||||
return;
|
||||
}
|
||||
|
||||
if (await checkGeoBypassByClientIp()) {
|
||||
bypassGeolocation("trusted_ip");
|
||||
return;
|
||||
}
|
||||
|
||||
showGeoConsentScreen();
|
||||
}
|
||||
|
||||
export function initGeolocation() {
|
||||
if (shouldBypassGeolocationHost()) {
|
||||
console.warn("Bypassing geolocation for trusted host.");
|
||||
bypassGeolocation("trusted_host", { host: window.location.hostname });
|
||||
return;
|
||||
}
|
||||
|
||||
checkGeoBypassByClientIp().then((bypassByIp) => {
|
||||
if (bypassByIp) {
|
||||
console.warn("Bypassing geolocation for trusted client IP.");
|
||||
bypassGeolocation("trusted_ip");
|
||||
return;
|
||||
}
|
||||
initGeolocationAfterBypassChecks();
|
||||
});
|
||||
}
|
||||
|
||||
export async function initGeolocationAfterBypassChecks(options = {}) {
|
||||
const geoScreen = document.getElementById("geoScreen");
|
||||
const loadingScreen = document.getElementById("loadingScreen");
|
||||
|
||||
const localBypassHosts = ["localhost", "127.0.0.1", "192.168.20.84"];
|
||||
if (window.location.protocol === "http:" && localBypassHosts.includes(window.location.hostname)) {
|
||||
console.warn("Bypassing geolocation on local HTTP environment.");
|
||||
bypassGeolocation("local_http", { host: window.location.hostname });
|
||||
return;
|
||||
}
|
||||
|
||||
loadingScreen?.classList.add("hidden");
|
||||
geoScreen?.classList.remove("hidden");
|
||||
configureGeoSecondaryButton(getAppAccessLevel() === "menu" ? "back_to_menu" : "menu_only");
|
||||
|
||||
if (!window.isSecureContext) {
|
||||
setGeoLead(GEO_DEFAULT_LEAD);
|
||||
setGeoStatus("Ta strona wymaga bezpiecznego połączenia HTTPS.", { error: true });
|
||||
showGeoInstructions(
|
||||
"Przeglądarki mobilne blokują geolokalizację bez <b>https://</b>. Otwórz aplikację przez HTTPS i spróbuj ponownie."
|
||||
);
|
||||
setGeoActionBusy(false);
|
||||
setGeoActionLabel("Spróbuj ponownie");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!navigator.geolocation) {
|
||||
setGeoLead(GEO_DEFAULT_LEAD);
|
||||
setGeoStatus("Twoja przeglądarka nie wspiera geolokalizacji.", { error: true });
|
||||
hideGeoInstructions();
|
||||
setGeoActionBusy(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setGeoLead(GEO_DEFAULT_LEAD);
|
||||
setGeoStatus("Sprawdzamy Twoją lokalizację…", { info: true });
|
||||
hideGeoInstructions();
|
||||
setGeoActionBusy(true);
|
||||
|
||||
const permissionState = await queryGeolocationPermissionState();
|
||||
if (permissionState === "denied") {
|
||||
showGeoPermissionBlockedState();
|
||||
return;
|
||||
}
|
||||
|
||||
trackEvent("geo_check_started");
|
||||
|
||||
try {
|
||||
const result = await requestRestaurantGeolocation();
|
||||
const dist = result.dist;
|
||||
const accuracy = result.accuracy;
|
||||
|
||||
console.log(
|
||||
`[GEO] Lat: ${result.position.coords.latitude}, Lng: ${result.position.coords.longitude}, ` +
|
||||
`MinDist: ${Math.round(dist)}m, Accuracy: ${Math.round(accuracy)}m`
|
||||
);
|
||||
|
||||
if (result.passed) {
|
||||
trackEvent("geo_check_passed", {
|
||||
distanceMeters: Math.round(dist),
|
||||
accuracyMeters: Math.round(accuracy),
|
||||
});
|
||||
unlockFullApp();
|
||||
return;
|
||||
}
|
||||
|
||||
trackEvent("geo_check_failed", {
|
||||
reason: "outside_restaurant",
|
||||
distanceMeters: Math.round(dist),
|
||||
accuracyMeters: Math.round(accuracy),
|
||||
});
|
||||
setGeoActionBusy(false);
|
||||
setGeoActionLabel("Spróbuj ponownie");
|
||||
configureGeoSecondaryButton(getAppAccessLevel() === "menu" ? "back_to_menu" : "menu_only");
|
||||
setGeoStatus(
|
||||
`Wygląda na to, że jesteś poza restauracją (ok. ${Math.round(dist)} m, dokładność GPS: ±${Math.round(accuracy)} m).`,
|
||||
{ error: true }
|
||||
);
|
||||
showGeoInstructions(
|
||||
"Przeglądarka często podaje inną lokalizację niż aplikacja Map Google. " +
|
||||
"Spróbuj ponownie na zewnątrz lub bliżej okna — albo przejdź do menu bez lokalizacji."
|
||||
);
|
||||
} catch (error) {
|
||||
trackEvent("geo_check_failed", {
|
||||
reason: "browser_error",
|
||||
code: error.code || null,
|
||||
message: String(error.message || ""),
|
||||
});
|
||||
setGeoActionBusy(false);
|
||||
setGeoActionLabel("Spróbuj ponownie");
|
||||
configureGeoSecondaryButton(getAppAccessLevel() === "menu" ? "back_to_menu" : "menu_only");
|
||||
const deniedBecauseInsecure = /secure origins|only secure|https/i.test(String(error.message || ""));
|
||||
|
||||
if (deniedBecauseInsecure) {
|
||||
setGeoStatus("Geolokalizacja wymaga HTTPS.", { error: true });
|
||||
showGeoInstructions("Otwórz aplikację przez bezpieczny adres <b>https://</b> i spróbuj ponownie.");
|
||||
} else if (isGeoPermissionDenied(error)) {
|
||||
showGeoPermissionBlockedState();
|
||||
} else {
|
||||
setGeoStatus("Nie udało się pobrać lokalizacji.", { error: true });
|
||||
showGeoInstructions("Sprawdź zasięg, włącz GPS i spróbuj ponownie.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function startGeoBootstrap() {
|
||||
bindGeoScreenButtons();
|
||||
|
||||
if (shouldBypassGeolocationHost()) {
|
||||
bypassGeolocation("trusted_host", { host: window.location.hostname });
|
||||
} else {
|
||||
bootstrapGeolocation();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user