import { Fragment, useEffect, useState } from "react"; import { INK, RED, SKY, FONT_MONO } from "../../theme"; import { POST_HOURS } from "../../constants"; import { postState, enablePost, disablePost, setPostHours } from "../../lib/postbode"; import { meldingFout } from "../../lib/plugin-fouten"; import { osNaam } from "../../lib/platform"; /* * Notificaties, bij Mijn profiel: een schakelaar en de bezorgmomenten. Uit tot je hem aanzet — een * melding is het enige in deze app dat uit zichzelf om aandacht vraagt. * * Kort gehouden met opzet. Hoe het werkt en waarom het zo werkt staat in de privacyverklaring en de * README, niet hier: een instelscherm dat zichzelf verantwoordt is een instelscherm dat je niet * leest. De momenten zijn hele uren (Android bepaalt zelf wanneer een app op de achtergrond aan de * beurt is, dus minuten zouden precisie beloven die er niet is) en ook te verzetten terwijl de * schakelaar uit staat. `getSince()` levert de hoogste brief die de app nu kent; zie lib/postbode.js. */ const uurLabel = (u) => String(u).padStart(2, "0") + ":00"; const ALLE_UREN = Array.from({ length: 24 }, (_, u) => u); /* Wat "uit" is in een keuzemenu: een lege waarde, want een uur is het niet */ const UIT = ""; export default function PostSection({ token, getSince }) { const [state, setState] = useState(null); // { enabled, allowed, hours } — null zolang onbekend const [busy, setBusy] = useState(false); const [denied, setDenied] = useState(false); const [fout, setFout] = useState(null); // een afgewezen aanroep, al geduid (zie lib/plugin-fouten) /* * Elk antwoord van de plugin komt hier langs. Een `fout` is geen state: hij zegt niets over hoe de * bezorging ervoor staat, alleen dat deze poging strandde — dus hij gaat apart, en wat er op het * scherm stond blijft staan in plaats van te worden overschreven met een halve waarheid. */ const verwerk = (antwoord) => { if (!antwoord) return false; // geen plugin (browser, of een app-versie die hem niet kent) if (antwoord.fout) { setFout(meldingFout(antwoord.fout)); return false; } setState((s) => ({ ...s, ...antwoord })); return true; }; useEffect(() => { let levend = true; postState().then((s) => levend && verwerk(s)); return () => { levend = false; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); /* * Altijd evenveel keuzemenu's als POST_HOURS momenten kent, gevuld met wat er bewaard is en * verder `null` — dat is een menu op "uit". De momenten zijn een verzameling tijdstippen, dus wat * terugkomt is oplopend en ontdubbeld; de menu's volgen die volgorde. */ const bewaard = state?.hours ?? POST_HOURS.filter((u) => u !== null); const uren = POST_HOURS.map((_, i) => bewaard[i] ?? null); const actief = uren.filter((u) => u !== null); const schakel = async (aan) => { setBusy(true); setDenied(false); setFout(null); const next = aan ? await enablePost({ token, since: getSince(), hours: actief }) : await disablePost(); if (next?.denied) setDenied(true); verwerk(next); setBusy(false); }; /* Eén moment verzetten of uitzetten (uur = null). Het laatste moment kan niet uit, zie hieronder. */ const verzet = async (index, uur) => { const volgende = uren.map((u, i) => (i === index ? uur : u)).filter((u) => u !== null); setState((s) => ({ ...(s ?? {}), hours: volgende })); // meteen zichtbaar, ook als de plugin traag is setFout(null); verwerk(await setPostHours(volgende)); }; const aan = state?.enabled === true; /* Aan in de app, uit in de systeeminstellingen: dan verschijnt er niets en hoort dat er te staan */ const gesmoord = aan && state?.allowed === false; const keuze = { padding: "6px 8px", border: `2px solid ${INK}`, background: "#fff", color: INK, fontSize: 14, fontWeight: 600, fontFamily: FONT_MONO, }; return (
Instellen lukte niet: {fout}
)} {denied && ({osNaam()} liet de melding niet toe. Zet meldingen aan bij de instellingen van je toestel.
)} {gesmoord && (Meldingen staan uit bij de instellingen van je toestel.
)}