repos / pico

pico services - prose.sh, pastes.sh, imgs.sh, feeds.sh, pgs.sh
git clone https://github.com/picosh/pico.git

pico / tui / plus
Eric Bower · 18 Dec 24

plus.go

  1package plus
  2
  3import (
  4	"fmt"
  5	"net/url"
  6
  7	"github.com/charmbracelet/bubbles/viewport"
  8	tea "github.com/charmbracelet/bubbletea"
  9	"github.com/charmbracelet/glamour"
 10	"github.com/picosh/pico/tui/common"
 11	"github.com/picosh/pico/tui/pages"
 12)
 13
 14func PlusView(username string, w int) string {
 15	paymentLink := "https://auth.pico.sh/checkout"
 16	url := fmt.Sprintf("%s/%s", paymentLink, url.QueryEscape(username))
 17	md := fmt.Sprintf(`Signup for pico+
 18
 19## $2/month (billed annually)
 20
 21- tuns
 22  - per-site analytics
 23- pages
 24  - per-site analytics
 25- prose
 26  - no inherent storage limits
 27  - blog analytics
 28- 10GB total storage
 29
 30There are a few ways to purchase a membership. We try our best to
 31provide immediate access to pico+ regardless of payment
 32method.
 33
 34## Online Payment (credit card, paypal)
 35
 36%s
 37
 38## Snail Mail
 39
 40Send cash (USD) or check to:
 41- pico.sh LLC
 42- 206 E Huron St
 43- Ann Arbor MI 48104
 44
 45## Notes
 46
 47You can keep purchasing pico+ to add additional years to your subscription.
 48
 49Have any questions not covered here? [Email](mailto:hello@pico.sh)
 50us or join [IRC](https://pico.sh/irc), we will promptly respond.
 51
 52We do not maintain active subscriptions for pico+.
 53Every year you must pay again. We do not take monthly payments, you
 54must pay for a year up-front. Pricing is subject to change because
 55we plan on continuing to include more services as we build them.`, url)
 56
 57	r, _ := glamour.NewTermRenderer(
 58		// detect background color and pick either the default dark or light theme
 59		glamour.WithAutoStyle(),
 60		glamour.WithWordWrap(w-20),
 61	)
 62	out, _ := r.Render(md)
 63	return out
 64}
 65
 66// Model holds the state of the username UI.
 67type Model struct {
 68	shared   *common.SharedModel
 69	viewport viewport.Model
 70}
 71
 72func headerHeight(shrd *common.SharedModel) int {
 73	return shrd.HeaderHeight
 74}
 75
 76func headerWidth(w int) int {
 77	return w - 2
 78}
 79
 80// NewModel returns a new username model in its initial state.
 81func NewModel(shared *common.SharedModel) Model {
 82	hh := headerHeight(shared)
 83	ww := headerWidth(shared.Width)
 84	viewport := viewport.New(ww, shared.Height-hh)
 85	if shared.User != nil {
 86		viewport.SetContent(PlusView(shared.User.Name, ww))
 87	}
 88
 89	return Model{
 90		shared:   shared,
 91		viewport: viewport,
 92	}
 93}
 94
 95func (m Model) Init() tea.Cmd {
 96	return nil
 97}
 98
 99// Update is the Bubble Tea update loop.
100func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
101	switch msg := msg.(type) {
102	case tea.WindowSizeMsg:
103		m.viewport.Width = headerWidth(msg.Width)
104		hh := headerHeight(m.shared)
105		m.viewport.Height = msg.Height - hh
106	case tea.KeyMsg:
107		switch msg.String() {
108		case "q", "esc":
109			return m, pages.Navigate(pages.MenuPage)
110		}
111	}
112
113	var cmd tea.Cmd
114	m.viewport, cmd = m.viewport.Update(msg)
115	return m, cmd
116}
117
118// View renders current view from the model.
119func (m Model) View() string {
120	return m.viewport.View()
121}