fix: prevent devices limit flicker

This commit is contained in:
3252a8
2026-06-05 15:45:57 +03:00
parent af1487731a
commit 1200e8ff70
3 changed files with 86 additions and 7 deletions
+13 -4
View File
@@ -6,19 +6,28 @@
*/
export function devicesLimitLabel(devicesData, t, maxDevicesOverride) {
const value = maxDevicesOverride !== undefined ? maxDevicesOverride : devicesData?.max_devices;
if (value === undefined || value === null || value === "") {
return t("wa_devices_limit_pending", {}, "...");
}
const numeric = Number(value ?? 0);
if (!Number.isFinite(numeric) || numeric <= 0) return t("wa_devices_unlimited");
return String(Math.trunc(numeric));
}
export function devicesCountLabel(devicesData, t) {
export function devicesCountLabel(devicesData, t, maxDevicesOverride) {
const current = Number(devicesData?.current_devices ?? devicesData?.devices?.length ?? 0);
return t("wa_devices_count", { current, max: devicesLimitLabel(devicesData, t) });
return t("wa_devices_count", {
current,
max: devicesLimitLabel(devicesData, t, maxDevicesOverride),
});
}
export function devicesPercent(devicesData) {
export function devicesPercent(devicesData, maxDevicesOverride) {
const current = Number(devicesData?.current_devices ?? devicesData?.devices?.length ?? 0);
const max = Number(devicesData?.max_devices || 0);
const maxValue =
maxDevicesOverride !== undefined ? maxDevicesOverride : devicesData?.max_devices;
if (maxValue === undefined || maxValue === null || maxValue === "") return 0;
const max = Number(maxValue || 0);
if (!max || max <= 0) return 100;
return Math.max(0, Math.min(100, Math.round((current / max) * 100)));
}
@@ -33,6 +33,7 @@
hideDevicesSummary &&
!(devicesBusy && !devicesLoaded) &&
(!devicesStatus || subscriptionNotActiveError);
$: effectiveMaxDevices = devicesData?.max_devices ?? subscription?.max_devices;
</script>
<main class="content with-nav">
@@ -42,7 +43,7 @@
<Smartphone size={28} />
<span>
<strong>{t("wa_devices_title")}</strong>
<small>{devicesCountLabel(devicesData, t)}</small>
<small>{devicesCountLabel(devicesData, t, effectiveMaxDevices)}</small>
</span>
<Button
variant="icon"
@@ -56,7 +57,7 @@
</div>
<LinearProgress
class="devices-progress"
value={devicesPercent(devicesData)}
value={devicesPercent(devicesData, effectiveMaxDevices)}
label={t("wa_devices_title")}
/>
{#if Number(subscription?.extra_hwid_devices || 0) > 0 && subscription?.extra_hwid_devices_valid_until_text}
@@ -91,7 +92,11 @@
<EmptyCard class="devices-empty-card">
<Smartphone size={28} />
<span>{t("wa_devices_empty")}</span>
<small>{t("wa_devices_empty_hint", { max: devicesLimitLabel(devicesData, t) })}</small>
<small>
{t("wa_devices_empty_hint", {
max: devicesLimitLabel(devicesData, t, effectiveMaxDevices),
})}
</small>
</EmptyCard>
{:else}
<div class="devices-list">
+65
View File
@@ -0,0 +1,65 @@
import json
import shutil
import subprocess
import textwrap
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parents[1]
def test_devices_labels_use_subscription_limit_before_devices_payload():
node = shutil.which("node")
if not node:
pytest.skip("node is required to exercise the webapp label helpers")
script = textwrap.dedent(
"""
const mod = await import("./frontend/src/lib/webapp/devicesLabels.js");
const t = (key, params = {}, fallback = "") => {
if (key === "wa_devices_count") return `${params.current}/${params.max}`;
if (key === "wa_devices_unlimited") return "Unlimited";
return fallback || key;
};
const result = {
missingLimit: mod.devicesLimitLabel(null, t),
fallbackLimit: mod.devicesLimitLabel(null, t, 5),
fallbackCount: mod.devicesCountLabel({ current_devices: 2 }, t, 5),
fallbackPercent: mod.devicesPercent({ current_devices: 2 }, 5),
unlimitedLimit: mod.devicesLimitLabel({ max_devices: 0 }, t),
unlimitedPercent: mod.devicesPercent({ current_devices: 2, max_devices: 0 }),
};
console.log(JSON.stringify(result));
"""
)
completed = subprocess.run(
[node, "--input-type=module", "--eval", script],
cwd=REPO_ROOT,
check=True,
capture_output=True,
text=True,
)
payload = json.loads(completed.stdout)
assert payload == {
"missingLimit": "...",
"fallbackLimit": "5",
"fallbackCount": "2/5",
"fallbackPercent": 40,
"unlimitedLimit": "Unlimited",
"unlimitedPercent": 100,
}
def test_devices_screen_passes_subscription_limit_as_initial_fallback():
source = (REPO_ROOT / "frontend/src/webapp/screens/DevicesScreen.svelte").read_text(
encoding="utf-8"
)
assert "effectiveMaxDevices = devicesData?.max_devices ?? subscription?.max_devices" in source
assert "devicesCountLabel(devicesData, t, effectiveMaxDevices)" in source
assert "devicesPercent(devicesData, effectiveMaxDevices)" in source
assert "devicesLimitLabel(devicesData, t, effectiveMaxDevices)" in source