MishkaCmsCore.Runtime.SiteIcon (Mishka CMS Core v0.0.1)

Copy Markdown View Source

A site's icons and its web manifest, each its own, answered on the site's own address (MishkaCmsCoreWeb.SeoController):

  • /favicon.ico — the file the site.favicon setting names (favicon/1);
  • /apple-touch-icon.png and /apple-touch-icon-precomposed.png — 180×180, the icon a phone puts on its home screen;
  • /site-icon-192.png and /site-icon-512.png — the icons of the manifest;
  • /site.webmanifest — what a browser installs the site with (manifest/1).

What the icons are drawn from

The site's site.app_icon setting, else its site.favicon — the first that names a picture of the site's own (MishkaCmsCore.Runtime.ShareCard.site_file/2): a file of its media or under the host's static folder that is a PNG, JPEG, WebP, GIF or SVG (source/1). An http(s):// address is never fetched, so a site whose pictures are elsewhere has no icons but its favicon link. Each icon is the picture fitted whole in its square and centered on transparency, never stretched (MishkaCmsCore.Runtime.ShareCard.squares/2).

When they are drawn

On the first request for one: every size at once, by libvips in a separate process, as a share card is drawn. A site draws one at a time; the requests that come meanwhile wait for it and send what it drew. The files are stored at /uploads/<site_id>/icons/<hash>-<size>.png, hash named by the picture's bytes, and every later request sends the file. A picture libvips cannot draw is not tried again until the setting or the file changes. Drawing them fires "runtime:site:after_icons" with %{site_id:, hash:, files: %{size => path}}; a site's older icons are deleted then.

What a page reads

Which files the favicon and the picture are, and the picture's hash, are kept in ETS (MishkaCmsCore.Runtime.States.AssetsState, under {site_id, "site_icon"}) beside the values of the two settings they were read for. The head of every page reads them from there (url/2, manifest_url/1); a setting that holds another value — on any node — has the files read again. A media file replaced in place is caught by MishkaCmsCore.Runtime.MediaSiteIcons, and saving either setting by MishkaCmsCore.Runtime.SettingSiteIcons: both forget/1 it.

The manifest

name is the site's name in its primary language (MishkaCmsCore.Runtime.Seo.site_name/2), start_url is /, display is browser and icons are the 192×192 and 512×512 PNGs. Three more keys are there only for a site that set them, on its "App & home screen" settings, and are left out of the manifest of one that did not: short_name (short_name/1, which a browser replaces with name when it is missing), theme_color (theme_color/1) and background_color (background_color/1). A plugin on "runtime:site:manifest" is handed %{site: site, manifest: manifest} and returns the map with its manifest: another display, a lang.

The three settings are read from the settings cache, as every setting is, so a change shows on the next request. A page's head names the theme color too, as a theme-color meta tag (MishkaCmsCore.Runtime.Seo), for a site with no icons as well.

SiteIcon.url(site.id, 180) #=> "/apple-touch-icon.png?v=4be1f0a3c2d9e8b7"

Summary

Types

A file of the site's own: where it is on disk and its type, read off its first bytes.

The picture the icons are drawn from, and the hash their names carry.

Functions

The color shown behind the icon while the installed app opens: the site's site.background_color setting, nil while it is empty or holds anything but a hex color (color?/1).

Whether value is a hex color a manifest and a theme-color meta tag take: #rgb or #rrggbb, in either case, and nothing around it.

color?/1, or empty: how site.theme_color and site.background_color are validated.

The folder the icons of the site site_id are written to.

The event fired after a site's icons are drawn: "runtime:site:after_icons".

The site's favicon as a file of its own: %{path:, type:}, type one of MishkaCmsCore.Runtime.ShareCard.picture_type/1's. nil for no favicon, and for one that is an address elsewhere or a file the site does not own.

The PNG of the site's icon of size on disk, drawn now when it is not there yet: {:ok, %{path:, hash:}}, hash the one its address carries (url/2). :error for a site with no picture to draw it from, and for a picture libvips could not draw.

Drops what is kept of the icons of the site site_id — of every site for nil — so the next request reads its settings and files again, and draws its icons again when their picture changed.

forget/1 for the site site_id when its favicon or the picture of its icons is the media file relative_path, /uploads/<site_id>/media/…; anything else is left as it is.

The site's web manifest: %{"name", "start_url", "display", "icons"}, and "short_name", "theme_color" and "background_color" when the site set them, as a plugin on manifest_event/0 leaves it, with string keys, ready to be encoded. nil for a site with no icons.

The event a plugin changes a site's manifest on: "runtime:site:manifest".

The site's /site.webmanifest, for a site with icons; nil for one with none.

The path the icon of size is served at, with no hash.

The settings the icons are read from: ["site.app_icon", "site.favicon"].

The name the site is given under its home-screen icon: its site.app_short_name setting, nil while it is empty.

Whether value fits site.app_short_name: at most short_name_max/0 characters.

The longest site.app_short_name a site may save, in characters: 30.

