package login import ( "errors" "log/slog" "github.com/ayn2op/tview/layers" "github.com/gdamore/tcell/v3" "github.com/ayn2op/discordo/internal/clipboard" "github.com/ayn2op/discordo/internal/config" "github.com/ayn2op/discordo/internal/keyring" "github.com/ayn2op/discordo/internal/ui" "github.com/ayn2op/tview" ) const ( formLayerName = "form" errorLayerName = "error" qrLayerName = "qr" ) type Form struct { *layers.Layers app *tview.Application cfg *config.Config form *tview.Form } func NewForm(app *tview.Application, cfg *config.Config) *Form { f := &Form{ Layers: layers.New(), app: app, cfg: cfg, form: tview.NewForm(), } f.form. AddPasswordField("Token", "", 0, 0, nil). AddButton("Login", f.login). AddButton("Login with QR", f.loginWithQR) f.SetBackgroundLayerStyle(f.cfg.Theme.Dialog.BackgroundStyle.Style) f.AddLayer(f.form, layers.WithName(formLayerName), layers.WithResize(true), layers.WithVisible(true)) return f } func (f *Form) login() { token := f.form.GetFormItem(0).(*tview.InputField).GetText() if token == "" { f.onError(errors.New("token required")) return } go keyring.SetToken(token) f.app.QueueEvent(NewTokenEvent(token)) } func (f *Form) onError(err error) { slog.Error("failed to login", "err", err) message := err.Error() modal := tview.NewModal(). SetText(message). AddButtons([]string{"Copy", "Close"}). SetDoneFunc(func(buttonIndex int, _ string) { if buttonIndex == 0 { go func() { if err := clipboard.Write(clipboard.FmtText, []byte(message)); err != nil { slog.Error("failed to copy error message", "err", err) } }() } else { f.RemoveLayer(errorLayerName) } }) { bg := f.cfg.Theme.Dialog.Style.GetBackground() buttonStyle := f.cfg.Theme.Dialog.Style.Style if bg != tcell.ColorDefault { modal.SetBackgroundColor(bg) buttonStyle = buttonStyle.Background(bg) } fg := f.cfg.Theme.Dialog.Style.GetForeground() if fg != tcell.ColorDefault { modal.SetTextColor(fg) buttonStyle = buttonStyle.Foreground(fg) } // Keep button styles aligned with dialog content without hiding text. modal.SetButtonStyle(buttonStyle) modal.SetButtonActivatedStyle(buttonStyle) } f. AddLayer( ui.Centered(modal, 0, 0), layers.WithName(errorLayerName), layers.WithResize(true), layers.WithVisible(true), layers.WithOverlay(), ). SendToFront(errorLayerName) } func (f *Form) loginWithQR() { qr := newQRLogin(f.app, f.cfg, func(token string, err error) { if err != nil { f.onError(err) return } if token == "" { f.RemoveLayer(qrLayerName) return } go keyring.SetToken(token) f.RemoveLayer(qrLayerName) f.app.QueueEvent(NewTokenEvent(token)) }) f.AddLayer(qr, layers.WithName(qrLayerName), layers.WithResize(true), layers.WithVisible(true)) qr.start() }