Een dating-app op basis van brieven, niet van gezichten!
Deze repository wordt als momentopname gepubliceerd: een clone bevat de volledige inhoud, maar geen commitgeschiedenis.
import { useEffect, useState } from "react";
import { Capacitor } from "@capacitor/core";
import { RED, BLUE, SKY, MUTE, FONT_MONO } from "../../theme";
import AirmailEdge from "../../components/AirmailEdge";
import { saveExport, bestandsgrootte } from "../../lib/export-file";
/*
* Inzage (AVG art. 15 en 20): de export van de server, als bestand. Wat erin zit staat hier in
* gewone taal; de JSON zelf wordt niet getoond — met de foto's als base64 erin is dat al gauw
* een tekstvak van tientallen megabytes, en niemand leest zijn brieven terug in een tekstvak.
* Het bestand is waar het om gaat, en dat werkt overal: in de app via het deelblad van het
* toestel, in de browser als download (zie src/lib/export-file.js).
*
* `data` is het object van api.exportMe(); onClose sluit.
*/
export default function ExportDialog({ data, onClose }) {
const native = Capacitor.isNativePlatform();
const [state, setState] = useState({ busy: false, done: null, error: null });
/* Escape sluit; de pagina eronder scrolt intussen niet mee (zoals de meldingsdialoog) */
useEffect(() => {
const onKey = (e) => e.key === "Escape" && onClose();
document.addEventListener("keydown", onKey);
const prev = document.body.style.overflow;
document.body.style.overflow = "hidden";
return () => {
document.removeEventListener("keydown", onKey);
document.body.style.overflow = prev;
};
}, [onClose]);
const save = async () => {
setState({ busy: true, done: null, error: null });
try {
const result = await saveExport(data);
setState({ busy: false, done: result.saved ? result : null, error: null });
} catch (e) {
setState({ busy: false, done: null, error: e.message });
}
};
const sent = data.letters.filter((l) => l.direction === "verzonden").length;
const received = data.letters.length - sent;
const photos = data.letters.filter((l) => l.photo).length;
const n = (count, one, many) => `${count} ${count === 1 ? one : many}`;
return (
<div
role="dialog"
aria-modal="true"
aria-label="Jouw gegevens"
className="dialog-backdrop"
style={{ background: "rgba(31,44,85,0.55)" }}
onClick={(e) => e.target === e.currentTarget && onClose()}
>
<div className="dialog-sheet" style={{ background: "#fff" }}>
<AirmailEdge />
<div style={{ padding: 20 }}>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 4 }}>
<h2 style={{ margin: 0, fontSize: 20 }}>Jouw gegevens</h2>
<button onClick={onClose} aria-label="Sluiten" style={{ background: "none", border: "none", fontSize: 22, color: MUTE, lineHeight: 1 }}>
×
</button>
</div>
<p style={{ fontFamily: FONT_MONO, fontSize: 11, color: MUTE, margin: "0 0 12px" }}>
alles wat we van je bewaren · {n(data.letters.length, "brief", "brieven")} ({sent} verstuurd, {received} ontvangen) ·{" "}
{n(photos, "foto", "foto's")} · {n(data.favorites.length, "favoriet", "favorieten")} · {data.blocks.length} geblokkeerd ·{" "}
{n(data.sessions.length, "ingelogd toestel", "ingelogde toestellen")}
</p>
<p style={{ fontSize: 14, color: MUTE, margin: "0 0 12px", lineHeight: 1.5 }}>
Account, profiel en filter, alle brieven van en aan jou (met de naam van de ander en de foto's erbij), favorieten,
blokkades en de tijdstippen van je sessies. Wachtwoord en sessietokens staan er niet in — die kennen wij zelf ook
niet in leesbare vorm.
</p>
<p style={{ fontSize: 14, color: MUTE, margin: "0 0 14px", lineHeight: 1.5 }}>
Het bestand is JSON: leesbare tekst, met de foto's er compleet in. Het staat hier niet op het scherm omdat het
daar te groot voor is — {native
? "kies hieronder waar je het bewaart (Bestanden, Drive, of mail het aan jezelf)."
: "het wordt gedownload naar je downloadmap."}
</p>
<button
onClick={save}
disabled={state.busy}
style={{
background: RED, color: "#fff", border: `2px solid ${RED}`, padding: "12px 18px", fontSize: 15, fontWeight: 700,
opacity: state.busy ? 0.6 : 1,
}}
>
{state.busy ? "Bezig…" : native ? "⤓ Bewaren of versturen" : "⤓ Downloaden (.json)"}
</button>
{state.done && (
<p style={{ fontFamily: FONT_MONO, fontSize: 12, color: BLUE, margin: "10px 0 0", background: SKY, padding: "8px 10px" }}>
✓ {state.done.name} · {bestandsgrootte(state.done.bytes)}
</p>
)}
{state.error && (
<p role="alert" style={{ fontFamily: FONT_MONO, fontSize: 12, color: RED, margin: "10px 0 0" }}>
het bestand kon niet bewaard worden: {state.error}
</p>
)}
<p style={{ fontSize: 13, color: MUTE, margin: "12px 0 0", lineHeight: 1.5 }}>
Bewaar het ergens waar niemand anders bij kan: hierin staan je brieven, en die van de mensen die jou schreven.
</p>
</div>
</div>
</div>
);
}