login.go

  1package tui
  2
  3import (
  4	"github.com/charmbracelet/bubbles/textinput"
  5	tea "github.com/charmbracelet/bubbletea"
  6	"github.com/charmbracelet/lipgloss"
  7)
  8
  9// Login holds the state for the login form.
 10type Login struct {
 11	focusIndex int
 12	inputs     []textinput.Model
 13}
 14
 15// NewLogin creates a new login model.
 16func NewLogin() *Login {
 17	m := &Login{
 18		inputs: make([]textinput.Model, 4), // Increased to 4 for provider, name, email, and password
 19	}
 20
 21	var t textinput.Model
 22	for i := range m.inputs {
 23		t = textinput.New()
 24		t.Cursor.Style = focusedStyle
 25		t.CharLimit = 64
 26
 27		switch i {
 28		case 0:
 29			t.Placeholder = "Provider (gmail or icloud)"
 30			t.Focus()
 31			t.Prompt = "☁️ > "
 32		case 1:
 33			t.Placeholder = "Name"
 34			t.Prompt = "👤 > "
 35		case 2:
 36			t.Placeholder = "Email"
 37			t.Prompt = "✉️ > "
 38		case 3:
 39			t.Placeholder = "Password"
 40			t.EchoMode = textinput.EchoPassword
 41			t.Prompt = "🔑 > "
 42		}
 43		m.inputs[i] = t
 44	}
 45
 46	return m
 47}
 48
 49// Init initializes the login model.
 50func (m *Login) Init() tea.Cmd {
 51	return textinput.Blink
 52}
 53
 54// Update handles messages for the login model.
 55func (m *Login) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 56	switch msg := msg.(type) {
 57	case tea.WindowSizeMsg:
 58		// When the window is resized, update the width of the inputs.
 59		for i := range m.inputs {
 60			m.inputs[i].Width = msg.Width - 6 // Subtract for padding and prompt
 61		}
 62
 63	case tea.KeyMsg:
 64		switch msg.Type {
 65		// On Enter, if we are on the last field, submit the credentials.
 66		case tea.KeyEnter:
 67			if m.focusIndex == len(m.inputs)-1 {
 68				return m, func() tea.Msg {
 69					return Credentials{
 70						Provider: m.inputs[0].Value(),
 71						Name:     m.inputs[1].Value(),
 72						Email:    m.inputs[2].Value(),
 73						Password: m.inputs[3].Value(),
 74					}
 75				}
 76			}
 77			fallthrough
 78		// Cycle focus between inputs.
 79		case tea.KeyTab, tea.KeyShiftTab, tea.KeyUp, tea.KeyDown:
 80			s := msg.String()
 81			if s == "up" || s == "shift+tab" {
 82				m.focusIndex--
 83			} else {
 84				m.focusIndex++
 85			}
 86
 87			if m.focusIndex >= len(m.inputs) {
 88				m.focusIndex = 0
 89			} else if m.focusIndex < 0 {
 90				m.focusIndex = len(m.inputs) - 1
 91			}
 92
 93			cmds := make([]tea.Cmd, len(m.inputs))
 94			for i := 0; i < len(m.inputs); i++ {
 95				if i == m.focusIndex {
 96					cmds[i] = m.inputs[i].Focus()
 97				} else {
 98					m.inputs[i].Blur()
 99				}
100			}
101			return m, tea.Batch(cmds...)
102		}
103	}
104
105	// Update the focused input field.
106	var cmds = make([]tea.Cmd, len(m.inputs))
107	for i := range m.inputs {
108		m.inputs[i], cmds[i] = m.inputs[i].Update(msg)
109	}
110	return m, tea.Batch(cmds...)
111}
112
113// View renders the login form.
114func (m *Login) View() string {
115	return lipgloss.JoinVertical(lipgloss.Left,
116		titleStyle.Render("Account Settings"),
117		"Update your credentials.",
118		m.inputs[0].View(),
119		m.inputs[1].View(),
120		m.inputs[2].View(),
121		m.inputs[3].View(),
122		helpStyle.Render("\nenter: save • tab: next field • esc: back to menu"),
123	)
124}