repos / pico

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

commit
428b94c
parent
2b9a0ab
author
Eric Bower
date
2024-05-13 17:42:27 +0000 UTC
chore: cleanup
3 files changed,  +167, -6
M pico/cli.go
+13, -0
 1@@ -10,6 +10,7 @@ import (
 2 	"github.com/picosh/pico/db"
 3 	"github.com/picosh/pico/shared"
 4 	"github.com/picosh/pico/tui/common"
 5+	"github.com/picosh/pico/tui/notifications"
 6 	"github.com/picosh/pico/tui/plus"
 7 	"github.com/picosh/send/send/utils"
 8 )
 9@@ -56,6 +57,12 @@ func (c *Cmd) plus() {
10 	c.output(view)
11 }
12 
13+func (c *Cmd) notifications() error {
14+	md := notifications.NotificationsView(c.Dbpool, c.User.ID)
15+	c.output(md)
16+	return nil
17+}
18+
19 type CliHandler struct {
20 	DBPool db.DB
21 	Logger *slog.Logger
22@@ -120,6 +127,12 @@ func WishMiddleware(handler *CliHandler) wish.Middleware {
23 				} else if cmd == "pico+" {
24 					opts.plus()
25 					return
26+				} else if cmd == "notifications" {
27+					err := opts.notifications()
28+					if err != nil {
29+						wish.Fatalln(sesh, err)
30+					}
31+					return
32 				} else {
33 					next(sesh)
34 					return
M tui/cms.go
+28, -6
  1@@ -20,6 +20,7 @@ import (
  2 	"github.com/picosh/pico/tui/common"
  3 	"github.com/picosh/pico/tui/info"
  4 	"github.com/picosh/pico/tui/keys"
  5+	"github.com/picosh/pico/tui/notifications"
  6 	"github.com/picosh/pico/tui/plus"
  7 	"github.com/picosh/pico/tui/tokens"
  8 )
  9@@ -33,6 +34,7 @@ const (
 10 	statusBrowsingKeys
 11 	statusBrowsingTokens
 12 	statusBrowsingPlus
 13+	statusBrowsingNotifications
 14 	statusChat
 15 	statusQuitting
 16 )
 17@@ -55,6 +57,7 @@ const (
 18 	keysChoice menuChoice = iota
 19 	tokensChoice
 20 	plusChoice
 21+	notificationsChoice
 22 	chatChoice
 23 	exitChoice
 24 	unsetChoice // set when no choice has been made
 25@@ -62,11 +65,12 @@ const (
 26 
 27 // menu text corresponding to menu choices. these are presented to the user.
 28 var menuChoices = map[menuChoice]string{
 29-	keysChoice:   "manage keys",
 30-	tokensChoice: "manage tokens",
 31-	plusChoice:   "pico+",
 32-	chatChoice:   "chat",
 33-	exitChoice:   "exit",
 34+	keysChoice:          "Manage keys",
 35+	tokensChoice:        "Manage tokens",
 36+	plusChoice:          "Pico+",
 37+	notificationsChoice: "Notifications",
 38+	chatChoice:          "Chat",
 39+	exitChoice:          "Exit",
 40 }
 41 
 42 func NewSpinner(styles common.Styles) spinner.Model {
 43@@ -142,6 +146,7 @@ type model struct {
 44 	keys            keys.Model
 45 	tokens          tokens.Model
 46 	plus            plus.Model
 47+	notifications   notifications.Model
 48 	createAccount   account.CreateModel
 49 	terminalSize    tea.WindowSizeMsg
 50 	session         ssh.Session
 51@@ -243,6 +248,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 52 		m.tokens = tokens.NewModel(m.styles, m.dbpool, m.user)
 53 		m.createAccount = account.NewCreateModel(m.styles, m.dbpool, m.publicKey)
 54 		m.plus = plus.NewModel(m.styles, m.user, m.session)
 55+		m.notifications = notifications.NewModel(m.styles, m.dbpool, m.user, m.session)
 56 	}
 57 
 58 	switch m.status {
 59@@ -252,6 +258,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 60 		m.tokens = tokens.NewModel(m.styles, m.dbpool, m.user)
 61 		m.createAccount = account.NewCreateModel(m.styles, m.dbpool, m.publicKey)
 62 		m.plus = plus.NewModel(m.styles, m.user, m.session)
 63+		m.notifications = notifications.NewModel(m.styles, m.dbpool, m.user, m.session)
 64 		if m.user == nil {
 65 			m.status = statusNoAccount
 66 		} else {
 67@@ -308,7 +315,16 @@ func updateChildren(msg tea.Msg, m model) (model, tea.Cmd) {
 68 		if m.plus.Done {
 69 			m.plus = plus.NewModel(m.styles, m.user, m.session)
 70 			m.status = statusReady
 71-		} else if m.tokens.Quit {
 72+		} else if m.plus.Quit {
 73+			m.status = statusQuitting
 74+			return m, tea.Quit
 75+		}
 76+	case statusBrowsingNotifications:
 77+		m.notifications, cmd = notifications.Update(msg, m.notifications)
 78+		if m.notifications.Done {
 79+			m.notifications = notifications.NewModel(m.styles, m.dbpool, m.user, m.session)
 80+			m.status = statusReady
 81+		} else if m.notifications.Quit {
 82 			m.status = statusQuitting
 83 			return m, tea.Quit
 84 		}
 85@@ -336,6 +352,9 @@ func updateChildren(msg tea.Msg, m model) (model, tea.Cmd) {
 86 	case plusChoice:
 87 		m.status = statusBrowsingPlus
 88 		m.menuChoice = unsetChoice
 89+	case notificationsChoice:
 90+		m.status = statusBrowsingNotifications
 91+		m.menuChoice = unsetChoice
 92 	case chatChoice:
 93 		m.status = statusChat
 94 		m.menuChoice = unsetChoice
 95@@ -435,6 +454,9 @@ func (m model) View() string {
 96 	case statusBrowsingPlus:
 97 		s = plus.View(m.plus)
 98 		return s
 99+	case statusBrowsingNotifications:
100+		s = notifications.View(m.notifications)
101+		return s
102 	}
103 	return m.styles.App.Render(wrap.String(wordwrap.String(s, w), w))
104 }
A tui/notifications/notifications.go
+126, -0
  1@@ -0,0 +1,126 @@
  2+package notifications
  3+
  4+import (
  5+	"fmt"
  6+
  7+	"github.com/charmbracelet/bubbles/viewport"
  8+	tea "github.com/charmbracelet/bubbletea"
  9+	"github.com/charmbracelet/glamour"
 10+	"github.com/charmbracelet/ssh"
 11+	"github.com/picosh/pico/db"
 12+	"github.com/picosh/pico/tui/common"
 13+)
 14+
 15+func NotificationsView(dbpool db.DB, userID string) string {
 16+	pass, err := dbpool.UpsertToken(userID, "pico-rss")
 17+	if err != nil {
 18+		return err.Error()
 19+	}
 20+	url := fmt.Sprintf("https://auth.pico.sh/rss/%s", pass)
 21+	md := fmt.Sprintf(`# Notifications
 22+
 23+We provide a special RSS feed for all pico users where we can send
 24+user-specific notifications. This is where we will send pico+
 25+expiration notices, among other alerts. To be clear, this is
 26+optional but **highly** recommended.
 27+
 28+Add this URL to your RSS feed reader:
 29+
 30+%s
 31+
 32+## Using our [rss-to-email](https://pico.sh/feeds) service
 33+
 34+Create a feeds file (e.g. pico.txt):`, url)
 35+
 36+	md += "\n```\n"
 37+	md += fmt.Sprintf(`=: email rss@myemail.com
 38+=: digest_interval 1day
 39+=> %s
 40+`, url)
 41+	md += "\n```\n"
 42+	md += "Then copy the file to us:\n"
 43+	md += "```\nrsync pico.txt feeds.pico.sh:/\n```"
 44+
 45+	r, _ := glamour.NewTermRenderer(
 46+		// detect background color and pick either the default dark or light theme
 47+		glamour.WithAutoStyle(),
 48+	)
 49+	out, err := r.Render(md)
 50+	if err != nil {
 51+		return err.Error()
 52+	}
 53+	return out
 54+}
 55+
 56+// Model holds the state of the username UI.
 57+type Model struct {
 58+	Done bool // true when it's time to exit this view
 59+	Quit bool // true when the user wants to quit the whole program
 60+
 61+	styles   common.Styles
 62+	user     *db.User
 63+	viewport viewport.Model
 64+}
 65+
 66+func headerHeight(styles common.Styles) int {
 67+	return 0
 68+}
 69+
 70+func headerWidth(w int) int {
 71+	return w - 2
 72+}
 73+
 74+// NewModel returns a new username model in its initial state.
 75+func NewModel(styles common.Styles, dbpool db.DB, user *db.User, session ssh.Session) Model {
 76+	pty, _, _ := session.Pty()
 77+	hh := headerHeight(styles)
 78+	viewport := viewport.New(headerWidth(pty.Window.Width), pty.Window.Height-hh)
 79+	viewport.YPosition = hh
 80+	viewport.SetContent(NotificationsView(dbpool, user.ID))
 81+
 82+	return Model{
 83+		Done:     false,
 84+		Quit:     false,
 85+		styles:   styles,
 86+		user:     user,
 87+		viewport: viewport,
 88+	}
 89+}
 90+
 91+// Update is the Bubble Tea update loop.
 92+func Update(msg tea.Msg, m Model) (Model, tea.Cmd) {
 93+	var cmd tea.Cmd
 94+	var cmds []tea.Cmd
 95+
 96+	switch msg := msg.(type) {
 97+	case tea.KeyMsg:
 98+		switch msg.Type {
 99+		case tea.KeyCtrlC:
100+			m.Quit = true
101+		case tea.KeyEscape:
102+			m.Done = true
103+
104+		default:
105+			switch msg.String() {
106+			case "q":
107+				m.Done = true
108+			}
109+		}
110+
111+	case tea.WindowSizeMsg:
112+		m.viewport.Width = headerWidth(msg.Width)
113+		hh := headerHeight(m.styles)
114+		m.viewport.Height = msg.Height - hh
115+	}
116+
117+	m.viewport, cmd = m.viewport.Update(msg)
118+	cmds = append(cmds, cmd)
119+
120+	return m, tea.Batch(cmds...)
121+}
122+
123+// View renders current view from the model.
124+func View(m Model) string {
125+	s := m.viewport.View()
126+	return s
127+}