Every size an icon is drawn at, in pixels: [180, 192, 512].

The picture the site's icons are drawn from: its app icon, else its favicon, when it is a drawable file of its own. nil when neither is.

The color of the browser's toolbar on phones and of the installed app's title bar: the site's site.theme_color setting, nil while it is empty or holds anything but a hex color (color?/1).

The address the site's icon of size is served at, with the hash of the picture it is drawn from: "/apple-touch-icon.png?v=…" for 180, "/site-icon-192.png?v=…". nil for a site with no picture to draw it from.

Types

file()

@type file() :: %{path: Path.t(), type: String.t()}

A file of the site's own: where it is on disk and its type, read off its first bytes.

source()

@type source() :: %{
  path: Path.t(),
  type: String.t(),
  hash: String.t(),
  failed?: boolean()
}

The picture the icons are drawn from, and the hash their names carry.

Functions

background_color(site_id)

@spec background_color(String.t() | nil) :: String.t() | nil

The color shown behind the icon while the installed app opens: the site's site.background_color setting, nil while it is empty or holds anything but a hex color (color?/1).

color?(arg1)

@spec color?(term()) :: boolean()

Whether value is a hex color a manifest and a theme-color meta tag take: #rgb or #rrggbb, in either case, and nothing around it.

color?("#0b0a14") #=> true
color?("red")     #=> false

color_or_empty?(value)

@spec color_or_empty?(term()) :: boolean()

color?/1, or empty: how site.theme_color and site.background_color are validated.

dir(site_id)

@spec dir(String.t()) :: String.t()

The folder the icons of the site site_id are written to.

drawn_event()

@spec drawn_event() :: String.t()

The event fired after a site's icons are drawn: "runtime:site:after_icons".

favicon(site_id)

@spec favicon(String.t() | nil) :: file() | nil

The site's favicon as a file of its own: %{path:, type:}, type one of MishkaCmsCore.Runtime.ShareCard.picture_type/1's. nil for no favicon, and for one that is an address elsewhere or a file the site does not own.

file(site_id, size)

@spec file(String.t() | nil, pos_integer()) ::
  {:ok, %{path: Path.t(), hash: String.t()}} | :error

The PNG of the site's icon of size on disk, drawn now when it is not there yet: {:ok, %{path:, hash:}}, hash the one its address carries (url/2). :error for a site with no picture to draw it from, and for a picture libvips could not draw.

forget(site_id)

@spec forget(String.t() | nil) :: :ok

Drops what is kept of the icons of the site site_id — of every site for nil — so the next request reads its settings and files again, and draws its icons again when their picture changed.

forget_file(site_id, relative_path)

@spec forget_file(String.t(), String.t()) :: :ok

forget/1 for the site site_id when its favicon or the picture of its icons is the media file relative_path, /uploads/<site_id>/media/…; anything else is left as it is.

manifest(site)

@spec manifest(%{id: String.t()}) :: map() | nil

The site's web manifest: %{"name", "start_url", "display", "icons"}, and "short_name", "theme_color" and "background_color" when the site set them, as a plugin on manifest_event/0 leaves it, with string keys, ready to be encoded. nil for a site with no icons.

manifest_event()

@spec manifest_event() :: String.t()

The event a plugin changes a site's manifest on: "runtime:site:manifest".

manifest_url(site_id)

@spec manifest_url(String.t() | nil) :: String.t() | nil

The site's /site.webmanifest, for a site with icons; nil for one with none.

path(size)

@spec path(pos_integer()) :: String.t()

The path the icon of size is served at, with no hash.

settings()

@spec settings() :: [String.t()]

The settings the icons are read from: ["site.app_icon", "site.favicon"].

short_name(site_id)

@spec short_name(String.t() | nil) :: String.t() | nil

The name the site is given under its home-screen icon: its site.app_short_name setting, nil while it is empty.

short_name?(value)

@spec short_name?(term()) :: boolean()

Whether value fits site.app_short_name: at most short_name_max/0 characters.

short_name_max()

@spec short_name_max() :: pos_integer()

The longest site.app_short_name a site may save, in characters: 30.

sizes()

@spec sizes() :: [pos_integer()]

Every size an icon is drawn at, in pixels: [180, 192, 512].

source(site_id)

@spec source(String.t() | nil) :: source() | nil

The picture the site's icons are drawn from: its app icon, else its favicon, when it is a drawable file of its own. nil when neither is.

theme_color(site_id)

@spec theme_color(String.t() | nil) :: String.t() | nil

The color of the browser's toolbar on phones and of the installed app's title bar: the site's site.theme_color setting, nil while it is empty or holds anything but a hex color (color?/1).

url(site_id, size)

@spec url(String.t() | nil, pos_integer()) :: String.t() | nil

The address the site's icon of size is served at, with the hash of the picture it is drawn from: "/apple-touch-icon.png?v=…" for 180, "/site-icon-192.png?v=…". nil for a site with no picture to draw it from.