Login
NAME
App::Moneymoor::Screen::Login - the centred unlock / create-a-budget dialog, and the only screen that runs before a passphrase exists.
SYNOPSIS
use App::Moneymoor::Screen::Login;
use App::Moneymoor::Themes;
my $login = App::Moneymoor::Screen::Login.new(
available-budgets => ('household', 'business'),
theme => App::Moneymoor::Themes::load('nord'),
);
my $root = $login.build; # a plain Selkie tree, no terminal needed
$login.on-login.tap: -> %data {
given %data<action> {
when 'login' { unlock(%data<budget-name>, %data<passphrase>) }
when 'create' { create(%data<budget-name>, %data<passphrase>) }
when 'focus-next' { $app.focus(...) }
}
};
$login.set-status('Invalid passphrase (or corrupted budget file)', :error);
DESCRIPTION
A fixed 24 x 64 dialog, centred by flex struts, with a rounded frame and one cell of padding. It has two modes, swapped in place inside a single form container:
open a budget β the budget
Selectplus one masked passphrase field.create a budget β the same
Select(parked on+ Create new budgetβ¦), a three-row warning, a name field, a passphrase field with a livePasswordStrengthmeter, and a confirmation field.
Either the picker or Ctrl+N gets you to the second mode; both go
through the picker's on-change tap, which is the one place that
knows how to swap the form.
Nothing here touches the database β it cannot, since the database is exactly what the passphrase typed into this screen unlocks. The only state it reads is the budget-name list the caller hands it, so the screen is safe to build and render before any credential exists.
The row budget
The dialog is centred but never scrolled, so every row it spends is
spent at build time and the create-a-budget branch spends all of them.
See the comment on DIALOG-ROWS in the source for the full sum;
t/63-login-layout.rakutest pins it. Adding a field, or a line of
warning copy, without growing DIALOG-ROWS pushes the hint line off
the bottom edge.
Error reporting
App::Moneymoor::DB.connect answers with a Failure β never an
exception β carrying one of two distinct messages:
"Not an encrypted budget file (plain SQLite file)"β the file exists but is plaintext SQLite, i.e. the user picked the wrong file."Invalid passphrase (or corrupted budget file)"β the file is encrypted and the key did not open it.
The distinction is worth surfacing verbatim: one is "you typed the
wrong thing", the other is "you opened the wrong thing", and a single
"login failed" would leave the user retyping a passphrase that was
always correct. App::Moneymoor::UI passes
$result.exception.message straight into set-status(:error) and
then calls .so on the Failure β a handled Failure that is never
defused re-throws at sink time.
SUPPLIES
on-login emits a Hash with an action key:
focus-nextβ{ target ='name' | 'password' | 'confirm' }>. The screen has noSelkie::Appreference, so moving focus between its own fields is a request to whoever owns the app.nameis theCtrl+Ncase: the field that had focus was destroyed with the form the mode switch replaced.loginβ{ budget-name, passphrase }.createβ{ budget-name, passphrase }.
ACCESSORS
password-input,confirm-input,name-input,budget-selectβ focus targets.in-new-db-modeβ which branch the form is currently showing.selected-budgetβ the picked budget name, or theStrtype object when the create row is selected.
SEE ALSO
App::Moneymoor::UI β owns the DB handshake this screen feeds.
App::Moneymoor::Config β supplies
available-budget-names.App::Moneymoor::View::HintBar β the footer's
logincontext.