MishkaCmsCoreWeb.Live.Admin.Components.SecurityCards (Mishka CMS Core v0.0.1)

Copy Markdown View Source

The cards of an account's own sign-in security, drawn on the settings page's Preferences tab: the core's — its authenticator app (TwoFactor) and its recovery codes (RecoveryCodes) — and any an extension adds on the "account:security:cards" mishka_installer event, as a widget is added on "dashboard:widgets".

A card is %{id: String.t(), priority: integer(), module: module()}: a LiveComponent drawn with id and current_user, low priority first. A plugin is handed %{cards: cards} and answers {:reply, %{cards: cards}} with its own added; a core card it drops or redefines comes back as the core made it. A card that changes what the others show sends :account_security_changed to its LiveView, which redraws every card with refresh: true (refresh/1).

use MishkaInstaller.Event.Hook, event: "account:security:cards"

def call(%{cards: cards} = data),
  do: {:reply, %{data | cards: cards ++ [%{id: "passkeys", priority: 30, module: MyApp.Passkeys}]}}

A card draws itself in card/1, with input_class/0, dark_button/0 and light_button/0, so every card on the tab looks alike.

Summary

Functions

A security card: its title and what it is for, a status chip beside them, and a message — {:ok, text} in green or {:error, text} in red — above what the card holds.

A card's status chip: green and labelled on when on?, grey and labelled off otherwise.

The cards to draw, the core's first among equals, low priority first.

A card's main button.

The text field of a card's code form.

A card's second button.

Redraws every card in cards with refresh: true, from the LiveView that holds them.

A code typed into a card, as an authenticator app shows it: {:ok, six_digits} with spaces dropped, or :error for anything else — answered in the card, never sent, so it spends none of the account's tries.

Types

card()

@type card() :: %{id: String.t(), priority: integer(), module: module()}

Functions

card(assigns)

A security card: its title and what it is for, a status chip beside them, and a message — {:ok, text} in green or {:error, text} in red — above what the card holds.

Attributes

  • id (:string) (required)
  • title (:string) (required)
  • description (:string) (required)
  • message (:any) - Defaults to nil.

Slots

  • status
  • inner_block (required)

chip(assigns)

A card's status chip: green and labelled on when on?, grey and labelled off otherwise.

Attributes

  • id (:string) (required)
  • on? (:boolean) (required)
  • on (:string) (required)
  • off (:string) (required)

collect()

@spec collect() :: [card()]

The cards to draw, the core's first among equals, low priority first.

dark_button()

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

A card's main button.

input_class()

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

The text field of a card's code form.

light_button()

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

A card's second button.

refresh(cards)

@spec refresh([card()]) :: :ok

Redraws every card in cards with refresh: true, from the LiveView that holds them.

six_digits(code)

@spec six_digits(String.t() | nil) :: {:ok, String.t()} | :error

A code typed into a card, as an authenticator app shows it: {:ok, six_digits} with spaces dropped, or :error for anything else — answered in the card, never sent, so it spends none of the account's tries.