source: Klonkt/src/services/guardianship/help.js@ 9ce14b8

main
Last change on this file since 9ce14b8 was 9ce14b8, checked in by roboburr <roboburr@…>, 5 weeks ago

Een hulpvraag van een oud-ward gaat naar de geschiedenis (shaer-lgo, vervolg)

Bart heeft een hulpvraag staan van iemand die hij niet meer bewaakt, en kwam er
niet vanaf. Terecht: de knop stond er wel, maar de markeerroute eist dat het NOG
je ward is en antwoordt anders met 403. Klikken deed dus niets, zonder uitleg.

HET LOSLAAT-SCHERM BELOOFDE DIT AL LETTERLIJK -- "je krijgt geen hulpvragen meer
van ze". Nieuwe komen inderdaad niet meer binnen, maar wat er al lag bleef in de
lijst staan die om aandacht vraagt. Dit is dus geen nieuwe keuze maar een belofte
die de app al doet en niet nakwam.

NIET ALS AFGEHANDELD. Dat lag voor de hand en is fout: afhandelen is een claim
over een kind waar je niets meer over te zeggen hebt, en die claim wordt ook nog
rondgestuurd naar de guardians die er wel nog zijn. Er is een derde uitkomst en
die leest als wat hij is: niet meer van jou. De vraag zakt naar de geschiedenis,
zonder knoppen, met een regel die zegt dat hun andere guardians er nog zijn.

Dat laatste is geen troost maar een feit: een guardianship eindigt nooit bij de
LAATSTE guardian (3.4 -- dan is het emancipatie). Er blijft altijd iemand over
voor wie de vraag wel open staat, dus dit dooft geen reddingsboei.

Wat er al gebeurd is blijft leesbaar: was je erbij toen je nog guardian was, dan
blijft die oppik met naam staan. Er wordt niets herschreven.

Vier toetsen, drie talen. Suite 615/615.

  • Property mode set to 100644
