GamendWeb.PresentationPage (gamend_web v1.0.1216)

Copy Markdown View Source

Shared hero-and-sections page renderer for host presentation pages.

Summary

Functions

page/1 rendered to HTML, memoised per {locale, path}.

A section's "links": a wrapping row of small link chips.

A section's illustration: video, image, light/dark image pair, or icon.

Functions

background_icons(assigns)

Attributes

  • icons (:list) - Defaults to [].
  • bands (:integer) - Defaults to 1.

buttons(assigns)

Attributes

  • buttons (:list) - Defaults to [].

cached_body(page_map, background_icons, locale, path)

@spec cached_body(map(), list(), String.t() | nil, String.t()) :: iodata()

page/1 rendered to HTML, memoised per {locale, path}.

The body of a presentation page is the largest single cost in the response and none of it depends on the reader: page/1 reads only hero, sections and background_icons, all derived from the theme config and the locale, and the module references no conn, scope, user, gettext call, clock or random source. Two anonymous visitors, or one visitor and the same visitor an hour later, were being served bytes that had been built from scratch each time -- 0.64 ms of a 1.9 ms home page, 57 KB of a 102 KB document.

The value carries a fingerprint of the inputs, so an edited theme cannot serve a stale body: a changed page map hashes differently and re-renders. The fingerprint lives in the value rather than the key so the key set stays bounded -- a theme edit overwrites its entry instead of stranding the old one. It is a fingerprint rather than the inputs themselves because a cache read that hands back the page map costs more than the render it replaces: measured at 0.66 ms against 0.47 ms to just render the thing.

A fingerprint is also why this does not key on a theme version. resolve_theme/2 merges conn.assigns[:theme], so a host that varies the theme per request would be served another request's page by a version-keyed cache. Hashing the actual inputs cannot make that mistake.

:persistent_term, like the icon and theme caches beside it, and not the application cache: Gamend.Cache is multilevel and would put a Redis round trip in the render path, and reaching past it into Gamend.Cache.L1 makes every page on the site depend on another app's cache process being started -- which in the web app's own test env it is not, so the site did not render at all. :persistent_term has no owner and cannot be missing.

Its one cost is that every write scans every process on the node, so this is only safe because the key set is bounded by configuration rather than by traffic: cached_body/4 is reached only for a path the theme actually configures (an unknown path is a 404 before this point), so the entries are configured pages times locales, each written once per theme version.

media(assigns)

A section's illustration: video, image, light/dark image pair, or icon.

Images carry data-lightbox, which a host may pick up to open them full-size. Inert on its own, so a host that ships no such script renders exactly what it did before. The cover layout's images are backgrounds rather than illustrations and are deliberately not marked.

Attributes

  • item (:map) (required)
  • variant (:string) - Defaults to "section".

media_visual(assigns)

Attributes

  • image (:map) - Defaults to %{}.
  • video (:map) - Defaults to %{}.
  • icon (:string) - Defaults to nil.
  • variant (:string) - Defaults to "section".
  • size (:string) - Defaults to "section".

page(assigns)

Attributes

  • page (:map) (required)
  • background_icons (:list) - Defaults to [].
  • full_bleed_hero (:boolean) - Defaults to true.

page_for_path(theme, path)

page_title(page, fallback \\ "Page")

rich_text(text)

section(assigns)

Attributes

  • section (:map) (required)