diff --git a/Web App/web/src/i18n/da.json b/Web App/web/src/i18n/da.json index 2171386..ea4e120 100644 --- a/Web App/web/src/i18n/da.json +++ b/Web App/web/src/i18n/da.json @@ -72,6 +72,8 @@ "tap": "Hold kortet mod laderen", "tapping": "Hold kortet mod læseren… {n}s", "tapHint": "Læseren er åben. Hold kortet mod laderen.", + "tapSave": "Hold kortet mod laderen, og tilføj det", + "tapSaveHint": "Tilføjer kortet, så snart det holdes mod læseren, med navnet RFID og kortets sidste fire cifre.", "tapNone": "Der blev ikke holdt et kort mod læseren, før den lukkede.", "addTitle": "Tilføj et kort", "remove": "Fjern", diff --git a/Web App/web/src/i18n/en.json b/Web App/web/src/i18n/en.json index c8427e4..e60298e 100644 --- a/Web App/web/src/i18n/en.json +++ b/Web App/web/src/i18n/en.json @@ -353,6 +353,8 @@ "tap": "Tap card at the charger", "tapping": "Hold the card against the reader… {n}s", "tapHint": "The reader is open. Hold the card against the charger.", + "tapSave": "Tap card and add it", + "tapSaveHint": "Adds the card as soon as it is tapped, named RFID and its last four digits.", "tapNone": "No card was tapped before the reader closed.", "addTitle": "Add a card", "remove": "Remove", diff --git a/Web App/web/src/i18n/pl.json b/Web App/web/src/i18n/pl.json index 7f60d41..86e0bdb 100644 --- a/Web App/web/src/i18n/pl.json +++ b/Web App/web/src/i18n/pl.json @@ -72,6 +72,8 @@ "tap": "Przyłóż kartę do ładowarki", "tapping": "Przytrzymaj kartę przy czytniku… {n}s", "tapHint": "Czytnik jest otwarty. Przytrzymaj kartę przy ładowarce.", + "tapSave": "Przyłóż kartę i dodaj ją", + "tapSaveHint": "Dodaje kartę zaraz po przyłożeniu, pod nazwą RFID i cztery ostatnie znaki numeru.", "tapNone": "Nie przyłożono karty, zanim czytnik się zamknął.", "addTitle": "Dodaj kartę", "remove": "Usuń", diff --git a/Web App/web/src/views/Charging.vue b/Web App/web/src/views/Charging.vue index 92e2b11..d7c6dd5 100644 --- a/Web App/web/src/views/Charging.vue +++ b/Web App/web/src/views/Charging.vue @@ -1501,14 +1501,7 @@ async function addRfidCard() { rfidBusy.value = "new"; rfidError.value = ""; try { - const res = await api.saveAnkerRfidCard(sn, number, newCardName.value.trim()); - rfidWritten.value = { ...rfidWritten.value, [sn]: res?.cards || [] }; - if (res?.present === false) { - rfidError.value = t("charging.rfid.notAdded"); - } else { - newCardNumber.value = ""; - newCardName.value = ""; - } + await writeCard(sn, number); } catch (e) { rfidError.value = e.message; } finally { @@ -1516,6 +1509,44 @@ async function addRfidCard() { } } +// Writing one card and then reading the list back, which is the only thing that +// says whether the write landed. Shared by both ways of adding one, so the tap +// and the typed number cannot end up judging their answers differently. +// +// The name is whatever is in the box, and an empty box is not a missing name: it +// is the server's own convention — "RFID" and the card's last four digits — and +// leaving it to the server is what keeps the two ways of adding a card from +// drifting into two naming conventions. +async function writeCard(sn, number) { + const res = await api.saveAnkerRfidCard(sn, number, newCardName.value.trim()); + rfidWritten.value = { ...rfidWritten.value, [sn]: res?.cards || [] }; + if (res?.present === false) { + rfidError.value = t("charging.rfid.notAdded"); + return false; + } + newCardNumber.value = ""; + newCardName.value = ""; + return true; +} + +// The reader's own twenty seconds, as a value rather than as a side effect on +// the form: one caller wants the number in the box, the other wants to write it. +// The caller owns rfidBusy, so the tap-and-save button can hold it across the +// write that follows and nothing re-enables between the two. +async function readCardAtCharger(sn) { + rfidCountdown.value = 20; + const tick = setInterval(() => { + rfidCountdown.value = Math.max(0, rfidCountdown.value - 1); + }, 1000); + try { + const res = await api.scanAnkerRfidCard(sn); + return res?.tapped && res.card ? res.card : ""; + } finally { + clearInterval(tick); + rfidCountdown.value = 0; + } +} + // Asking the charger to read a card, which is what the Anker app's second way of // adding one does: the reader opens for twenty seconds, and whatever is held // against it comes back as a number. Nothing is written by this — the card lands @@ -1525,22 +1556,42 @@ async function scanRfidCard() { if (!sn || rfidBusy.value) return; rfidBusy.value = "scan"; rfidError.value = ""; - rfidCountdown.value = 20; - const tick = setInterval(() => { - rfidCountdown.value = Math.max(0, rfidCountdown.value - 1); - }, 1000); try { - const res = await api.scanAnkerRfidCard(sn); - if (res?.tapped && res.card) { - newCardNumber.value = res.card; - } else { + const card = await readCardAtCharger(sn); + if (card) newCardNumber.value = card; + else rfidError.value = t("charging.rfid.tapNone"); + } catch (e) { + rfidError.value = e.message; + } finally { + rfidBusy.value = ""; + } +} + +// The same tap, carried through to the end: the reader opens, and whatever is +// held against it is written without a second press. Enrolling a card happens at +// the charger with the card in your hand — the walk back to the keyboard to +// press Add was the whole cost of the two-step version. +// +// Nothing new is written by this that the two buttons above could not write +// between them; it is the same read and the same write, with nothing to do in +// between. The number lands in the box on the way past, so a write that fails +// leaves something to look at and retry rather than a card nobody can name. +async function tapAndSaveRfidCard() { + const sn = detailSn.value; + if (!sn || rfidBusy.value) return; + rfidBusy.value = "tapSave"; + rfidError.value = ""; + try { + const card = await readCardAtCharger(sn); + if (!card) { rfidError.value = t("charging.rfid.tapNone"); + return; } + newCardNumber.value = card; + await writeCard(sn, card); } catch (e) { rfidError.value = e.message; } finally { - clearInterval(tick); - rfidCountdown.value = 0; rfidBusy.value = ""; } } @@ -2271,7 +2322,22 @@ onMounted(async () => { > {{ rfidBusy === "scan" ? t("charging.rfid.tapping", { n: rfidCountdown }) : t("charging.rfid.tap") }} -
{{ t("charging.rfid.tapHint") }}
+ + ++ {{ t("charging.rfid.tapHint") }} +
+{{ t("charging.rfid.tapSaveHint") }}
{{ t("charging.rfid.inferred") }}