repos / pico

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

pico / pkg / tui
Eric Bower  ·  2026-04-16

plus.go

  1package tui
  2
  3import (
  4	"fmt"
  5	"math"
  6	"net/url"
  7
  8	"git.sr.ht/~rockorager/vaxis"
  9	"git.sr.ht/~rockorager/vaxis/vxfw"
 10	"git.sr.ht/~rockorager/vaxis/vxfw/richtext"
 11)
 12
 13type PlusPage struct {
 14	shared *SharedModel
 15
 16	pager *Pager
 17}
 18
 19func NewPlusPage(shrd *SharedModel) *PlusPage {
 20	page := &PlusPage{
 21		shared: shrd,
 22	}
 23	page.pager = NewPager()
 24	return page
 25}
 26
 27func (m *PlusPage) HandleEvent(ev vaxis.Event, phase vxfw.EventPhase) (vxfw.Command, error) {
 28	switch ev.(type) {
 29	case PageIn:
 30		return vxfw.FocusWidgetCmd(m.pager), nil
 31	}
 32	return nil, nil
 33}
 34
 35func (m *PlusPage) header(ctx vxfw.DrawContext) vxfw.Surface {
 36	intro := richtext.New([]vaxis.Segment{
 37		{
 38			Text: "$2/mo\n",
 39			Style: vaxis.Style{
 40				UnderlineStyle: vaxis.UnderlineCurly,
 41				UnderlineColor: oj,
 42				Foreground:     oj,
 43			},
 44		},
 45		{
 46			Text: "(billed yearly)\n\n",
 47		},
 48
 49		{Text: "• tuns\n"},
 50		{Text: "  • per-site analytics\n"},
 51		{Text: "• pgs\n"},
 52		{Text: "  • per-file max size 100mb\n"},
 53		{Text: "  • per-site analytics\n"},
 54		{Text: "• prose\n"},
 55		{Text: "  • blog analytics\n"},
 56		{Text: "• irc bouncer\n"},
 57		{Text: "• 10GB total storage\n"},
 58	})
 59	brd := NewBorder(intro)
 60	brd.Label = "pico+"
 61	surf, _ := brd.Draw(ctx)
 62	return surf
 63}
 64
 65func (m *PlusPage) payment(ctx vxfw.DrawContext) vxfw.Surface {
 66	paymentLink := "https://auth.pico.sh/checkout"
 67	link := fmt.Sprintf("%s/%s", paymentLink, url.QueryEscape(m.shared.User.Name))
 68	header := vaxis.Style{Foreground: oj, UnderlineColor: oj, UnderlineStyle: vaxis.UnderlineDashed}
 69	pay := richtext.New([]vaxis.Segment{
 70		{Text: "You can use this same flow to add additional years to your membership, including if you are already a pico+ user.\n\n", Style: vaxis.Style{Foreground: green}},
 71
 72		{Text: "There are a few ways to purchase a membership. We try our best to provide immediate access to pico+ regardless of payment method.\n"},
 73
 74		{Text: "\nOnline payment\n\n", Style: header},
 75
 76		{Text: link + "\n", Style: vaxis.Style{Hyperlink: link}},
 77
 78		{Text: "\nSnail mail\n\n", Style: header},
 79
 80		{Text: "Send cash (USD) or check to:\n\n"},
 81		{Text: "• pico.sh LLC\n"},
 82		{Text: "• 206 E Huron St\n"},
 83		{Text: "• Ann Arbor MI 48104\n\n"},
 84		{Text: "Have any questions? Feel free to reach out:\n\n"},
 85		{Text: "• "}, {Text: "mailto:hello@pico.sh\n", Style: vaxis.Style{Hyperlink: "mailto:hello@pico.sh"}},
 86		{Text: "• "}, {Text: "https://pico.sh/irc\n", Style: vaxis.Style{Hyperlink: "https://pico.sh/irc"}},
 87	})
 88	brd := NewBorder(pay)
 89	brd.Label = "payment"
 90	surf, _ := brd.Draw(vxfw.DrawContext{
 91		Characters: ctx.Characters,
 92		Max:        vxfw.Size{Width: 50, Height: math.MaxUint16},
 93	})
 94	return surf
 95}
 96
 97func (m *PlusPage) notes(ctx vxfw.DrawContext) vxfw.Surface {
 98	wdgt := richtext.New([]vaxis.Segment{
 99		{Text: "We do not maintain active subscriptions. "},
100		{Text: "Every year you must pay again. We do not take monthly payments, you must pay for a year up-front. Pricing is subject to change."},
101	})
102	brd := NewBorder(wdgt)
103	brd.Label = "notes"
104	surf, _ := brd.Draw(vxfw.DrawContext{
105		Characters: ctx.Characters,
106		Max:        vxfw.Size{Width: 50, Height: math.MaxUint16},
107	})
108	return surf
109}
110
111func (m *PlusPage) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) {
112	stack := NewGroupStack([]vxfw.Surface{
113		m.header(ctx),
114		m.notes(ctx),
115		m.payment(ctx),
116	})
117	stack.Gap = 1
118	surf, _ := stack.Draw(createDrawCtx(ctx, math.MaxUint16))
119	m.pager.Surface = surf
120	return m.pager.Draw(ctx)
121}