Een dating-app op basis van brieven, niet van gezichten!

main

Deze repository wordt als momentopname gepubliceerd: een clone bevat de volledige inhoud, maar geen commitgeschiedenis.

3.9 kB Ruw bestand
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 <a download> 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`;