cautpartener / client / src / app / views / about.ts
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
/**
 * #/despre — the About + honest-limits page (M6, PROTOCOL.md §7). Plain-words
 * RO/EN statements of every trade-off the serverless design makes: public
 * window TTL (live value from RuntimeLimits, never hardcoded), best-effort
 * feeds, non-retraction, client-side-only spam defense, what the node
 * operator can see, the Sybil/trust model, missing forward secrecy, and the
 * public metadata of restricted posts. Also surfaces the running app version,
 * a manual update check and the public source repository.
 */

import { RuntimeLimits } from "../../config";
import { t } from "../i18n";
import { h, toast } from "../ui-kit";

const SOURCE_URL = "https://cod-sursa.cautpartener.ro";

function limitItem(text: string): HTMLElement {
  return h("li", { class: "about-limit" }, text);
}

export function renderAbout(root: HTMLElement): void {
  const hours = RuntimeLimits.ttlHours;

  const checkBtn = h(
    "button",
    {
      type: "button",
      class: "btn ghost small",
      "data-testid": "about-check-updates",
      onclick: () => {
        void navigator.serviceWorker
          ?.getRegistration()
          .then((reg) => reg?.update())
          .catch(() => {});
        toast(t("aboutCheckStarted"));
      },
    },
    t("aboutCheckUpdates"),
  );

  root.replaceChildren(
    h(
      "div",
      { class: "onboarding about-view", "data-testid": "about-view" },
      h(
        "div",
        { class: "onb-inner about-inner" },
        h("h2", {}, t("aboutTitle")),

        h(
          "section",
          { class: "card about-card" },
          h("h3", {}, t("aboutWhatTitle")),
          h("p", { class: "muted" }, t("aboutIntro")),
        ),

        h(
          "section",
          { class: "card about-card", "data-testid": "about-limits" },
          h("h3", {}, t("aboutLimitsTitle")),
          h("p", { class: "muted" }, t("aboutLimitsIntro")),
          h(
            "ul",
            { class: "about-limits" },
            limitItem(t("aboutLimWindow", { hours })),
            limitItem(t("aboutLimFeeds")),
            limitItem(t("aboutLimRetract")),
            limitItem(t("aboutLimSpam")),
            limitItem(t("aboutLimOperator")),
            limitItem(t("aboutLimSybil")),
            limitItem(t("aboutLimForward")),
            limitItem(t("aboutLimRestricted")),
            limitItem(t("aboutLimActivities")),
            limitItem(t("aboutLimMatching")),
            limitItem(t("aboutLimCalls")),
          ),
        ),

        h(
          "section",
          { class: "card about-card" },
          h("h3", {}, t("aboutSourceTitle")),
          h("p", { class: "muted" }, t("aboutSourceBody")),
          h("p", {}, h("a", { class: "mono", href: SOURCE_URL, target: "_blank", rel: "noopener", "data-testid": "about-source-link" }, SOURCE_URL)),
        ),

        h(
          "section",
          { class: "card about-card" },
          h("h3", {}, t("aboutUpdatesTitle")),
          h("p", { class: "muted mono", "data-testid": "about-version" }, t("versionLabel", { version: __APP_VERSION__ })),
          h("p", { class: "muted" }, t("aboutUpdateHint")),
          h("div", { class: "btn-row" }, checkBtn),
        ),

        h("div", { class: "btn-col spaced-top" }, h("a", { class: "btn ghost", href: "#/" }, t("backHome"))),
      ),
    ),
  );
}

static mirror of HEAD · about · clone: git clone https://cod-sursa.cautpartener.ro/cautpartener.git