repos / pico

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

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

info.go

  1package tui
  2
  3import (
  4	"fmt"
  5	"time"
  6
  7	"git.sr.ht/~rockorager/vaxis"
  8	"git.sr.ht/~rockorager/vaxis/vxfw"
  9	"github.com/picosh/pico/pkg/db"
 10)
 11
 12type UsageInfo struct {
 13	stats *db.UserServiceStats
 14	Label string
 15}
 16
 17func NewUsageInfo(label string, stats *db.UserServiceStats) *UsageInfo {
 18	return &UsageInfo{Label: label, stats: stats}
 19}
 20
 21func (m *UsageInfo) HandleEvent(ev vaxis.Event, phase vxfw.EventPhase) (vxfw.Command, error) {
 22	return nil, nil
 23}
 24
 25func (m *UsageInfo) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) {
 26	info := NewKv(m.getKv())
 27	brd := NewBorder(info)
 28	brd.Label = m.Label
 29	brd.Style = vaxis.Style{Foreground: purp}
 30	return brd.Draw(vxfw.DrawContext{
 31		Characters: ctx.Characters,
 32		Max: vxfw.Size{
 33			Width:  30,
 34			Height: 3 + 3,
 35		},
 36	})
 37}
 38
 39func (m *UsageInfo) getKv() []Kv {
 40	label := "posts"
 41	if m.Label == "pages" {
 42		label = "sites"
 43	}
 44	kv := []Kv{
 45		{Key: label, Value: fmt.Sprintf("%d", m.stats.Num)},
 46		{Key: "oldest", Value: m.stats.FirstCreatedAt.Format(time.DateOnly)},
 47		{Key: "newest", Value: m.stats.LastestCreatedAt.Format(time.DateOnly)},
 48	}
 49	return kv
 50}
 51
 52type UserInfo struct {
 53	shared *SharedModel
 54}
 55
 56func NewUserInfo(shrd *SharedModel) *UserInfo {
 57	return &UserInfo{shrd}
 58}
 59
 60func (m *UserInfo) HandleEvent(ev vaxis.Event, phase vxfw.EventPhase) (vxfw.Command, error) {
 61	return nil, nil
 62}
 63
 64func (m *UserInfo) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) {
 65	features := NewKv(m.getKv())
 66	brd := NewBorder(features)
 67	brd.Label = "info"
 68	brd.Style = vaxis.Style{Foreground: purp}
 69	h := 1
 70	if m.shared.PlusFeatureFlag != nil {
 71		h += 1
 72	}
 73	return brd.Draw(vxfw.DrawContext{
 74		Characters: ctx.Characters,
 75		Max: vxfw.Size{
 76			Width:  30,
 77			Height: uint16(h) + 3,
 78		},
 79	})
 80}
 81
 82func (m *UserInfo) getKv() []Kv {
 83	createdAt := m.shared.User.CreatedAt.Format(time.DateOnly)
 84	kv := []Kv{
 85		{Key: "joined", Value: createdAt},
 86	}
 87
 88	if m.shared.PlusFeatureFlag != nil {
 89		expiresAt := m.shared.PlusFeatureFlag.ExpiresAt.Format(time.DateOnly)
 90		kv = append(kv, Kv{Key: "pico+ expires", Value: expiresAt})
 91	}
 92
 93	return kv
 94}
 95
 96type FeaturesList struct {
 97	shared   *SharedModel
 98	features []*db.FeatureFlag
 99	err      error
100}
101
102func NewFeaturesList(shrd *SharedModel) *FeaturesList {
103	return &FeaturesList{shared: shrd}
104}
105
106func (m *FeaturesList) HandleEvent(ev vaxis.Event, phase vxfw.EventPhase) (vxfw.Command, error) {
107	switch ev.(type) {
108	case vxfw.Init:
109		m.err = m.fetchFeatures()
110		return vxfw.RedrawCmd{}, nil
111	}
112	return nil, nil
113}
114
115func (m *FeaturesList) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) {
116	features := NewKv(m.getFeaturesKv())
117	brd := NewBorder(features)
118	brd.Label = "features"
119	brd.Style = vaxis.Style{Foreground: purp}
120	return brd.Draw(vxfw.DrawContext{
121		Characters: ctx.Characters,
122		Max: vxfw.Size{
123			Width:  30,
124			Height: uint16(len(m.features)) + 4,
125		},
126	})
127}
128
129func (m *FeaturesList) fetchFeatures() error {
130	features, err := m.shared.Dbpool.FindFeaturesForUser(m.shared.User.ID)
131	m.features = features
132	return err
133}
134
135func (m *FeaturesList) getFeaturesKv() []Kv {
136	kv := []Kv{
137		{
138			Key:   "name",
139			Value: "expires at",
140			Style: vaxis.Style{
141				UnderlineColor: purp,
142				UnderlineStyle: vaxis.UnderlineDashed,
143				Foreground:     purp,
144			},
145		},
146	}
147
148	for _, feature := range m.features {
149		kv = append(kv, Kv{Key: feature.Name, Value: feature.ExpiresAt.Format(time.DateOnly)})
150	}
151
152	return kv
153}
154
155type ServicesList struct {
156	ff *db.FeatureFlag
157}
158
159func NewServicesList(ff *db.FeatureFlag) *ServicesList {
160	return &ServicesList{ff}
161}
162
163func (m *ServicesList) HandleEvent(ev vaxis.Event, phase vxfw.EventPhase) (vxfw.Command, error) {
164	return nil, nil
165}
166
167func (m *ServicesList) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) {
168	services := NewKv(m.getServiceKv())
169	brd := NewBorder(services)
170	brd.Label = "services"
171	brd.Style = vaxis.Style{Foreground: purp}
172	servicesHeight := 8
173	return brd.Draw(vxfw.DrawContext{
174		Characters: ctx.Characters,
175		Max: vxfw.Size{
176			Width:  30,
177			Height: uint16(servicesHeight) + 3,
178		},
179	})
180}
181
182func (m *ServicesList) getServiceKv() []Kv {
183	hasPlus := m.ff != nil
184	data := [][]string{
185		{"name", "status"},
186		{"prose", "active"},
187		{"pipe", "active"},
188		{"pastes", "active"},
189		{"rss-to-email", "active"},
190	}
191
192	if hasPlus {
193		data = append(
194			data,
195			[]string{"pages", "active"},
196			[]string{"tuns", "active"},
197			[]string{"irc bouncer", "active"},
198		)
199	} else {
200		data = append(
201			data,
202			[]string{"pages", "free tier"},
203			[]string{"tuns", "pico+"},
204			[]string{"irc bouncer", "pico+"},
205		)
206	}
207
208	kv := []Kv{}
209	for idx, d := range data {
210		value := d[1]
211		var style vaxis.Style
212		if idx == 0 {
213			style = vaxis.Style{
214				UnderlineColor: purp,
215				UnderlineStyle: vaxis.UnderlineDashed,
216				Foreground:     purp,
217			}
218		} else if value == "active" {
219			style = vaxis.Style{Foreground: green}
220		} else if value == "free tier" {
221			style = vaxis.Style{Foreground: oj}
222		} else {
223			style = vaxis.Style{Foreground: red}
224		}
225
226		kv = append(kv, Kv{
227			Key:   d[0],
228			Value: value,
229			Style: style,
230		})
231
232	}
233
234	return kv
235}