Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
'use strict';

// ==================== CONFIG ====================
var BUILD = '10'; // bump every deploy — shown on Debug screen to confirm version
var BUILD = '11'; // bump every deploy — shown on Debug screen to confirm version
var STORAGE_KEY = 'stepview_v1';
var RING_CIRC = 603; // 2*pi*r, r=96
var KCAL_PER_KG_PER_KM = 0.9; // gross walking estimate
Expand Down Expand Up @@ -313,6 +313,7 @@
case 'weight-dec': state.settings.weightKg = clampWeight(state.settings.weightKg - 1); afterSetting(); break;
case 'height-inc': state.settings.heightCm = clampHeight(state.settings.heightCm + 1); afterSetting(); break;
case 'height-dec': state.settings.heightCm = clampHeight(state.settings.heightCm - 1); afterSetting(); break;
case 'update-refresh': if (waitingWorker) waitingWorker.postMessage('skipWaiting'); break;
}
}
function afterSetting() { renderSettings(); render(); save(); }
Expand Down Expand Up @@ -437,10 +438,40 @@
};
}

// ==================== SERVICE WORKER ====================
var waitingWorker = null;
function setupSW() {
if (!('serviceWorker' in navigator)) return;
navigator.serviceWorker.register('sw.js').then(function (reg) {
if (reg.waiting) {
waitingWorker = reg.waiting;
showUpdateBanner();
}
reg.addEventListener('updatefound', function () {
var newWorker = reg.installing;
newWorker.addEventListener('statechange', function () {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
waitingWorker = newWorker;
showUpdateBanner();
}
});
});
});
navigator.serviceWorker.addEventListener('controllerchange', function () {
window.location.reload();
});
}

function showUpdateBanner() {
var banner = $('update-banner');
if (banner) banner.classList.remove('hidden');
}

// ==================== INIT ====================
function init() {
document.querySelectorAll('.screen').forEach(function (s) { if (s.id) screens[s.id] = s; });
load();
setupSW();
setupEvents();
setupMotion(); // always-on accelerometer capture (independent of the UI)
render();
Expand Down
8 changes: 6 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
<meta name="mrbd-web-app-capable" content="yes">
<title>StepView — Step, Distance & Calorie Counter</title>
<link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="styles.css?v=10">
<link rel="manifest" href="manifest.json">
<link rel="stylesheet" href="styles.css?v=11">
</head>
<body>
<div id="app">
Expand Down Expand Up @@ -132,6 +133,9 @@ <h1>Debug</h1>
</nav>
</div>
</div>
<script src="app.js?v=10"></script>
<script src="app.js?v=11"></script>
<div id="update-banner" class="update-banner hidden" data-action="update-refresh">
New version available! <span>Tap to update</span>
</div>
</body>
</html>
16 changes: 16 additions & 0 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "StepView",
"short_name": "StepView",
"description": "Step, distance, and calorie counter for Meta Ray-Ban Display glasses.",
"start_url": "./index.html",
"display": "standalone",
"background_color": "#000000",
"theme_color": "#000000",
"icons": [
{
"src": "favicon.svg",
"sizes": "any",
"type": "image/svg+xml"
}
]
}
21 changes: 21 additions & 0 deletions styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,25 @@ html, body {
}
.toast.show { transform: translateX(-50%) translateY(0); opacity: 1; }

/* --- Update Banner --- */
.update-banner {
position: absolute;
bottom: 22px;
left: 50%;
transform: translateX(-50%);
background: var(--surface);
border: 2px solid var(--green);
border-radius: 16px;
padding: 14px 24px;
font-size: 19px;
font-weight: 700;
color: #fff;
z-index: 100;
cursor: pointer;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5);
white-space: nowrap;
}
.update-banner span { color: var(--green); margin-left: 8px; text-decoration: underline; }
.update-banner:focus { outline: none; border-color: var(--focus); box-shadow: 0 0 28px var(--focus-glow); }

.hidden { display: none !important; }
55 changes: 55 additions & 0 deletions sw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
var CACHE_NAME = 'stepview-v11';
var ASSETS = [
'./',
'index.html',
'app.js?v=11',
'styles.css?v=11',
'favicon.svg',
'manifest.json'
];

self.addEventListener('install', function (event) {
event.waitUntil(
caches.open(CACHE_NAME).then(function (cache) {
return cache.addAll(ASSETS);
})
);
});

self.addEventListener('activate', function (event) {
event.waitUntil(
caches.keys().then(function (keys) {
return Promise.all(
keys.map(function (key) {
if (key !== CACHE_NAME) {
return caches.delete(key);
}
})
);
})
);
});

self.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request).then(function (response) {
if (response) return response;
return fetch(event.request).then(function (networkResponse) {
if (!networkResponse || networkResponse.status !== 200 || networkResponse.type !== 'basic') {
return networkResponse;
}
var responseToCache = networkResponse.clone();
caches.open(CACHE_NAME).then(function (cache) {
cache.put(event.request, responseToCache);
});
return networkResponse;
});
})
);
});

self.addEventListener('message', function (event) {
if (event.data === 'skipWaiting') {
self.skipWaiting();
}
});
Loading