aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/index-dock.js
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/index-dock.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/index-dock.js62
1 files changed, 53 insertions, 9 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/index-dock.js b/packages/meshbay-hub/src/meshbay_hub/static/index-dock.js
index 6c89eb6..47a5818 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/index-dock.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/index-dock.js
@@ -6,7 +6,7 @@ import { navigate, session } from './hub-client.js';
import { useStickyBand } from './sticky.js';
import { Icon } from './icon.js';
import {
- fromLoopback, fromPush, mergeActivity, dockRows, hideRow, percent, queuedCount,
+ fromLoopback, fromPush, mergeActivity, dockRows, hideRow, pauseRow, percent, queuedCount,
} from './index-dock-model.js';
// ── Indexing dock ───────────────────────────────────────────────────────────
@@ -29,6 +29,9 @@ const ABSENT_POLL_MS = 30000;
// rather than a prop through App: the page and the dock are far apart, and a
// push every two seconds must not re-render the whole application.
const pushed = new Map();
+// How to pause a root of that group over the page's connection, signed by its
+// operator — the dock holds no transport of its own.
+const pushedPause = new Map();
const listeners = new Set();
/**
@@ -36,11 +39,17 @@ const listeners = new Set();
* member keeps the sidebar dot. `status` is the push (or the handshake ack's
* `indexing`); `roots` the roots table the page opened, which names `root_pos`.
* `null` when the page lets go of the group: nothing will update the entry any
- * more, and a stale "scanning" would stay on screen for ever.
+ * more, and a stale "scanning" would stay on screen for ever. `pause(rootName)`
+ * is what the row's pause button calls when no node answers on this machine.
*/
-export function reportIndexPush(groupId, status, roots) {
- if (status) pushed.set(groupId, fromPush(groupId, status, roots));
- else pushed.delete(groupId);
+export function reportIndexPush(groupId, status, roots, pause) {
+ if (status) {
+ pushed.set(groupId, fromPush(groupId, status, roots));
+ if (pause) pushedPause.set(groupId, pause);
+ } else {
+ pushed.delete(groupId);
+ pushedPause.delete(groupId);
+ }
for (const fn of listeners) fn();
}
@@ -96,7 +105,7 @@ function usePushedActivity() {
return snapshot;
}
-function DockRow({ row, onOpen, onHide }) {
+function DockRow({ row, error, onOpen, onPause, onHide }) {
const done = row.state === 'done';
const waiting = !done && !row.scanning;
const listing = !done && row.scanning && !row.totalBytes;
@@ -109,7 +118,9 @@ function DockRow({ row, onOpen, onHide }) {
const where = row.root ? `${row.groupName} › ${row.root}` : row.groupName;
let meta = '';
- if (listing) {
+ if (error) {
+ meta = error;
+ } else if (listing) {
meta = t('indexdock.listing');
} else if (!done && !waiting) {
meta = [
@@ -133,7 +144,8 @@ function DockRow({ row, onOpen, onHide }) {
onClick=${onOpen}>
<span class="index-dock-head">
<span class="index-dock-title"><strong>${status}</strong> ${where}</span>
- ${meta && html`<span class="index-dock-meta">${meta}</span>`}
+ ${meta && html`<span class="index-dock-meta ${error ? 'index-dock-error' : ''}"
+ role=${error ? 'alert' : null}>${meta}</span>`}
</span>
<span class="index-progress-bar index-dock-bar">
<span class="index-progress-fill ${moving ? 'index-dock-indeterminate' : ''}"
@@ -141,6 +153,12 @@ function DockRow({ row, onOpen, onHide }) {
</span>
${next && html`<span class="index-dock-next">${next}</span>`}
</button>
+ ${!done && onPause && html`
+ <button type="button" class="index-dock-hide" title=${t('indexdock.pause')}
+ aria-label=${t('indexdock.pause')} onClick=${onPause}>
+ <${Icon} name="pause" />
+ </button>
+ `}
${!done && html`
<button type="button" class="index-dock-hide" title=${t('indexdock.hide')}
aria-label=${t('indexdock.hide')} onClick=${onHide}>
@@ -156,6 +174,7 @@ export function IndexingDock({ groups }) {
const remote = usePushedActivity();
const memory = useRef({});
const [, setTick] = useState(0);
+ const [errors, setErrors] = useState({});
// Publishes `--index-dock-h` for the sidebar, as the music bar publishes its
// own; withdrawn when the dock renders nothing.
const band = useStickyBand('--index-dock-h');
@@ -173,11 +192,36 @@ export function IndexingDock({ groups }) {
return () => clearInterval(id);
}, [pending]);
+ // A whole-root walk names its root; a burst of file events does not, and
+ // there is nothing to pause. The local node first, as for the figures.
+ const pauser = (row) => {
+ if (row.state !== 'running' || !row.scanning || !row.root) return null;
+ if (local[row.groupId]) {
+ return (rootName) => platform.node.op('pauseRoot', { groupId: row.groupId, rootName });
+ }
+ return pushedPause.get(row.groupId) || null;
+ };
+
+ const pause = async (row, via) => {
+ const before = memory.current;
+ memory.current = pauseRow(memory.current, row.groupId);
+ setErrors((e) => ({ ...e, [row.groupId]: '' }));
+ setTick((n) => n + 1);
+ try {
+ await via(row.root);
+ } catch (err) {
+ // Still running: put the row back, and say why.
+ memory.current = { ...memory.current, [row.groupId]: before[row.groupId] };
+ setErrors((e) => ({ ...e, [row.groupId]: platform.bridgeMessage(err) }));
+ }
+ };
+
if (!rows.length) return null;
return html`
<div class="index-dock" ref=${band}>
${rows.map((row) => html`
- <${DockRow} key=${row.groupId} row=${row}
+ <${DockRow} key=${row.groupId} row=${row} error=${errors[row.groupId] || ''}
+ onPause=${pauser(row) && (() => pause(row, pauser(row)))}
onOpen=${() => {
session.openGroupTab = { groupId: row.groupId, tab: 'settings' };
navigate('/group/' + row.groupId);