summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/app.js15
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/de.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/en.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/es.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/it.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js1
-rw-r--r--packages/meshbay-hub/tests/test_transfers.py109
12 files changed, 132 insertions, 2 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js
index 0394af6..de47982 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/app.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js
@@ -159,10 +159,20 @@ function TransferWidget() {
const running = items.filter(i => i.status === 'running');
const waiting = items.filter(
i => i.status === 'queued' || i.status === 'preparing');
+ // Its own group, and not a leftover.
+ //
+ // "Finished" used to be defined as everything that is not running, queued or
+ // preparing — a definition by exclusion, which quietly swallowed `paused` the
+ // day pausing shipped. A transfer somebody stopped on purpose then sat under
+ // "Finished", beside the ones that are actually over, offering a resume
+ // button in the section of things that cannot be resumed.
+ const paused = items.filter(i => i.status === 'paused');
const finished = items.filter(
i => i.status !== 'running' && i.status !== 'queued'
- && i.status !== 'preparing');
- const active = running.length + waiting.length;
+ && i.status !== 'preparing' && i.status !== 'paused');
+ // Paused counts as active: it is not over, the person means to come back to
+ // it, and the badge saying nothing is happening would be a lie.
+ const active = running.length + waiting.length + paused.length;
// Grouped, and in this order: what is moving, what is waiting, what is over.
// Re-sorting the flat list on every emit made rows jump under the pointer
@@ -171,6 +181,7 @@ function TransferWidget() {
const groups = [
['running', running],
['waiting', waiting],
+ ['paused', paused],
['finished', finished],
].filter(([, rows]) => rows.length);
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
index fd63ddf..738b131 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
@@ -584,6 +584,7 @@ export default {
'transfers.summary': '{running} laufend · {waiting} wartend',
'transfers.group_running': 'Laufend',
'transfers.group_waiting': 'Wartend',
+ 'transfers.group_paused': 'Angehalten',
'transfers.group_finished': 'Abgeschlossen',
'transfers.cancel_one': '{name} abbrechen',
'transfers.pause': 'Anhalten',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
index e9d4072..0b9de8a 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
@@ -700,6 +700,7 @@ export default {
'transfers.summary': '{running} running · {waiting} waiting',
'transfers.group_running': 'Running',
'transfers.group_waiting': 'Waiting',
+ 'transfers.group_paused': 'Paused',
'transfers.group_finished': 'Finished',
'transfers.cancel_one': 'Cancel {name}',
'transfers.pause': 'Pause',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
index 4fa19e7..8da8f9f 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
@@ -580,6 +580,7 @@ export default {
'transfers.summary': '{running} en curso · {waiting} en espera',
'transfers.group_running': 'En curso',
'transfers.group_waiting': 'En espera',
+ 'transfers.group_paused': 'En pausa',
'transfers.group_finished': 'Finalizados',
'transfers.cancel_one': 'Cancelar {name}',
'transfers.pause': 'Pausar',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
index 6fba91d..3135c03 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
@@ -583,6 +583,7 @@ export default {
'transfers.summary': '{running} en cours · {waiting} en attente',
'transfers.group_running': 'En cours',
'transfers.group_waiting': 'En attente',
+ 'transfers.group_paused': 'En pause',
'transfers.group_finished': 'Terminés',
'transfers.cancel_one': 'Annuler {name}',
'transfers.pause': 'Suspendre',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
index 76b3101..91f0e9b 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
@@ -583,6 +583,7 @@ export default {
'transfers.summary': '{running} in corso · {waiting} in attesa',
'transfers.group_running': 'In corso',
'transfers.group_waiting': 'In attesa',
+ 'transfers.group_paused': 'In pausa',
'transfers.group_finished': 'Completati',
'transfers.cancel_one': 'Annulla {name}',
'transfers.pause': 'Sospendi',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
index cfc1125..b352188 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
@@ -575,6 +575,7 @@ export default {
'transfers.summary': '実行中 {running} · 待機中 {waiting}',
'transfers.group_running': '実行中',
'transfers.group_waiting': '待機中',
+ 'transfers.group_paused': '一時停止中',
'transfers.group_finished': '完了',
'transfers.cancel_one': '{name} をキャンセル',
'transfers.pause': '一時停止',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
index 29cc566..e0a799d 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
@@ -584,6 +584,7 @@ export default {
'transfers.summary': '{running} bezig · {waiting} wachtend',
'transfers.group_running': 'Bezig',
'transfers.group_waiting': 'Wachtend',
+ 'transfers.group_paused': 'Gepauzeerd',
'transfers.group_finished': 'Voltooid',
'transfers.cancel_one': '{name} annuleren',
'transfers.pause': 'Pauzeren',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
index 38fe714..e6319b0 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
@@ -596,6 +596,7 @@ export default {
'transfers.summary': '{running} w toku · {waiting} oczekuje',
'transfers.group_running': 'W toku',
'transfers.group_waiting': 'Oczekuje',
+ 'transfers.group_paused': 'Wstrzymane',
'transfers.group_finished': 'Zakończone',
'transfers.cancel_one': 'Anuluj {name}',
'transfers.pause': 'Wstrzymaj',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js
index 5942b77..02c8356 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js
@@ -582,6 +582,7 @@ export default {
'transfers.summary': '{running} em andamento · {waiting} aguardando',
'transfers.group_running': 'Em andamento',
'transfers.group_waiting': 'Aguardando',
+ 'transfers.group_paused': 'Pausados',
'transfers.group_finished': 'Concluídos',
'transfers.cancel_one': 'Cancelar {name}',
'transfers.pause': 'Pausar',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js
index 56af6c8..6c4c73f 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js
@@ -563,6 +563,7 @@ export default {
'transfers.summary': '进行中 {running} · 等待中 {waiting}',
'transfers.group_running': '进行中',
'transfers.group_waiting': '等待中',
+ 'transfers.group_paused': '已暂停',
'transfers.group_finished': '已完成',
'transfers.cancel_one': '取消 {name}',
'transfers.pause': '暂停',
diff --git a/packages/meshbay-hub/tests/test_transfers.py b/packages/meshbay-hub/tests/test_transfers.py
index f27d4fd..d19a458 100644
--- a/packages/meshbay-hub/tests/test_transfers.py
+++ b/packages/meshbay-hub/tests/test_transfers.py
@@ -770,3 +770,112 @@ def test_an_upload_handed_a_lease_it_cannot_recreate_is_not_offered_pause(tmp_pa
t.cancel(id);
""", tmp_path)
assert out == ["status:running"]
+
+
+def test_pausing_one_transfer_leaves_the_others_alone(tmp_path):
+ """Reported: three downloads running, one upload paused, and the three
+ downloads lost their pause buttons.
+
+ The button is drawn from `pausable` and the status, so this asks the store
+ what it says about the other three at the moment one of them pauses.
+ """
+ out = _run(_lease_stub() + """
+ const t = new TransferStore();
+ const leases = [];
+ const mk = (kind, name) => t.start({
+ kind, name, total: 100, pausable: true,
+ makeLease: () => { const l = new L(); leases.push(l); return l; },
+ run: async ({ signal, from }) => {
+ for (let i = from || 0; i < 40; i++) {
+ await new Promise(r => setTimeout(r, 5));
+ if (signal.aborted) { const e = new Error('c'); e.name = 'AbortError'; throw e; }
+ if (signal.paused) {
+ signal.resumeFrom = i;
+ const e = new Error('p'); e.name = 'PausedError'; throw e;
+ }
+ }
+ },
+ });
+ mk('download', 'd1'); mk('download', 'd2'); mk('download', 'd3');
+ mk('upload', 'u1');
+ await new Promise(r => setTimeout(r, 5));
+ for (const l of leases) l.grant();
+ await new Promise(r => setTimeout(r, 20));
+ const up = t.list().find(i => i.kind === 'upload');
+ say('before:' + t.list().filter(
+ i => i.kind === 'download' && i.pausable && i.status === 'running').length);
+ t.pause(up.id);
+ await new Promise(r => setTimeout(r, 40));
+ const rows = t.list();
+ say('after:' + rows.filter(
+ i => i.kind === 'download' && i.pausable && i.status === 'running').length);
+ say('statuses:' + rows.map(i => i.kind[0] + ':' + i.status).join(','));
+ for (const r of rows) t.cancel(r.id);
+ """, tmp_path)
+ assert out[0] == "before:3"
+ assert out[1] == "after:3", (
+ f"pausing the upload changed the downloads — {out[2]}")
+
+
+def test_a_paused_transfer_is_not_filed_under_finished(tmp_path):
+ """"Finished" was defined by exclusion — everything that is not running,
+ queued or preparing — so it quietly swallowed `paused` the day pausing
+ shipped. A transfer somebody stopped on purpose then sat beside the ones
+ that are actually over, offering a resume button in the section of things
+ that cannot be resumed.
+
+ The three filters are lifted out of `app.js` and run, rather than described
+ here: a copy of them in this file would agree with a broken version by
+ construction.
+ """
+ src = (STATIC / "app.js").read_text()
+ start = src.index(" const running = items.filter(")
+ block = src[start:src.index("const active =", start)]
+
+ script = tmp_path / "groups.mjs"
+ script.write_text("""
+const items = [
+ { id: 1, status: 'running' },
+ { id: 2, status: 'queued' },
+ { id: 3, status: 'preparing' },
+ { id: 4, status: 'paused' },
+ { id: 5, status: 'done' },
+ { id: 6, status: 'failed' },
+ { id: 7, status: 'cancelled' },
+];
+""" + block + """
+const seen = { running, waiting, paused, finished };
+console.log(JSON.stringify(Object.fromEntries(
+ Object.entries(seen).map(([k, v]) => [k, v.map(i => i.id)]))));
+""")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+ assert proc.returncode == 0, proc.stderr
+ groups = json.loads(proc.stdout)
+
+ assert groups["paused"] == [4]
+ assert groups["finished"] == [5, 6, 7], (
+ f"paused landed in {groups['finished']}")
+ assert groups["running"] == [1] and groups["waiting"] == [2, 3]
+ # Every row appears exactly once: a state added later that lands in no group
+ # is a transfer the panel simply does not show.
+ placed = sum((groups[k] for k in groups), [])
+ assert sorted(placed) == [1, 2, 3, 4, 5, 6, 7]
+
+
+def test_a_paused_transfer_still_counts_as_active(tmp_path):
+ """The badge says how much is going on. A paused transfer is not over — the
+ person means to come back to it — so counting it as nothing would be a
+ panel that says "0" over work that is still there."""
+ src = (STATIC / "app.js").read_text()
+ start = src.index(" const running = items.filter(")
+ block = src[start:src.index("\n\n", src.index("const active =", start))]
+
+ script = tmp_path / "active.mjs"
+ script.write_text("""
+const items = [{ id: 1, status: 'paused' }, { id: 2, status: 'done' }];
+""" + block + """
+console.log(JSON.stringify({ active }));
+""")
+ proc = subprocess.run(["node", str(script)], capture_output=True, text=True)
+ assert proc.returncode == 0, proc.stderr
+ assert json.loads(proc.stdout)["active"] == 1