openvibers/openvibe.network Commits

HO1, universal approvals: the hub shows the kind of every act that waits for you (#652)

OP OpenVibers committed 5 days ago · ff8e9f6797 · 3 files, +26 −3

The Action Center (plan HO1) merges each service's waiting items into one list. An approval now arrives with
the kind of act in plain words (publishing for others to see, sending something to someone, spending your
money), the thread it came from, the agent by its name and its own page. The tests lock that shape in, in
the merged feed and in the "Needs you" list on the account home: the kind is in plain words, the agent is
named, and Review opens the approval itself — the hub never approves with one tap.

Changes

ddiff --git a/public/my.html b/public/my.html
iindex b04b1b54..64d0fdae 100644
---- a/public/my.html
++++ b/public/my.html
@@@ -54,7 +54,10 @@
<div class="page">
<!-- Needs you (plan OpenVibe_Seven_Domains_Plan §4): the Action Center's waiting items, first on the screen.
my/today.js fills it from GET /api/v1/me/actions; a waiting item has one button (Approve, Try again, or
-- Open), and an empty queue leaves the section out. The hero and the sections follow unchanged. -->
++ Open), and an empty queue leaves the section out. The hero and the sections follow unchanged.
++ An approval says what kind of act it is in plain words (plan HO1: publishing for others to see, sending
++ something to someone, spending your money), which agent asked and in which thread; the agent's name is
++ shown, never its id. Review opens the approval itself — the hub never approves with one tap. -->
<section class="ny" id="needs-you" hidden aria-labelledby="needs-you-h">
<div class="ny-h">
<h2 id="needs-you-h"><span class="ny-dot" aria-hidden="true"></span>Needs you</h2>
ddiff --git a/test/me-actions.test.js b/test/me-actions.test.js
iindex 0e1df3ca..b07cc379 100644
---- a/test/me-actions.test.js
++++ b/test/me-actions.test.js
@@@ -41,6 +41,10 @@ const svc = http.createServer((req, res) => {
if (u.pathname === '/actor/api/v1/actions') return json(200, { items: [
item(cancelled ? { state: 'cancelled', bucket: 'failed', actions: ['open'], updated_at: iso(1) } : {}),
item({ id: 'apr_1', kind: 'approval', title: 'Spend $0.40 on a desk hour', bucket: 'waiting', state: 'pending', actions: ['approve', 'decline', 'open'], updated_at: iso(5), href: 'https://openvibe.actor/me/approvals' }),
++ // HO1: an approval of one of the new kinds, with the kind in plain words, the agent by its name and
++ // its own page.
++ item({ id: 'apr_publish', kind: 'approval', title: 'Publish your post on openvibe.blog', summary: 'publishing for others to see · Weekly digest', agent: 'Vibe', bucket: 'waiting', state: 'pending', actions: ['approve', 'decline', 'open'], updated_at: iso(2), href: 'https://openvibe.actor/me/approvals/apr_publish' }),
++ item({ id: 'apr_send', kind: 'approval', title: 'Send one email to the venue', summary: 'sending something to someone · Trip planner', agent: 'Trip planner', bucket: 'waiting', state: 'pending', actions: ['approve', 'decline', 'open'], updated_at: iso(3), href: 'https://openvibe.actor/me/approvals/apr_send' }),
item({ id: 'tsk_bad', href: 'http://openvibe.actor/tasks/x' }),
item({ id: 'tsk_spoof', service: 'codes' }),
item({ id: 'agt_1', bucket: 'scheduled', state: 'scheduled', next_run_at: new Date(T0 + 3600000).toISOString(), actions: ['open'] }),
@@@ -96,8 +100,16 @@ try {
assert.ok(validate('network.me-actions-result@1', doc).valid, JSON.stringify(validate('network.me-actions-result@1', doc).errors));
assert.deepStrictEqual(doc.sources, { network: 'ok', actor: 'ok', codes: 'not_connected', ai: 'timeout', domains: 'ok' });
assert.strictEqual(doc.partial, true, 'a timeout makes the answer partial');
-- assert.deepStrictEqual(doc.counts, { running: 1, waiting: 3, scheduled: 1, done: 0, failed: 1 });
-- assert.deepStrictEqual(doc.buckets.waiting.map((x) => x.id), ['cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ', 'apr_1', 'dpr_1'], 'newest first');
++ assert.deepStrictEqual(doc.counts, { running: 1, waiting: 5, scheduled: 1, done: 0, failed: 1 });
++ assert.deepStrictEqual(doc.buckets.waiting.map((x) => x.id), ['cnf_01JAB2C3D4E5F6G7H8J9K0MNPQ', 'apr_publish', 'apr_send', 'apr_1', 'dpr_1'], 'newest first');
++ // The new kinds reach the hub as they are (plan HO1): the kind in plain words, the agent by its name, and
++ // the approval's own page. The person reviews it there; the hub never approves with one tap.
++ const published = doc.buckets.waiting.find((x) => x.id === 'apr_publish');
++ assert.deepStrictEqual([published.kind, published.bucket, published.state, published.agent], ['approval', 'waiting', 'pending', 'Vibe']);
++ assert.match(published.summary, /publishing for others to see/, 'the kind in plain words');
++ assert.match(published.summary, /Weekly digest/, 'the thread it came from');
++ assert.strictEqual(published.href, 'https://openvibe.actor/me/approvals/apr_publish');
++ assert.deepStrictEqual(published.actions, ['approve', 'decline', 'open']);
const cnf = doc.buckets.waiting[0];
assert.deepStrictEqual([cnf.service, cnf.kind, cnf.title, cnf.summary, cnf.href, cnf.actions], ['network', 'approval', 'Send one email to the venue', 'Trip planner asks to use mail.send', 'https://openvibe.network/my#approvals', ['approve', 'decline', 'open']]);
const all = Object.values(doc.buckets).flat().map((x) => x.id);
ddiff --git a/test/my-today.test.js b/test/my-today.test.js
iindex 5cdddfb4..30e6d674 100644
---- a/test/my-today.test.js
++++ b/test/my-today.test.js
@@@ -165,11 +165,19 @@ assert.match(CSS, /@media \(min-width:1000px\)\{[\s\S]*\.settings\{display:grid;
p.sandbox.renderNeedsYou({ buckets: { waiting: [
{ service: 'actor', id: 'apr_1', kind: 'approval', title: 'Run a command', agent: 'agt01M6VEKFK36XWYB3CSY1RE60YN', href: 'https://openvibe.actor/me/approvals/apr_1', actions: ['approve', 'open'] },
{ service: 'actor', id: 'tsk_2', kind: 'agent_task', title: 'Summarise the inbox', href: 'https://openvibe.actor/tasks/tsk_2', actions: ['retry', 'open'] },
++ // An approval of one of the new kinds (plan HO1): the kind in plain words, the agent by its name, and
++ // the approval's own page. Review opens it; the hub never approves with one tap.
++ { service: 'actor', id: 'apr_publish', kind: 'approval', title: 'Publish your post on openvibe.blog', summary: 'publishing for others to see · Weekly digest', agent: 'Vibe', href: 'https://openvibe.actor/me/approvals/apr_publish', actions: ['approve', 'decline', 'open'] },
] } });
const two = p.ids['needs-you-list'].children;
assert.ok(!/agt01M6/.test(two[0].textContent), 'a raw agent id is left out');
assert.strictEqual(two[0].all((c) => c.tagName === 'A')[0].href, 'https://openvibe.actor/me/approvals/apr_1', 'Review opens the approval');
assert.strictEqual(two[1].all((c) => c.tagName === 'BUTTON')[0].textContent, 'Try again');
++ assert.match(two[2].textContent, /publishing for others to see/, 'the kind of the act is in plain words');
++ assert.match(two[2].textContent, /Weekly digest/, 'the thread it came from');
++ assert.match(two[2].textContent, /Vibe/, 'the agent by its name');
++ assert.strictEqual(two[2].all((c) => c.tagName === 'A')[0].href, 'https://openvibe.actor/me/approvals/apr_publish');
++ assert.strictEqual(two[2].all((c) => c.tagName === 'BUTTON').length, 0, 'no one-tap approve on an approval');
p.sandbox.renderNeedsYou({ buckets: { waiting: [] } });
assert.strictEqual(p.ids['needs-you'].hidden, true, 'an empty queue leaves the section out');