Antonio Mika
·
2026-10-10
1package tui
2
3import (
4 "time"
5
6 "github.com/picosh/pico/pkg/db"
7 "go.rockorager.dev/vaxis"
8 "go.rockorager.dev/vaxis/vxfw"
9 "go.rockorager.dev/vaxis/vxfw/button"
10 "go.rockorager.dev/vaxis/vxfw/list"
11 "go.rockorager.dev/vaxis/vxfw/richtext"
12 "go.rockorager.dev/vaxis/vxfw/text"
13)
14
15type AddInvitePage struct {
16 shared *SharedModel
17 list list.Dynamic
18
19 err error
20 focus string
21 input *TextInput
22 btn *button.Button
23 invites []*db.Invite
24}
25
26func NewAddInvitePage(shrd *SharedModel) *AddInvitePage {
27 btn := button.New("SEND", func() (vxfw.Command, error) { return nil, nil })
28 btn.Style = button.StyleSet{
29 Default: vaxis.Style{Background: grey},
30 Focus: vaxis.Style{Background: oj, Foreground: black},
31 }
32 m := &AddInvitePage{
33 shared: shrd,
34
35 input: NewTextInput("pico user"),
36 btn: btn,
37 }
38 m.list = list.Dynamic{DrawCursor: true, Builder: m.getWidget, Gap: 1}
39 return m
40}
41
42func (m *AddInvitePage) Footer() []Shortcut {
43 return []Shortcut{
44 {Shortcut: "tab", Text: "focus"},
45 {Shortcut: "enter", Text: "invite user"},
46 }
47}
48
49func (m *AddInvitePage) getWidget(i uint, cursor uint) vxfw.Widget {
50 if int(i) >= len(m.invites) {
51 return nil
52 }
53
54 style := vaxis.Style{Foreground: grey}
55 isSelected := i == cursor
56 if isSelected {
57 style = vaxis.Style{Foreground: fuschia}
58 }
59
60 invite := m.invites[i]
61
62 txt := richtext.New([]vaxis.Segment{
63 {Text: "User: ", Style: style},
64 {Text: invite.ToUserName + "\n"},
65
66 {Text: "Invited: ", Style: style},
67 {Text: invite.CreatedAt.Format(time.DateOnly)},
68 })
69
70 return txt
71}
72
73func (m *AddInvitePage) CaptureEvent(ev vaxis.Event) (vxfw.Command, error) {
74 switch msg := ev.(type) {
75 case vaxis.Key:
76 if msg.Matches(vaxis.KeyEnter) {
77 err := m.addInvite(m.input.GetValue())
78 m.err = err
79 if err == nil {
80 m.input.Reset()
81 m.shared.App.PostEvent(Navigate{To: HOME})
82 return nil, nil
83 }
84 return vxfw.RedrawCmd{}, nil
85 }
86 }
87 return nil, nil
88}
89
90func (m *AddInvitePage) HandleEvent(ev vaxis.Event, phase vxfw.EventPhase) (vxfw.Command, error) {
91 switch msg := ev.(type) {
92 case PageIn:
93 m.err = m.fetchInvites()
94 m.focus = "input"
95 m.input.Reset()
96 return m.input.FocusIn()
97 case vaxis.Key:
98 if msg.Matches(vaxis.KeyTab) {
99 if m.focus == "input" {
100 m.focus = "button"
101 cmd, _ := m.input.FocusOut()
102 return vxfw.BatchCmd([]vxfw.Command{
103 vxfw.FocusWidgetCmd(m.btn),
104 cmd,
105 }), nil
106 }
107 m.focus = "input"
108 return m.input.FocusIn()
109 }
110 }
111
112 return nil, nil
113}
114
115func (m *AddInvitePage) fetchInvites() error {
116 invites, err := m.shared.Dbpool.FindInvitesByUser(m.shared.User.ID)
117 if err != nil {
118 return err
119
120 }
121 m.invites = invites
122 return nil
123}
124
125func (m *AddInvitePage) addInvite(username string) error {
126 db := m.shared.Dbpool
127 user, err := db.FindUserByName(username)
128 if err != nil {
129 return err
130 }
131
132 err = db.InviteUser(m.shared.User.ID, user.ID)
133 if err != nil {
134 return err
135 }
136
137 return m.fetchInvites()
138}
139
140func (m *AddInvitePage) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) {
141 w := ctx.Max.Width
142 h := ctx.Max.Height
143 root := vxfw.NewSurface(w, h, m)
144 ah := 0
145
146 header := text.New("Invite a user to pico! Invitee must already have a pico account. This grants them `pgs`, `prose` and `pastes` access on their respective free tiers. Only pico+ members or users with an active invite can invite other users.")
147 headerSurf, _ := header.Draw(ctx)
148 root.AddChild(0, ah, headerSurf)
149 ah += int(headerSurf.Size.Height) + 1
150
151 inputSurf, _ := m.input.Draw(ctx)
152 root.AddChild(0, ah, inputSurf)
153 ah += int(headerSurf.Size.Height) + 1
154
155 btnSurf, _ := m.btn.Draw(vxfw.DrawContext{
156 Characters: ctx.Characters,
157 Max: vxfw.Size{Width: 6, Height: 1},
158 })
159 root.AddChild(0, ah, btnSurf)
160 ah += int(btnSurf.Size.Height) + 1
161
162 if m.err != nil {
163 e := richtext.New([]vaxis.Segment{
164 {
165 Text: m.err.Error(),
166 Style: vaxis.Style{Foreground: red},
167 },
168 })
169 errSurf, _ := e.Draw(createDrawCtx(ctx, 1))
170 root.AddChild(0, ah, errSurf)
171 ah += int(errSurf.Size.Height) + 1
172 }
173
174 listSurf, _ := m.list.Draw(createDrawCtx(ctx, ctx.Max.Height-uint16(ah)))
175 root.AddChild(0, ah, listSurf)
176
177 return root, nil
178}