File size: 6.3 KB
Line 
1/**
2 * Wie er op een hulpvraag af is, en wanneer hij is afgesloten (shaer-lgo).
3 *
4 * Een hulpvraag (FEP-633c 5.2.1) gaat naar ALLE guardians van een kind, die op
5 * verschillende servers zitten. Zonder gedeelde staat denken er twee dat de
6 * ander het oppakt -- en dat is precies het scenario waar de reddingsboei voor
7 * bestaat.
8 *
9 * DE FAALSTAND IS HIER NIET VEILIG, en dat maakt dit anders dan elke gate. Bij
10 * een gate is "dicht" het veilige antwoord. Hier is de faalstand "iedereen denkt
11 * dat het geregeld is", en dat is gevaarlijker dan geen markering. Daaruit volgt
12 * de regel die overal in dit bestand terugkomt: bij twijfel is een hulpvraag
13 * OPEN.
14 *
15 * Twee besluiten van Bart (7-8) zitten in de vorm:
16 *
17 * OPGEPIKT mag stapelen en vervalt niet, maar VEROUDERT zichtbaar. Twee mensen
18 * die tegelijk reageren op een kind is geen probleem; twee die allebei niets
19 * doen omdat de ander het "geclaimd" had, wel. En een signaal dat vanzelf
20 * verdwijnt laat een hulpvraag er onaangeroerd uitzien terwijl er iemand mee
21 * bezig is.
22 *
23 * AFGEHANDELD kent geen terugdraai. Sluiten gebeurt met een stevige
24 * bevestiging, en leeft de vraag daarna nog, dan wordt hij OPNIEUW GESTELD --
25 * een nieuwe hulpvraag. Er wordt niets herschreven, er wordt toegevoegd.
26 */
27
28import db from '../../config/database.js';
29
30let _s = null;
31function stmts() {
32 if (!_s) {
33 _s = {
34 ins: db.prepare(`INSERT OR IGNORE INTO ap_help_state (note_uri, guardian_uri, kind, guardian_handle)
35 VALUES (?,?,?,?)`),
36 forNote: db.prepare('SELECT * FROM ap_help_state WHERE note_uri = ? ORDER BY created_at ASC'),
37 forNotes: db.prepare('SELECT * FROM ap_help_state WHERE note_uri IN (SELECT value FROM json_each(?))'),
38 };
39 }
40 return _s;
41}
42
43/** Leg vast dat iemand deze hulpvraag heeft opgepikt of afgesloten. */
44export function record(noteUri, guardianUri, kind, handle = null) {
45 if (!noteUri || !guardianUri) return;
46 const k = kind === 'handled' ? 'handled' : 'pickup';
47 try { stmts().ins.run(noteUri, guardianUri, k, handle); } catch { /* nooit dragend */ }
48}
49
50/**
51 * De staat van een hulpvraag, uit zijn rijen. Puur, zodat de regels te toetsen
52 * zijn zonder database of scherm.
53 *
54 * `ageMs` is de leeftijd van de OUDSTE oppik. Daar tekent het scherm mee dat een
55 * signaal oud wordt -- niets verdwijnt, maar je ziet wel dat er misschien niets
56 * meer gebeurt.
57 */
58export function helpStatus(rows, now = Date.now()) {
59 const list = rows || [];
60 const pickups = list.filter((r) => r.kind === 'pickup');
61 const done = list.find((r) => r.kind === 'handled') || null;
62 const stamp = (r) => { const t = Date.parse(r.created_at); return isNaN(t) ? null : t; };
63 const oudste = pickups.map(stamp).filter((t) => t !== null).sort((a, b) => a - b)[0];
64 return {
65 // Namen erbij: "door wie" was de hele vraag. Zonder dat is het een vinkje.
66 pickedUpBy: pickups.map((r) => ({ uri: r.guardian_uri, handle: r.guardian_handle || null, at: r.created_at })),
67 handled: done ? { uri: done.guardian_uri, handle: done.guardian_handle || null, at: done.created_at } : null,
68 // Alleen betekenisvol zolang er niets is afgesloten.
69 ageMs: (!done && oudste) ? Math.max(0, now - oudste) : null,
70 // Waar het scherm op afgaat. Bij twijfel OPEN: een lege lijst, een rij die we
71 // niet kunnen lezen, wat dan ook -- alles wat geen expliciete afsluiting is,
72 // is een hulpvraag die nog op iemand wacht.
73 open: !done,
74 };
75}
76
77/**
78 * Een hulpvraag van iemand die je NIET MEER bewaakt.
79 *
80 * Het loslaat-scherm belooft dit al letterlijk: "je krijgt geen hulpvragen meer
81 * van ze". Nieuwe komen inderdaad niet meer binnen, maar wat er al lag bleef in
82 * de open lijst staan -- en was niet af te sluiten, want de markeerroute eist
83 * dat het nog je ward is en antwoordt anders met 403. De knop stond er dus wel
84 * en deed niets.
85 *
86 * Zo'n vraag is niet AFGEHANDELD -- dat zou een claim zijn over een kind waar je
87 * niets meer over te zeggen hebt, en die claim wordt ook nog rondgestuurd. Hij is
88 * niet meer van jou. Dat is een derde uitkomst en die hoort als zodanig te lezen.
89 *
90 * Veilig omdat een guardianship nooit bij de LAATSTE guardian eindigt (3.4,
91 * emancipatie): er blijft altijd iemand over voor wie de vraag wel open staat.
92 */
93export function withWardship(status, stillWard) {
94 if (stillWard) return status;
95 return { ...status, open: false, formerWard: true };
96}
97
98/** De staat van een hulpvraag zoals die nu is opgeslagen. */
99export function statusOf(noteUri, now = Date.now()) {
100 if (!noteUri) return helpStatus([], now);
101 try { return helpStatus(stmts().forNote.all(noteUri), now); } catch { return helpStatus([], now); }
102}
103
104/** Idem voor een hele lijst in een query, zodat een paneel geen N+1 wordt. */
105export function statusFor(noteUris, now = Date.now()) {
106 const uit = new Map();
107 const lijst = [...new Set((noteUris || []).filter(Boolean))];
108 if (!lijst.length) return uit;
109 let rijen = [];
110 try { rijen = stmts().forNotes.all(JSON.stringify(lijst)); } catch { rijen = []; }
111 const perNote = new Map();
112 for (const r of rijen) {
113 if (!perNote.has(r.note_uri)) perNote.set(r.note_uri, []);
114 perNote.get(r.note_uri).push(r);
115 }
116 for (const uri of lijst) uit.set(uri, helpStatus(perNote.get(uri) || [], now));
117 return uit;
118}
119
120/**
121 * De markering als bericht. Bewust een gewone directe note met een
122 * shaer:-markering, zoals de zwaai en de afwezigheidsmelding: dan reist het over
123 * de bestaande bezorging, en de WARD leest het als wat het is -- er komt iemand.
124 */
125export function markerNote({ id, me, noteUri, kind, to }) {
126 const k = kind === 'handled' ? 'handled' : 'pickup';
127 return {
128 id, type: 'Note', attributedTo: me, to,
129 [k === 'handled' ? 'shaer:helpHandled' : 'shaer:helpPickup']: noteUri,
130 content: k === 'handled'
131 ? '<p>Deze hulpvraag is afgehandeld.</p>'
132 : '<p>Ik kijk hiernaar.</p>',
133 };
134}
135
136/** Leest een binnengekomen note als markering, of null als hij er geen is. */
137export function parseMarker(object) {
138 if (!object || typeof object !== 'object') return null;
139 const pickup = object['shaer:helpPickup'];
140 const handled = object['shaer:helpHandled'];
141 if (typeof handled === 'string' && handled) return { kind: 'handled', noteUri: handled };
142 if (typeof pickup === 'string' && pickup) return { kind: 'pickup', noteUri: pickup };
143 return null;
144}
145
146export default { record, helpStatus, withWardship, statusOf, statusFor, markerNote, parseMarker };
Note: See TracBrowser for help on using the repository browser.