2024-03-20 01:38:46 +00:00
|
|
|
package tui
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
2024-03-23 00:43:15 +00:00
|
|
|
"code.humancabbage.net/sam/moonmath/coindesk"
|
2024-03-20 04:44:11 +00:00
|
|
|
"code.humancabbage.net/sam/moonmath/config"
|
2024-03-23 00:43:15 +00:00
|
|
|
"code.humancabbage.net/sam/moonmath/tui/asset"
|
2024-03-20 01:38:46 +00:00
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
|
|
)
|
|
|
|
|
2024-03-23 00:43:15 +00:00
|
|
|
type Model struct {
|
|
|
|
assets []asset.Model
|
|
|
|
}
|
|
|
|
|
|
|
|
func New(assets []coindesk.Asset, cfg config.All) (p *tea.Program) {
|
|
|
|
model := newModel(assets, cfg)
|
2024-03-21 09:28:56 +00:00
|
|
|
p = tea.NewProgram(
|
2024-03-23 00:43:15 +00:00
|
|
|
model,
|
2024-03-21 09:28:56 +00:00
|
|
|
tea.WithAltScreen(),
|
|
|
|
tea.WithFPS(30),
|
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-03-23 00:43:15 +00:00
|
|
|
func newModel(assets []coindesk.Asset, cfg config.All) (m Model) {
|
|
|
|
// construct models for each asset, but don't filter out dupes
|
|
|
|
seen := map[coindesk.Asset]struct{}{}
|
|
|
|
for _, a := range assets {
|
|
|
|
_, ok := seen[a]
|
|
|
|
if ok {
|
|
|
|
continue
|
2024-03-21 09:28:56 +00:00
|
|
|
}
|
2024-03-23 00:43:15 +00:00
|
|
|
assetCfg := cfg.GetData(a)
|
|
|
|
assetModel := asset.New(assetCfg)
|
|
|
|
m.assets = append(m.assets, assetModel)
|
|
|
|
seen[a] = struct{}{}
|
2024-03-21 09:28:56 +00:00
|
|
|
}
|
|
|
|
return
|
2024-03-20 01:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m Model) Init() tea.Cmd {
|
2024-03-23 00:43:15 +00:00
|
|
|
// initialize child models, collecting their commands,
|
|
|
|
// then return them all in a batch
|
|
|
|
var inits []tea.Cmd
|
|
|
|
for i := range m.assets {
|
|
|
|
cmd := m.assets[i].Init()
|
|
|
|
inits = append(inits, cmd)
|
|
|
|
}
|
|
|
|
return tea.Batch(inits...)
|
2024-03-20 01:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
|
|
switch msg := msg.(type) {
|
2024-03-23 00:43:15 +00:00
|
|
|
// handle keys for quitting
|
2024-03-20 01:38:46 +00:00
|
|
|
case tea.KeyMsg:
|
|
|
|
switch msg.String() {
|
|
|
|
case "ctrl+c", "q", "esc":
|
|
|
|
return m, tea.Quit
|
|
|
|
}
|
2024-03-23 00:43:15 +00:00
|
|
|
// forward asset messages to the appropriate model
|
|
|
|
case asset.Msg:
|
|
|
|
cmd := m.forward(msg.Asset, msg)
|
|
|
|
return m, cmd
|
|
|
|
// forward any other message to each child model.
|
|
|
|
// typically, this is for animation.
|
|
|
|
default:
|
|
|
|
var commands []tea.Cmd
|
|
|
|
for i := range m.assets {
|
|
|
|
var cmd tea.Cmd
|
|
|
|
m.assets[i], cmd = m.assets[i].Update(msg)
|
|
|
|
commands = append(commands, cmd)
|
|
|
|
}
|
|
|
|
return m, tea.Batch(commands...)
|
2024-03-20 01:38:46 +00:00
|
|
|
}
|
|
|
|
return m, nil
|
|
|
|
}
|
|
|
|
|
2024-03-23 00:43:15 +00:00
|
|
|
func (m Model) View() string {
|
|
|
|
var ss []string
|
|
|
|
for i := range m.assets {
|
|
|
|
s := m.assets[i].View()
|
|
|
|
ss = append(ss, s)
|
2024-03-21 06:40:38 +00:00
|
|
|
}
|
2024-03-23 00:43:15 +00:00
|
|
|
r := lipgloss.JoinVertical(lipgloss.Center, ss...)
|
|
|
|
return r
|
2024-03-21 06:40:38 +00:00
|
|
|
}
|
2024-03-20 01:38:46 +00:00
|
|
|
|
2024-03-23 00:43:15 +00:00
|
|
|
func (m Model) forward(a coindesk.Asset, msg tea.Msg) (cmd tea.Cmd) {
|
|
|
|
// O(n) is fine when n is small
|
|
|
|
for i := range m.assets {
|
|
|
|
if !m.assets[i].Handles(a) {
|
|
|
|
continue
|
2024-03-20 01:38:46 +00:00
|
|
|
}
|
2024-03-23 00:43:15 +00:00
|
|
|
m.assets[i], cmd = m.assets[i].Update(msg)
|
|
|
|
return
|
2024-03-20 01:38:46 +00:00
|
|
|
}
|
2024-03-23 00:43:15 +00:00
|
|
|
panic(fmt.Errorf("rogue message: %v", msg))
|
2024-03-20 01:38:46 +00:00
|
|
|
}
|