import { Capacitor } from "@capacitor/core"; import { Filesystem, Directory, Encoding } from "@capacitor/filesystem"; import { Share } from "@capacitor/share"; import { log } from "./log"; /* * De gegevensexport als bestand (AVG art. 15 en 20: inzage én overdraagbaarheid). Er is geen * webversie van de app, dus "download hem maar in de browser" is geen antwoord — de knop moet het * op de telefoon doen. * * Op Android/iOS kan dat niet met een blob-download: de webview van Capacitor kent geen * downloadmanager, een doet daar eenvoudigweg niets. Wat er wél is, is de weg die het * besturingssysteem zelf voor bestanden heeft: schrijf hem weg en geef hem aan het deelblad, waar * "Opslaan in Bestanden", Drive of een mailtje aan jezelf tussen staat. Dat is dezelfde handeling * als downloaden, alleen kiest de gebruiker de bestemming. * * Het bestand gaat naar de cachemap van de app: privéopslag waar geen andere app bij kan, en die * het systeem zelf mag opruimen. Bij een volgende export ruimen we onze eigen oude bestanden op — * een complete kopie van je brieven hoort niet te blijven rondslingeren omdat je één keer op de * knop drukte. Meteen ná het delen wissen kan niet: de app die het bestand aanneemt leest het dan * nog. * * In de browser (`npm run dev`) blijft het een gewone blob-download. */ const PREFIX = "liefdesbrieven-"; const naamVan = (exportedAt) => `${PREFIX}${new Date(exportedAt).toISOString().slice(0, 10)}.json`; /* Eerdere exports uit de cachemap. Mislukt dit, dan is dat geen reden om het opslaan af te breken */ async function ruimOp() { try { const { files } = await Filesystem.readdir({ path: "", directory: Directory.Cache }); await Promise.all( files .filter((f) => (f.name ?? f).startsWith(PREFIX)) .map((f) => Filesystem.deleteFile({ path: f.name ?? f, directory: Directory.Cache }).catch((e) => log("✗ export opruimen", f.name ?? f, e?.message ?? String(e)) ) ) ); } catch (e) { /* geen cachemap, of niet te lezen: dan valt er ook niets op te ruimen */ log("✗ export opruimen", e?.message ?? String(e)); } } /* * Bewaar de export als bestand. Geeft { name, bytes, saved } terug, of { canceled: true } als de * gebruiker het deelblad wegtikt. Gooit als het schrijven zelf mislukt (volle schijf, geen ruimte). * * `bytes` is de lengte van de JSON in tekens. Dat is niet exact het aantal bytes, maar de export * bestaat vrijwel volledig uit ASCII (de foto's zitten er als base64 in), dus als maat voor "hoe * groot is dit bestand" klopt het tot op een haar — en het scheelt een tweede kopie in het geheugen. */ export async function saveExport(data) { const json = JSON.stringify(data, null, 2); const name = naamVan(data.exportedAt); const bytes = json.length; if (!Capacitor.isNativePlatform()) { const url = URL.createObjectURL(new Blob([json], { type: "application/json" })); const a = Object.assign(document.createElement("a"), { href: url, download: name }); document.body.appendChild(a); a.click(); a.remove(); setTimeout(() => URL.revokeObjectURL(url), 1000); return { name, bytes, saved: true }; } await ruimOp(); const { uri } = await Filesystem.writeFile({ path: name, directory: Directory.Cache, data: json, encoding: Encoding.UTF8 }); try { await Share.share({ title: "Mijn gegevens (Liefdesbrieven)", files: [uri] }); } catch (e) { /* Wegtikken is geen fout — de plugin meldt het wel als een afwijzing */ if (/cancel/i.test(e?.message ?? "")) return { name, bytes, canceled: true }; throw e; } return { name, bytes, saved: true }; } /* 'MB' zodra het er een is; daaronder kB. Nederlandse komma, want dit staat in de app */ export const bestandsgrootte = (bytes) => bytes >= 1024 * 1024 ? `${(bytes / 1024 / 1024).toFixed(1).replace(".", ",")} MB` : `${Math.max(1, Math.round(bytes / 1024))} kB`;