ui: drop the network-monitor area line, label the refresh time (v2.7.5)

The second line under the location (e.g. "RAI FFFE") showed only the LAC
part of the current area identity: after every dummy-writing scenario it
was the constant FFFE, and otherwise it was redundant with the
EF.LOCI/PSLOCI/EPSLOCI rows below.  netStateRender no longer renders it.

The timestamp line now carries a translated "Last refresh" label
('Last refresh': 'Последнее обновление').

- frontend/index.html: remove #netstate-area (markup + render), label the
  read time, LANG_RU entry
- frontend/tests: html test pins the label and the absence of the area
  line; netstate test asserts the rendered timestamp
- sw.js cache simple-v208; version trio 2.7.5
This commit is contained in:
2026-09-20 13:50:42 +03:00
parent ef9faabdf8
commit 6131d34d28
6 changed files with 13 additions and 9 deletions
+3 -4
View File
@@ -1036,8 +1036,7 @@
<button id="netstate-refresh-btn" data-needs="card" onclick="netStateRefresh()" class="px-2 py-0.5 text-xs rounded bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600 disabled:opacity-40 disabled:cursor-not-allowed" data-l10n="Refresh">Refresh</button> <button id="netstate-refresh-btn" data-needs="card" onclick="netStateRefresh()" class="px-2 py-0.5 text-xs rounded bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-300 hover:bg-gray-300 dark:hover:bg-slate-600 disabled:opacity-40 disabled:cursor-not-allowed" data-l10n="Refresh">Refresh</button>
</div> </div>
<div id="netstate-location" class="text-xs font-mono text-gray-600 dark:text-slate-400"></div> <div id="netstate-location" class="text-xs font-mono text-gray-600 dark:text-slate-400"></div>
<div id="netstate-area" class="text-xs font-mono text-gray-400 dark:text-slate-500"></div> <div class="text-xs text-gray-400 dark:text-slate-500 mt-1"><span data-l10n="Last refresh">Last refresh</span>: <span id="netstate-read"></span></div>
<div id="netstate-read" class="text-xs text-gray-400 dark:text-slate-500 mt-1"></div>
<div id="netstate-body" class="mt-2 text-xs font-mono text-gray-600 dark:text-slate-400 max-h-[45vh] overflow-auto"></div> <div id="netstate-body" class="mt-2 text-xs font-mono text-gray-600 dark:text-slate-400 max-h-[45vh] overflow-auto"></div>
</fieldset> </fieldset>
</div> </div>
@@ -1414,7 +1413,7 @@
// ===== Version ===== // ===== Version =====
// Single source of truth for the PWA version: shown in the header and used // Single source of truth for the PWA version: shown in the header and used
// by the server version check in pysimConnect(). // by the server version check in pysimConnect().
const SIMPLE_VERSION = '2.7.4'; const SIMPLE_VERSION = '2.7.5';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION; document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== Tab switching ===== // ===== Tab switching =====
@@ -12654,7 +12653,6 @@ function netStateRender(state) {
} else { } else {
locEl.innerHTML = '<span class="text-gray-400"></span>'; locEl.innerHTML = '<span class="text-gray-400"></span>';
} }
document.getElementById('netstate-area').textContent = loc ? (loc.area + ' ' + loc.lac) : '';
document.getElementById('netstate-read').textContent = document.getElementById('netstate-read').textContent =
state && state.read_at ? new Date(state.read_at * 1000).toLocaleTimeString() : ''; state && state.read_at ? new Date(state.read_at * 1000).toLocaleTimeString() : '';
const files = (state && state.files) || {}; const files = (state && state.files) || {};
@@ -12952,6 +12950,7 @@ const LANG_RU = {
'Raw APDU': 'Отправка APDU', 'Raw APDU': 'Отправка APDU',
'Read': 'Прочитать', 'Read': 'Прочитать',
'Refresh': 'Обновить', 'Refresh': 'Обновить',
'Last refresh': 'Последнее обновление',
'Save': 'Сохранить', 'Save': 'Сохранить',
'Raw': 'Данные как на карте', 'Raw': 'Данные как на карте',
'Decoded': 'Декодированные данные', 'Decoded': 'Декодированные данные',
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simple-v207'; const CACHE = 'simple-v208';
const URLS = [ const URLS = [
'index.html', 'index.html',
'help.html', 'help.html',
+5
View File
@@ -293,3 +293,8 @@ test('SCP81 listener exposes its four modes with the matching notes', () => {
assert.ok(html.includes("mode === 'redirect' && (!hostVal || !portVal)")); assert.ok(html.includes("mode === 'redirect' && (!hostVal || !portVal)"));
assert.ok(html.includes("el.disabled = (mode === 'passthru')")); assert.ok(html.includes("el.disabled = (mode === 'passthru')"));
}); });
test('the network monitor labels its refresh timestamp and has no area line', () => {
assert.match(html, /data-l10n="Last refresh">Last refresh<\/span>: <span id="netstate-read"><\/span>/);
assert.ok(!/id="netstate-area"/.test(html), 'the area line is gone; LAI/RAI/TAI live in the EF rows');
});
+2 -2
View File
@@ -47,7 +47,7 @@ eval(code);
function setup() { function setup() {
const els = {}; const els = {};
for (const id of ['netstate-badge', 'netstate-location', 'netstate-area', for (const id of ['netstate-badge', 'netstate-location',
'netstate-read', 'netstate-body']) { 'netstate-read', 'netstate-body']) {
els[id] = { className: '', textContent: '', innerHTML: '' }; els[id] = { className: '', textContent: '', innerHTML: '' };
} }
@@ -109,7 +109,7 @@ test('netStateRender paints the service badge, location and rows', () => {
assert.ok(els['netstate-location'].innerHTML.includes( assert.ok(els['netstate-location'].innerHTML.includes(
'Telekom<span class="block">Guest (roaming) · <span class="text-red-500">⛔ PLMN not allowed</span></span>')); 'Telekom<span class="block">Guest (roaming) · <span class="text-red-500">⛔ PLMN not allowed</span></span>'));
assert.ok(!els['netstate-location'].innerHTML.includes('Telekom · ')); assert.ok(!els['netstate-location'].innerHTML.includes('Telekom · '));
assert.strictEqual(els['netstate-area'].textContent, 'LAI 6CD7'); assert.strictEqual(els['netstate-read'].textContent, new Date(1700000000 * 1000).toLocaleTimeString());
assert.ok(els['netstate-body'].innerHTML.includes('EF.IMSI')); assert.ok(els['netstate-body'].innerHTML.includes('EF.IMSI'));
assert.ok(els['netstate-body'].innerHTML.includes('262011234567890')); assert.ok(els['netstate-body'].innerHTML.includes('262011234567890'));
assert.ok(els['netstate-body'].innerHTML.includes('>write<')); assert.ok(els['netstate-body'].innerHTML.includes('>write<'));
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "pysim-simple-server" name = "pysim-simple-server"
version = "2.7.4" version = "2.7.5"
description = "HTTP REST server wrapping pysim for the SIMple PWA" description = "HTTP REST server wrapping pysim for the SIMple PWA"
requires-python = ">=3.8" requires-python = ">=3.8"
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh. # pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
+1 -1
View File
@@ -25,7 +25,7 @@ from osmocom.construct import GsmOrUcs2Adapter
from osmocom.tlv import BER_TLV_IE from osmocom.tlv import BER_TLV_IE
VERSION = '2.7.4' VERSION = '2.7.5'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE