support for resume on multiple devices, fix camera freeze bug when opening setup overlay
All checks were successful
pedestrian-simulator / build (push) Successful in 1m19s
All checks were successful
pedestrian-simulator / build (push) Successful in 1m19s
This commit is contained in:
@@ -824,6 +824,11 @@ function showSetupOverlay() {
|
|||||||
|
|
||||||
function hideSetupOverlay() {
|
function hideSetupOverlay() {
|
||||||
document.getElementById('setup-overlay').classList.remove('active');
|
document.getElementById('setup-overlay').classList.remove('active');
|
||||||
|
|
||||||
|
// Resume animation if a trip is active
|
||||||
|
if (localStorage.getItem(LOCATION_STORAGE)) {
|
||||||
|
startCameraAnimation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupEventListeners() {
|
function setupEventListeners() {
|
||||||
@@ -989,6 +994,8 @@ async function calculateAndStartRoute(startAddress, endAddress, isRestoring = fa
|
|||||||
// Reset display steps for visual consistency
|
// Reset display steps for visual consistency
|
||||||
displayTripSteps = 0;
|
displayTripSteps = 0;
|
||||||
stateBuffer = [];
|
stateBuffer = [];
|
||||||
|
window.lastPositionUpdateDistance = 0;
|
||||||
|
window.lastPanoUpdate = 0;
|
||||||
|
|
||||||
startFromLocation(leg.start_location, locationData.startAddress);
|
startFromLocation(leg.start_location, locationData.startAddress);
|
||||||
hideSetupOverlay();
|
hideSetupOverlay();
|
||||||
@@ -1169,7 +1176,7 @@ function resetLocation() {
|
|||||||
showSetupOverlay();
|
showSetupOverlay();
|
||||||
document.getElementById('startLocationInput').value = '';
|
document.getElementById('startLocationInput').value = '';
|
||||||
document.getElementById('endLocationInput').value = '';
|
document.getElementById('endLocationInput').value = '';
|
||||||
stopAnimation();
|
// stopAnimation(); // Don't stop animation just because we opened the setup overlay
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rate limiting for refresh
|
// Rate limiting for refresh
|
||||||
@@ -1230,9 +1237,42 @@ async function fetchStatus() {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Server-Side Trip Synchronization
|
||||||
|
if (data.activeTrip) {
|
||||||
|
// A trip is active on the server
|
||||||
|
|
||||||
|
// Check if we need to sync/resume this trip locally
|
||||||
|
const localLocation = localStorage.getItem(LOCATION_STORAGE);
|
||||||
|
let needsResync = false;
|
||||||
|
|
||||||
|
if (!localLocation) {
|
||||||
|
needsResync = true;
|
||||||
|
console.log("[Sync] No local trip found. Syncing from server.");
|
||||||
|
} else {
|
||||||
|
// We have a local trip, check if it's the SAME trip
|
||||||
|
try {
|
||||||
|
const localData = JSON.parse(localLocation);
|
||||||
|
if (localData.routeName !== data.activeTrip.route_name) {
|
||||||
|
needsResync = true;
|
||||||
|
console.log("[Sync] Local trip route mismatch. Syncing from server.");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
needsResync = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsResync) {
|
||||||
|
syncActiveTrip(data.activeTrip);
|
||||||
|
}
|
||||||
|
|
||||||
// Update target steps from server
|
// Update target steps from server
|
||||||
if (data.tripSteps !== undefined) {
|
if (data.tripSteps !== undefined) {
|
||||||
// Add to buffer
|
// Safety: Only accept step updates if they are for the current trip
|
||||||
|
if (needsResync) {
|
||||||
|
stateBuffer = [];
|
||||||
|
displayTripSteps = 0;
|
||||||
|
}
|
||||||
|
|
||||||
stateBuffer.push({ t: now, s: data.tripSteps });
|
stateBuffer.push({ t: now, s: data.tripSteps });
|
||||||
|
|
||||||
// Keep buffer size manageable (20 points is ~1.5 mins)
|
// Keep buffer size manageable (20 points is ~1.5 mins)
|
||||||
@@ -1240,6 +1280,23 @@ async function fetchStatus() {
|
|||||||
stateBuffer.shift();
|
stateBuffer.shift();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// No trip active on the server
|
||||||
|
if (localStorage.getItem(LOCATION_STORAGE)) {
|
||||||
|
console.log("[Sync] No trip on server. Clearing local trip.");
|
||||||
|
localStorage.removeItem(LOCATION_STORAGE);
|
||||||
|
|
||||||
|
if (!document.getElementById('celebration-overlay').classList.contains('active')) {
|
||||||
|
document.getElementById('routeInfo').textContent = 'Not Started';
|
||||||
|
document.getElementById('currentSteps').textContent = '0';
|
||||||
|
document.getElementById('tripMeter').textContent = '0.0 / 0.0 km';
|
||||||
|
masterPath = [];
|
||||||
|
routeTotalDistance = 0;
|
||||||
|
displayTripSteps = 0;
|
||||||
|
stateBuffer = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update user info
|
// Update user info
|
||||||
updateUserProfile(data.user);
|
updateUserProfile(data.user);
|
||||||
@@ -1254,6 +1311,24 @@ async function fetchStatus() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function syncActiveTrip(activeTrip) {
|
||||||
|
console.log("[Sync] Restoring trip from server:", activeTrip.route_name);
|
||||||
|
|
||||||
|
if (activeTrip.trip_type === 'address') {
|
||||||
|
calculateAndStartRoute(activeTrip.start_address, activeTrip.end_address, true);
|
||||||
|
} else if (activeTrip.trip_type === 'kml') {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/kml/download?owner_id=${activeTrip.kml_owner_id || currentUserID}&filename=${encodeURIComponent(activeTrip.route_name)}`);
|
||||||
|
if (response.ok) {
|
||||||
|
const kmlContent = await response.text();
|
||||||
|
processKML(kmlContent, activeTrip.route_name);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[Sync] Failed to download KML for sync:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateNextSyncCountdown(nextSyncTime) {
|
function updateNextSyncCountdown(nextSyncTime) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const diff = nextSyncTime - now;
|
const diff = nextSyncTime - now;
|
||||||
@@ -2154,6 +2229,8 @@ function startFromCustomRoute(pathData, routeName, isRestoring = false, markers
|
|||||||
// Reset display steps
|
// Reset display steps
|
||||||
displayTripSteps = 0;
|
displayTripSteps = 0;
|
||||||
stateBuffer = [];
|
stateBuffer = [];
|
||||||
|
window.lastPositionUpdateDistance = 0;
|
||||||
|
window.lastPanoUpdate = 0;
|
||||||
|
|
||||||
// Initialize Map View
|
// Initialize Map View
|
||||||
startFromLocation(startPoint, routeName);
|
startFromLocation(startPoint, routeName);
|
||||||
@@ -2301,6 +2378,14 @@ async function triggerCelebration(finalDistance) {
|
|||||||
currentTripDetails = null;
|
currentTripDetails = null;
|
||||||
masterPath = [];
|
masterPath = [];
|
||||||
routeTotalDistance = 0;
|
routeTotalDistance = 0;
|
||||||
|
displayTripSteps = 0;
|
||||||
|
stateBuffer = [];
|
||||||
|
|
||||||
|
// Update UI to reflect reset
|
||||||
|
document.getElementById('routeInfo').textContent = 'Not Started';
|
||||||
|
document.getElementById('currentSteps').textContent = '0';
|
||||||
|
document.getElementById('tripMeter').textContent = '0.0 / 0.0 km';
|
||||||
|
|
||||||
// We keep window.hasCelebrated = true to prevent immediate re-trigger
|
// We keep window.hasCelebrated = true to prevent immediate re-trigger
|
||||||
// but starting a new trip will reset it.
|
// but starting a new trip will reset it.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -360,10 +360,26 @@ func (sm *StepManager) GetStatus() map[string]interface{} {
|
|||||||
go sm.Sync() // Async sync
|
go sm.Sync() // Async sync
|
||||||
}
|
}
|
||||||
|
|
||||||
return map[string]interface{}{
|
res := map[string]interface{}{
|
||||||
"tripSteps": currentSmoothed,
|
"tripSteps": currentSmoothed,
|
||||||
"nextSyncTime": nextSyncMilli,
|
"nextSyncTime": nextSyncMilli,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sm.mu.Lock()
|
||||||
|
if sm.tripState.StartDate != "" {
|
||||||
|
res["activeTrip"] = map[string]interface{}{
|
||||||
|
"trip_type": sm.tripState.TripType,
|
||||||
|
"route_name": sm.tripState.RouteName,
|
||||||
|
"start_address": sm.tripState.StartAddress,
|
||||||
|
"end_address": sm.tripState.EndAddress,
|
||||||
|
"kml_id": sm.tripState.KmlID,
|
||||||
|
"total_distance": sm.tripState.TotalDistance,
|
||||||
|
"start_time": sm.tripState.StartTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sm.mu.Unlock()
|
||||||
|
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecalculateTotalFromState sums up the steps from the DailyCache without making external API calls
|
// RecalculateTotalFromState sums up the steps from the DailyCache without making external API calls
|
||||||
|
|||||||
Reference in New Issue
Block a user