Eric Bower
·
2025-12-20
menu.go
1package tui
2
3import (
4 "git.sr.ht/~rockorager/vaxis"
5 "git.sr.ht/~rockorager/vaxis/vxfw"
6 "git.sr.ht/~rockorager/vaxis/vxfw/list"
7 "git.sr.ht/~rockorager/vaxis/vxfw/text"
8 "github.com/picosh/pico/pkg/db"
9)
10
11var menuChoices = []string{
12 "pubkeys",
13 "tokens",
14 "logs",
15 "access_logs",
16 "analytics",
17 "pages",
18 "tuns",
19 "pico+",
20 "chat",
21}
22
23type LoadedUsageStats struct{}
24
25type MenuPage struct {
26 shared *SharedModel
27
28 list list.Dynamic
29 features *FeaturesList
30 stats *db.UserStats
31}
32
33func getMenuWidget(i uint, cursor uint) vxfw.Widget {
34 if int(i) >= len(menuChoices) {
35 return nil
36 }
37 var style vaxis.Style
38 if i == cursor {
39 style.Attribute = vaxis.AttrReverse
40 }
41 content := menuChoices[i]
42 return &text.Text{
43 Content: content,
44 Style: style,
45 }
46}
47
48func NewMenuPage(shrd *SharedModel) *MenuPage {
49 m := &MenuPage{shared: shrd}
50 m.list = list.Dynamic{Builder: getMenuWidget, DrawCursor: true}
51 m.features = NewFeaturesList(shrd)
52 return m
53}
54
55func loadChat(shrd *SharedModel) {
56 sp := &SenpaiCmd{
57 Shared: shrd,
58 }
59 _ = sp.Run()
60}
61
62func (m *MenuPage) fetchUsageStats() error {
63 stats, err := m.shared.Dbpool.FindUserStats(m.shared.User.ID)
64 if err != nil {
65 return err
66 }
67 m.stats = stats
68 return nil
69}
70
71func (m *MenuPage) HandleEvent(ev vaxis.Event, phase vxfw.EventPhase) (vxfw.Command, error) {
72 switch msg := ev.(type) {
73 case PageIn:
74 _ = m.fetchUsageStats()
75 cmd, _ := m.features.HandleEvent(vxfw.Init{}, phase)
76 return vxfw.BatchCmd([]vxfw.Command{
77 cmd,
78 vxfw.FocusWidgetCmd(&m.list),
79 }), nil
80 case vaxis.Key:
81 if msg.Matches(vaxis.KeyEnter) {
82 cursor := int(m.list.Cursor())
83 if cursor >= len(menuChoices) {
84 return nil, nil
85 }
86 choice := menuChoices[m.list.Cursor()]
87 m.shared.App.PostEvent(Navigate{To: choice})
88 }
89 }
90 return nil, nil
91}
92
93func (m *MenuPage) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) {
94 info, _ := NewUserInfo(m.shared).Draw(ctx)
95
96 brd := NewBorder(&m.list)
97 brd.Label = "menu"
98 brd.Style = vaxis.Style{Foreground: oj}
99 menuSurf, _ := brd.Draw(vxfw.DrawContext{
100 Characters: ctx.Characters,
101 Max: vxfw.Size{
102 Width: 30,
103 Height: uint16(len(menuChoices)) + 3,
104 },
105 })
106
107 services, _ := NewServicesList(m.shared.PlusFeatureFlag).Draw(ctx)
108 features, _ := m.features.Draw(ctx)
109
110 leftPane := NewGroupStack([]vxfw.Surface{
111 menuSurf,
112 info,
113 services,
114 })
115 leftSurf, _ := leftPane.Draw(ctx)
116
117 right := []vxfw.Surface{}
118 if len(m.features.features) > 0 {
119 right = append(right, features)
120 }
121
122 if m.stats != nil {
123 pages, _ := NewUsageInfo("pages", &m.stats.Pages).Draw(ctx)
124 prose, _ := NewUsageInfo("prose", &m.stats.Prose).Draw(ctx)
125 pastes, _ := NewUsageInfo("pastes", &m.stats.Pastes).Draw(ctx)
126 feeds, _ := NewUsageInfo("rss-to-email", &m.stats.Feeds).Draw(ctx)
127 right = append(right,
128 pages,
129 prose,
130 pastes,
131 feeds,
132 )
133 }
134 rightPane := NewGroupStack(right)
135 rightSurf, _ := rightPane.Draw(ctx)
136
137 root := vxfw.NewSurface(ctx.Max.Width, ctx.Max.Height, m)
138 root.AddChild(0, 0, leftSurf)
139 root.AddChild(30, 0, rightSurf)
140 return root, nil
141}