v3: GPS tap-to-enable + server-side EXIF bypass for manual creation
GPS:
- initGPS() now shows tappable GPS badge instead of failing silently
- Badge pulses amber with '📍 Tap for GPS' — click triggers permission prompt
- fillGpsParking() auto-triggers badge click if GPS not yet available
- requestLocation() extracted as reusable Promise
EXIF bypass:
- submitManualAsset() now uses server-extracted GPS from raw upload bytes
- Server returns exif_gps in /api/upload/photo response (was already working)
- Client was ignoring it — now fills parking + map link fields automatically
- Bypasses Android content provider EXIF stripping since server reads raw bytes
Admin separation:
- admin.canteen.ourpad.casa now served by systemd service (canteen-admin.service)
- Main server also systemd (canteen-main.service) — both auto-restart on crash/boot
This commit is contained in:
+67
-18
@@ -89,11 +89,18 @@
|
||||
.gps-badge {
|
||||
display: inline-flex; align-items: center; gap: 3px;
|
||||
font-size: 10px; font-weight: 600; padding: 4px 8px; border-radius: 20px;
|
||||
white-space: nowrap;
|
||||
white-space: nowrap; transition: 0.2s;
|
||||
}
|
||||
.gps-badge.ok { background: var(--green-bg); color: var(--green); }
|
||||
.gps-badge.waiting{ background: var(--amber-bg); color: var(--amber); }
|
||||
.gps-badge.err { background: var(--red-bg); color: var(--red); }
|
||||
.gps-badge.waiting:hover,
|
||||
.gps-badge.err:hover { filter: brightness(1.15); }
|
||||
.gps-badge.waiting { animation: pulse-gps 2s ease-in-out infinite; }
|
||||
@keyframes pulse-gps {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.65; }
|
||||
}
|
||||
.user-badge {
|
||||
width: 30px; height: 30px; border-radius: 50%;
|
||||
background: var(--accent-bg); color: var(--accent2);
|
||||
@@ -755,7 +762,7 @@
|
||||
<h1>📦 Canteen Assets</h1>
|
||||
<div class="header-badges">
|
||||
<span id="roleBadge" class="role-badge guest" style="display:none;">guest</span>
|
||||
<span id="gpsBadge" class="gps-badge waiting">📍 GPS...</span>
|
||||
<span id="gpsBadge" class="gps-badge waiting" onclick="void(0)" style="cursor:pointer;">📍 Tap GPS</span>
|
||||
<span id="queueIndicator" class="queue-indicator" onclick="processOfflineQueue()" title="Offline queue">⬇️ <span id="queueIndicatorCount">0</span></span>
|
||||
<div class="user-badge" id="userBadge" onclick="openDrawer()" title="User">?</div>
|
||||
</div>
|
||||
@@ -1463,27 +1470,55 @@
|
||||
// =========================================================================
|
||||
// GPS
|
||||
// =========================================================================
|
||||
function requestLocation() {
|
||||
return new Promise((resolve, reject) => {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
pos => {
|
||||
AppState.gpsLat = pos.coords.latitude;
|
||||
AppState.gpsLng = pos.coords.longitude;
|
||||
AppState.gpsAcc = pos.coords.accuracy;
|
||||
const badge = document.getElementById('gpsBadge');
|
||||
badge.className = 'gps-badge ok';
|
||||
badge.textContent = `📍 ${AppState.gpsLat.toFixed(4)}, ${AppState.gpsLng.toFixed(4)}`;
|
||||
resolve(pos);
|
||||
},
|
||||
err => {
|
||||
const badge = document.getElementById('gpsBadge');
|
||||
badge.className = 'gps-badge err';
|
||||
if (err.code === 1) badge.textContent = '📍 Tap for GPS';
|
||||
else badge.textContent = '📍 ' + err.message;
|
||||
reject(err);
|
||||
},
|
||||
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 300000 }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function initGPS() {
|
||||
const badge = document.getElementById('gpsBadge');
|
||||
if (!navigator.geolocation) {
|
||||
badge.className = 'gps-badge err';
|
||||
badge.textContent = '📍 Unavailable';
|
||||
badge.style.cursor = 'default';
|
||||
return;
|
||||
}
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
pos => {
|
||||
AppState.gpsLat = pos.coords.latitude;
|
||||
AppState.gpsLng = pos.coords.longitude;
|
||||
AppState.gpsAcc = pos.coords.accuracy;
|
||||
badge.className = 'gps-badge ok';
|
||||
badge.textContent = `📍 ${AppState.gpsLat.toFixed(4)}, ${AppState.gpsLng.toFixed(4)}`;
|
||||
},
|
||||
err => {
|
||||
badge.className = 'gps-badge err';
|
||||
badge.textContent = '📍 ' + err.message;
|
||||
},
|
||||
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 300000 }
|
||||
);
|
||||
// Show a tappable hint so user knows to interact
|
||||
badge.className = 'gps-badge waiting';
|
||||
badge.textContent = '📍 Tap for GPS';
|
||||
badge.style.cursor = 'pointer';
|
||||
badge.onclick = function() {
|
||||
badge.textContent = '📍 Locating...';
|
||||
badge.onclick = null; // one-shot
|
||||
requestLocation().catch(() => {
|
||||
badge.onclick = function() { requestLocation().catch(() => {}); };
|
||||
badge.style.cursor = 'pointer';
|
||||
badge.textContent = '📍 Tap to retry';
|
||||
});
|
||||
};
|
||||
// Also try silently once in case browser allows it without gesture
|
||||
requestLocation().catch(() => {
|
||||
// Silent fail is expected on mobile — badge click handler covers it
|
||||
});
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
@@ -2637,6 +2672,17 @@
|
||||
fd.append('file', manualPhotoFile, manualPhotoFile.name || 'photo.jpg');
|
||||
const upData = await api('/api/upload/photo', { method: 'POST', body: fd });
|
||||
photoPath = upData.path;
|
||||
// Use server-extracted GPS from raw bytes (bypasses client-side EXIF stripping)
|
||||
if (upData.exif_gps) {
|
||||
const coords = upData.exif_gps.lat.toFixed(6) + ', ' + upData.exif_gps.lng.toFixed(6);
|
||||
if (!document.getElementById('manParkingLocation').value.trim()) {
|
||||
document.getElementById('manParkingLocation').value = coords;
|
||||
}
|
||||
if (!document.getElementById('manMapLink').value.trim()) {
|
||||
document.getElementById('manMapLink').value = `https://www.openstreetmap.org/?mlat=${upData.exif_gps.lat}&mlon=${upData.exif_gps.lng}&zoom=17`;
|
||||
}
|
||||
showToast('📍 GPS extracted from photo!');
|
||||
}
|
||||
} catch (e) { /* photo upload failed, continue without */ }
|
||||
}
|
||||
|
||||
@@ -2764,11 +2810,14 @@
|
||||
|
||||
function fillGpsParking(inputId) {
|
||||
if (!AppState.gpsLat) {
|
||||
showToast('GPS not available — allow location access', true);
|
||||
showToast('📍 Tap the GPS badge in the header to enable location', true);
|
||||
// Also trigger the GPS badge click if it exists
|
||||
const badge = document.getElementById('gpsBadge');
|
||||
if (badge && badge.onclick) badge.onclick();
|
||||
return;
|
||||
}
|
||||
document.getElementById(inputId).value = AppState.gpsLat.toFixed(6) + ', ' + AppState.gpsLng.toFixed(6);
|
||||
showToast('GPS coords filled!');
|
||||
showToast('📍 GPS coords filled!');
|
||||
}
|
||||
|
||||
// Settings are loaded after auth (in doLogin / initAuth)
|
||||
|
||||
Reference in New Issue
Block a user