repos / pico

pico services mono repo
git clone https://github.com/picosh/pico.git

pico / pkg / tui
Eric Bower  ·  2025-03-27

signup.go

  1package tui
  2
  3import (
  4	"fmt"
  5
  6	"git.sr.ht/~rockorager/vaxis"
  7	"git.sr.ht/~rockorager/vaxis/vxfw"
  8	"git.sr.ht/~rockorager/vaxis/vxfw/button"
  9	"git.sr.ht/~rockorager/vaxis/vxfw/richtext"
 10	"git.sr.ht/~rockorager/vaxis/vxfw/text"
 11	"github.com/picosh/pico/pkg/db"
 12	"github.com/picosh/utils"
 13	"golang.org/x/crypto/ssh"
 14)
 15
 16type SignupPage struct {
 17	shared *SharedModel
 18	focus  string
 19	err    error
 20
 21	input *TextInput
 22	btn   *button.Button
 23}
 24
 25func NewSignupPage(shrd *SharedModel) *SignupPage {
 26	btn := button.New("SIGNUP", func() (vxfw.Command, error) { return nil, nil })
 27	btn.Style = button.StyleSet{
 28		Default: vaxis.Style{Background: grey},
 29		Focus:   vaxis.Style{Background: oj, Foreground: black},
 30	}
 31	input := NewTextInput("signup")
 32	return &SignupPage{shared: shrd, btn: btn, input: input}
 33}
 34
 35func (m *SignupPage) Footer() []Shortcut {
 36	return []Shortcut{
 37		{Shortcut: "tab", Text: "focus"},
 38		{Shortcut: "enter", Text: "create user"},
 39	}
 40}
 41
 42func (m *SignupPage) createAccount(name string) (*db.User, error) {
 43	if name == "" {
 44		return nil, fmt.Errorf("name cannot be empty")
 45	}
 46	key := utils.KeyForKeyText(m.shared.Session.PublicKey())
 47	return m.shared.Dbpool.RegisterUser(name, key, "")
 48}
 49
 50func (m *SignupPage) CaptureEvent(ev vaxis.Event) (vxfw.Command, error) {
 51	switch msg := ev.(type) {
 52	case vaxis.Key:
 53		if msg.Matches(vaxis.KeyEnter) {
 54			user, err := m.createAccount(m.input.GetValue())
 55			if err != nil {
 56				m.err = err
 57				return vxfw.RedrawCmd{}, nil
 58			}
 59			m.shared.User = user
 60			m.shared.App.PostEvent(Navigate{To: HOME})
 61		}
 62	}
 63	return nil, nil
 64}
 65
 66func (m *SignupPage) HandleEvent(ev vaxis.Event, phase vxfw.EventPhase) (vxfw.Command, error) {
 67	switch msg := ev.(type) {
 68	case PageIn:
 69		return m.input.FocusIn()
 70	case vaxis.Key:
 71		if msg.Matches(vaxis.KeyTab) {
 72			if m.focus == "button" {
 73				m.focus = "input"
 74				return m.input.FocusIn()
 75			}
 76			m.focus = "button"
 77			cmd, _ := m.input.FocusOut()
 78			return vxfw.BatchCmd([]vxfw.Command{
 79				cmd,
 80				vxfw.FocusWidgetCmd(m.btn),
 81			}), nil
 82		}
 83	}
 84
 85	return nil, nil
 86}
 87
 88func (m *SignupPage) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) {
 89	w := ctx.Max.Width
 90	h := ctx.Max.Height
 91
 92	root := vxfw.NewSurface(w, h, m)
 93
 94	fp := ssh.FingerprintSHA256(m.shared.Session.PublicKey())
 95	intro := richtext.New([]vaxis.Segment{
 96		{Text: "Welcome to pico.sh's management TUI!\n\n"},
 97		{Text: "By creating an account you get access to our pico services.  We have free and paid services."},
 98		{Text: "  After you create an account, you can go to our docs site to get started:\n\n  "},
 99		{Text: "https://pico.sh/getting-started\n\n", Style: vaxis.Style{Hyperlink: "https://pico.sh/getting-started"}},
100		{Text: fmt.Sprintf("pubkey: %s\n\n", fp), Style: vaxis.Style{Foreground: purp}},
101	})
102	introSurf, _ := intro.Draw(ctx)
103	ah := 0
104	root.AddChild(0, ah, introSurf)
105	ah += int(introSurf.Size.Height)
106
107	inpSurf, _ := m.input.Draw(vxfw.DrawContext{
108		Characters: ctx.Characters,
109		Max:        vxfw.Size{Width: ctx.Max.Width, Height: 4},
110	})
111	root.AddChild(0, ah, inpSurf)
112	ah += int(inpSurf.Size.Height)
113
114	btnSurf, _ := m.btn.Draw(vxfw.DrawContext{
115		Characters: ctx.Characters,
116		Max:        vxfw.Size{Width: 10, Height: 1},
117	})
118	root.AddChild(0, ah, btnSurf)
119	ah += int(btnSurf.Size.Height)
120
121	if m.err != nil {
122		errTxt := text.New(m.err.Error())
123		errTxt.Style = vaxis.Style{Foreground: red}
124		errSurf, _ := errTxt.Draw(ctx)
125		root.AddChild(0, ah, errSurf)
126	}
127
128	return root, nil
129}