ficsit-cli-flake/tea/scenes/mod_info.go

239 lines
6.4 KiB
Go
Raw Normal View History

2021-12-02 04:00:33 +00:00
package scenes
import (
"context"
"strconv"
"strings"
"time"
2021-12-02 04:00:33 +00:00
2021-12-04 18:02:05 +00:00
"github.com/rs/zerolog/log"
2021-12-04 05:47:48 +00:00
"github.com/PuerkitoBio/goquery"
2021-12-04 00:21:19 +00:00
md "github.com/JohannesKaufmann/html-to-markdown"
2021-12-02 04:00:33 +00:00
"github.com/charmbracelet/bubbles/help"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/spinner"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/glamour"
"github.com/charmbracelet/lipgloss"
"github.com/satisfactorymodding/ficsit-cli/ficsit"
"github.com/satisfactorymodding/ficsit-cli/tea/components"
"github.com/satisfactorymodding/ficsit-cli/tea/utils"
)
var _ tea.Model = (*modVersionMenu)(nil)
type modInfo struct {
root components.RootModel
viewport viewport.Model
spinner spinner.Model
parent tea.Model
2022-06-06 23:55:26 +00:00
modData chan ficsit.GetModMod
modError chan string
2021-12-02 04:00:33 +00:00
ready bool
help help.Model
keys modInfoKeyMap
error *components.ErrorComponent
2021-12-02 04:00:33 +00:00
}
type modInfoKeyMap struct {
Up key.Binding
UpHalf key.Binding
UpPage key.Binding
Down key.Binding
DownHalf key.Binding
DownPage key.Binding
Help key.Binding
Back key.Binding
}
func (k modInfoKeyMap) ShortHelp() []key.Binding {
return []key.Binding{k.Help, k.Back}
}
func (k modInfoKeyMap) FullHelp() [][]key.Binding {
return [][]key.Binding{
{k.Up, k.UpHalf, k.UpPage},
{k.Down, k.DownHalf, k.DownPage},
{k.Help, k.Back},
}
}
func NewModInfo(root components.RootModel, parent tea.Model, mod utils.Mod) tea.Model {
model := modInfo{
root: root,
viewport: viewport.Model{},
2022-04-14 01:27:39 +00:00
spinner: spinner.New(),
2021-12-02 04:00:33 +00:00
parent: parent,
2022-06-06 23:55:26 +00:00
modData: make(chan ficsit.GetModMod),
modError: make(chan string),
2021-12-02 04:00:33 +00:00
ready: false,
2022-04-14 01:27:39 +00:00
help: help.New(),
2021-12-02 04:00:33 +00:00
keys: modInfoKeyMap{
Up: key.NewBinding(key.WithHelp("↑/k", "move up")),
UpHalf: key.NewBinding(key.WithHelp("u", "up half page")),
UpPage: key.NewBinding(key.WithHelp("pgup/b", "page up")),
Down: key.NewBinding(key.WithHelp("↓/j", "move down")),
DownHalf: key.NewBinding(key.WithHelp("d", "down half page")),
DownPage: key.NewBinding(key.WithHelp("pgdn/ /f", "page down")),
Help: key.NewBinding(key.WithHelp("?", "toggle help")),
Back: key.NewBinding(key.WithHelp("q", "back")),
},
}
model.spinner.Spinner = spinner.MiniDot
model.help.Width = root.Size().Width
go func() {
2022-06-06 23:55:26 +00:00
fullMod, err := ficsit.GetMod(context.TODO(), root.GetAPIClient(), mod.Reference)
2021-12-02 04:00:33 +00:00
if err != nil {
model.modError <- err.Error()
return
2021-12-02 04:00:33 +00:00
}
if fullMod == nil {
model.modError <- "unknown error (mod is nil)"
return
2021-12-02 04:00:33 +00:00
}
2022-06-06 23:55:26 +00:00
model.modData <- fullMod.Mod
2021-12-02 04:00:33 +00:00
}()
return model
}
func (m modInfo) Init() tea.Cmd {
return tea.Batch(utils.Ticker(), spinner.Tick)
}
func (m modInfo) CalculateSizes(msg tea.WindowSizeMsg) (tea.Model, tea.Cmd) {
if m.viewport.Width == 0 {
return m, nil
}
bottomPadding := 2
if m.help.ShowAll {
bottomPadding = 4
}
top, right, bottom, left := lipgloss.NewStyle().Margin(m.root.Height(), 3, bottomPadding).GetMargin()
m.viewport.Width = msg.Width - left - right
m.viewport.Height = msg.Height - top - bottom
m.root.SetSize(msg)
m.help.Width = m.viewport.Width
var cmd tea.Cmd
m.viewport, cmd = m.viewport.Update(msg)
return m, cmd
}
func (m modInfo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch keypress := msg.String(); keypress {
case KeyControlC:
return m, tea.Quit
case "q":
if m.parent != nil {
m.parent.Update(m.root.Size())
return m.parent, nil
}
return m, tea.Quit
case "?":
m.help.ShowAll = !m.help.ShowAll
newModel, cmd := m.CalculateSizes(m.root.Size())
return newModel, cmd
default:
var cmd tea.Cmd
m.viewport, cmd = m.viewport.Update(msg)
return m, cmd
}
case tea.WindowSizeMsg:
return m.CalculateSizes(msg)
case spinner.TickMsg:
var cmd tea.Cmd
m.spinner, cmd = m.spinner.Update(msg)
return m, cmd
case utils.TickMsg:
select {
case mod := <-m.modData:
bottomPadding := 2
if m.help.ShowAll {
bottomPadding = 4
}
top, right, bottom, left := lipgloss.NewStyle().Margin(m.root.Height(), 3, bottomPadding).GetMargin()
m.viewport = viewport.Model{Width: m.root.Size().Width - left - right, Height: m.root.Size().Height - top - bottom}
title := lipgloss.NewStyle().Padding(0, 2).Render(utils.TitleStyle.Render(mod.Name)) + "\n"
sidebar := ""
sidebar += utils.LabelStyle.Render("Views: ") + strconv.Itoa(mod.Views) + "\n"
sidebar += utils.LabelStyle.Render("Downloads: ") + strconv.Itoa(mod.Downloads) + "\n"
sidebar += "\n"
sidebar += utils.LabelStyle.Render("Authors:") + "\n"
for _, author := range mod.Authors {
sidebar += "\n"
sidebar += utils.LabelStyle.Render(author.User.Username) + " - " + author.Role
}
2021-12-04 00:21:19 +00:00
converter := md.NewConverter("", true, nil)
2021-12-04 05:47:48 +00:00
converter.AddRules(md.Rule{
Filter: []string{"#text"},
Replacement: func(content string, selec *goquery.Selection, options *md.Options) *string {
text := selec.Text()
return &text
},
})
2021-12-04 00:21:19 +00:00
markdownDescription, err := converter.ConvertString(mod.Full_description)
if err != nil {
2021-12-04 18:02:05 +00:00
log.Error().Err(err).Msg("failed to convert html to markdown")
markdownDescription = mod.Full_description
2021-12-04 00:21:19 +00:00
}
description, err := glamour.Render(markdownDescription, "dark")
2021-12-02 04:00:33 +00:00
if err != nil {
2021-12-04 18:02:05 +00:00
log.Error().Err(err).Msg("failed to render markdown")
description = mod.Full_description
2021-12-02 04:00:33 +00:00
}
bottomPart := lipgloss.JoinHorizontal(lipgloss.Top, sidebar, strings.TrimSpace(description))
m.viewport.SetContent(lipgloss.JoinVertical(lipgloss.Left, title, bottomPart))
var cmd tea.Cmd
m.viewport, cmd = m.viewport.Update(msg)
return m, cmd
case err := <-m.modError:
errorComponent, cmd := components.NewErrorComponent(err, time.Second*5)
m.error = errorComponent
return m, cmd
2021-12-02 04:00:33 +00:00
default:
return m, utils.Ticker()
}
}
return m, nil
}
func (m modInfo) View() string {
if m.error != nil {
helpBar := lipgloss.NewStyle().Padding(1, 2).Render(m.help.View(m.keys))
return lipgloss.JoinVertical(lipgloss.Left, m.root.View(), (*m.error).View(), m.viewport.View(), helpBar)
}
2021-12-02 04:00:33 +00:00
if m.viewport.Height == 0 {
spinnerView := lipgloss.NewStyle().Padding(0, 2, 1).Render(m.spinner.View() + " Loading...")
return lipgloss.JoinVertical(lipgloss.Left, m.root.View(), spinnerView)
}
helpBar := lipgloss.NewStyle().Padding(1, 2).Render(m.help.View(m.keys))
return lipgloss.JoinVertical(lipgloss.Left, m.root.View(), m.viewport.View(), helpBar)
}