mirror of
https://github.com/charmbracelet/glow.git
synced 2026-08-24 00:54:17 +02:00
Load, browse and render news
This commit is contained in:
parent
1b7c70a829
commit
055ae6e3c2
2 changed files with 134 additions and 29 deletions
|
|
@ -64,7 +64,7 @@ type pagerModel struct {
|
|||
|
||||
// Current document being rendered, sans-glamour rendering. We cache
|
||||
// this here so we can re-render it on resize.
|
||||
currentDocument *charm.Markdown
|
||||
currentDocument *markdown
|
||||
}
|
||||
|
||||
func newPagerModel(glamourStyle string) pagerModel {
|
||||
|
|
@ -144,6 +144,11 @@ func pagerUpdate(msg boba.Msg, m pagerModel) (pagerModel, boba.Cmd) {
|
|||
return m, nil
|
||||
}
|
||||
case "n":
|
||||
// Users can't set the note on news markdown
|
||||
if m.currentDocument.markdownType == newsMarkdown {
|
||||
break
|
||||
}
|
||||
|
||||
m.state = pagerStateSetNote
|
||||
if m.textInput.Value() == "" {
|
||||
// Pre-populate with existing value
|
||||
|
|
|
|||
158
ui/stash.go
158
ui/stash.go
|
|
@ -25,18 +25,46 @@ const (
|
|||
stashHorizontalPadding = 6
|
||||
)
|
||||
|
||||
var (
|
||||
faintGreen = common.NewColorPair("#2B4A3F", "#ABE5D1")
|
||||
green = common.NewColorPair("#04B575", "#04B575")
|
||||
)
|
||||
|
||||
// MSG
|
||||
|
||||
type stashErrMsg error
|
||||
type stashSpinnerTickMsg struct{}
|
||||
type gotStashMsg []*charm.Markdown
|
||||
type gotStashedItemMsg *charm.Markdown
|
||||
type gotNewsMsg []*charm.Markdown
|
||||
type gotStashedItemMsg *markdown
|
||||
type deletedStashedItemMsg int
|
||||
|
||||
// MODEL
|
||||
|
||||
type stashState int
|
||||
|
||||
type markdownType int
|
||||
|
||||
const (
|
||||
userMarkdown markdownType = iota
|
||||
newsMarkdown
|
||||
)
|
||||
|
||||
// markdown wraps charm.Markdown so we can differentiate between stashed items
|
||||
// and news
|
||||
type markdown struct {
|
||||
markdownType markdownType
|
||||
*charm.Markdown
|
||||
}
|
||||
|
||||
// Sort documents by date in descending order
|
||||
type markdownsByCreatedAtDesc []*markdown
|
||||
|
||||
// Sort implementation for MarkdownByCreatedAt
|
||||
func (m markdownsByCreatedAtDesc) Len() int { return len(m) }
|
||||
func (m markdownsByCreatedAtDesc) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
|
||||
func (m markdownsByCreatedAtDesc) Less(i, j int) bool { return m[i].CreatedAt.After(*m[j].CreatedAt) }
|
||||
|
||||
const (
|
||||
stashStateInit stashState = iota
|
||||
stashStateReady
|
||||
|
|
@ -48,22 +76,26 @@ type stashModel struct {
|
|||
cc *charm.Client
|
||||
err error
|
||||
state stashState
|
||||
documents []*charm.Markdown
|
||||
documents []*markdown
|
||||
spinner spinner.Model
|
||||
index int
|
||||
terminalWidth int
|
||||
terminalHeight int
|
||||
loading bool // are we currently loading something?
|
||||
fullyLoaded bool // Have we loaded everything from the server?
|
||||
|
||||
// This is just the index of the current page in view. To get the index
|
||||
// of the selected item as it relates to the full set of documents we've
|
||||
// fetched use the mardownIndex() method of this struct.
|
||||
index int
|
||||
|
||||
// This handles the local pagination, which is different than the page
|
||||
// we're fetching from on the server side
|
||||
paginator paginator.Model
|
||||
|
||||
// Page we're fetching items from on the server, which is different from
|
||||
// the local pagination. Generally, the server will return more items than
|
||||
// we can display at a time so we can paginate locally without having to
|
||||
// fetch every time.
|
||||
// Page we're fetching stash items from on the server, which is different
|
||||
// from the local pagination. Generally, the server will return more items
|
||||
// than we can display at a time so we can paginate locally without having
|
||||
// to fetch every time.
|
||||
page int
|
||||
}
|
||||
|
||||
|
|
@ -82,6 +114,11 @@ func (m *stashModel) setSize(width, height int) {
|
|||
}
|
||||
}
|
||||
|
||||
// markdownIndex returns the index of the currently selected markdown item.
|
||||
func (m stashModel) markdownIndex() int {
|
||||
return m.paginator.Page*m.paginator.PerPage + m.index
|
||||
}
|
||||
|
||||
// INIT
|
||||
|
||||
func stashInit(cc *charm.Client) (stashModel, boba.Cmd) {
|
||||
|
|
@ -103,6 +140,7 @@ func stashInit(cc *charm.Client) (stashModel, boba.Cmd) {
|
|||
|
||||
return m, boba.Batch(
|
||||
loadStash(m),
|
||||
loadNews(m),
|
||||
spinner.Tick(s),
|
||||
)
|
||||
}
|
||||
|
|
@ -156,15 +194,17 @@ func stashUpdate(msg boba.Msg, m stashModel) (stashModel, boba.Cmd) {
|
|||
// Load the document from the server. We'll handle the message
|
||||
// that comes back in the main update function.
|
||||
m.state = stashStateLoadingDocument
|
||||
indexToLoad := m.paginator.Page*m.paginator.PerPage + m.index
|
||||
doc := m.documents[m.markdownIndex()]
|
||||
cmds = append(cmds,
|
||||
loadStashedItem(m.cc, m.documents[indexToLoad].ID),
|
||||
loadStashedItem(m.cc, doc.ID, doc.markdownType),
|
||||
spinner.Tick(m.spinner),
|
||||
)
|
||||
|
||||
case "x":
|
||||
// Confirm deletion
|
||||
m.state = stashStatePromptDelete
|
||||
if m.documents[m.markdownIndex()].markdownType == userMarkdown {
|
||||
m.state = stashStatePromptDelete
|
||||
}
|
||||
|
||||
case "y":
|
||||
if m.state != stashStatePromptDelete {
|
||||
|
|
@ -204,11 +244,22 @@ func stashUpdate(msg boba.Msg, m stashModel) (stashModel, boba.Cmd) {
|
|||
break
|
||||
}
|
||||
|
||||
sort.Sort(charm.MarkdownsByCreatedAtDesc(msg)) // sort by date
|
||||
m.documents = append(m.documents, msg...)
|
||||
docs := wrapMarkdowns(userMarkdown, msg)
|
||||
sort.Sort(markdownsByCreatedAtDesc(docs)) // sort by date
|
||||
m.documents = append(m.documents, docs...)
|
||||
m.state = stashStateReady
|
||||
m.paginator.SetTotalPages(len(m.documents))
|
||||
|
||||
case gotNewsMsg:
|
||||
if len(msg) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
docs := wrapMarkdowns(newsMarkdown, msg)
|
||||
sort.Sort(markdownsByCreatedAtDesc(docs))
|
||||
m.documents = append(m.documents, docs...)
|
||||
m.paginator.SetTotalPages(len(m.documents))
|
||||
|
||||
case stashSpinnerTickMsg:
|
||||
if m.state == stashStateInit || m.state == stashStateLoadingDocument {
|
||||
m.spinner, cmd = spinner.Update(msg, m.spinner)
|
||||
|
|
@ -278,8 +329,8 @@ func stashView(m stashModel) string {
|
|||
break
|
||||
}
|
||||
|
||||
// Blank lines we'll need to fill with newlines if the viewport is
|
||||
// properly filled
|
||||
// We need to fill any empty height with newlines so the footer reaches
|
||||
// the bottom.
|
||||
numBlankLines := max(0, (m.terminalHeight-stashViewTopPadding-stashViewBottomPadding)%stashViewItemHeight)
|
||||
blankLines := ""
|
||||
if numBlankLines > 0 {
|
||||
|
|
@ -310,7 +361,7 @@ func stashView(m stashModel) string {
|
|||
stashPopulatedView(m),
|
||||
blankLines,
|
||||
pagination,
|
||||
helpView(m),
|
||||
stashHelpView(m),
|
||||
)
|
||||
}
|
||||
return "\n" + indent.String(s, 2)
|
||||
|
|
@ -357,8 +408,12 @@ func stashPopulatedView(m stashModel) string {
|
|||
return s
|
||||
}
|
||||
|
||||
func helpView(m stashModel) string {
|
||||
h := []string{}
|
||||
func stashHelpView(m stashModel) string {
|
||||
var (
|
||||
h []string
|
||||
isNews bool = m.documents[m.markdownIndex()].markdownType == newsMarkdown
|
||||
)
|
||||
|
||||
if m.state == stashStatePromptDelete {
|
||||
h = append(h, "y: delete", "n: cancel")
|
||||
} else {
|
||||
|
|
@ -369,16 +424,26 @@ func helpView(m stashModel) string {
|
|||
if m.paginator.TotalPages > 1 {
|
||||
h = append(h, "h/l, ←/→: page")
|
||||
}
|
||||
h = append(h, []string{"x: delete", "esc: exit"}...)
|
||||
if !isNews {
|
||||
h = append(h, []string{"x: delete"}...)
|
||||
}
|
||||
h = append(h, []string{"esc: exit"}...)
|
||||
}
|
||||
return common.HelpView(h...)
|
||||
}
|
||||
|
||||
type stashListItemView charm.Markdown
|
||||
// stashListItemView renders items in the stash view
|
||||
type stashListItemView markdown
|
||||
|
||||
func (m stashListItemView) render(width int, state common.State) string {
|
||||
|
||||
line := common.VerticalLine(state) + " "
|
||||
var line string
|
||||
if m.markdownType == newsMarkdown && state != common.StateSelected {
|
||||
line = te.String("│").Foreground(faintGreen.Color()).String()
|
||||
} else {
|
||||
line = common.VerticalLine(state)
|
||||
}
|
||||
line += " "
|
||||
|
||||
keyColor := common.NoColor
|
||||
switch state {
|
||||
|
|
@ -386,18 +451,30 @@ func (m stashListItemView) render(width int, state common.State) string {
|
|||
keyColor = common.Fuschia
|
||||
case common.StateDeleting:
|
||||
keyColor = common.Red
|
||||
default:
|
||||
if m.markdownType == newsMarkdown {
|
||||
keyColor = common.Green
|
||||
}
|
||||
}
|
||||
|
||||
titleKey := strconv.Itoa(m.ID)
|
||||
titleKey := "#" + strconv.Itoa(m.ID)
|
||||
if m.markdownType == newsMarkdown {
|
||||
titleKey = "System Announcement"
|
||||
}
|
||||
if m.Note != "" {
|
||||
titleKey += ":"
|
||||
}
|
||||
|
||||
titleVal := truncate(m.Note, width-(stashHorizontalPadding*2)-len(titleKey))
|
||||
titleVal = titleView(titleVal, state)
|
||||
dateKey := "Stashed:"
|
||||
if m.markdownType == newsMarkdown {
|
||||
dateKey = "Posted:"
|
||||
}
|
||||
|
||||
titleKey = te.String("#" + titleKey).Foreground(keyColor.Color()).String()
|
||||
dateKey := te.String("Stashed:").Foreground(keyColor.Color()).String()
|
||||
titleVal := truncate(m.Note, width-(stashHorizontalPadding*2)-len(titleKey))
|
||||
titleVal = m.title(titleVal, state)
|
||||
|
||||
titleKey = te.String(titleKey).Foreground(keyColor.Color()).String()
|
||||
dateKey = te.String(dateKey).Foreground(keyColor.Color()).String()
|
||||
dateVal := m.date(state)
|
||||
|
||||
var s string
|
||||
|
|
@ -415,7 +492,7 @@ func (m stashListItemView) date(state common.State) string {
|
|||
return te.String(s).Foreground(c.Color()).String()
|
||||
}
|
||||
|
||||
func titleView(title string, state common.State) string {
|
||||
func (m stashListItemView) title(title string, state common.State) string {
|
||||
if title == "" {
|
||||
return ""
|
||||
}
|
||||
|
|
@ -438,13 +515,26 @@ func loadStash(m stashModel) boba.Cmd {
|
|||
}
|
||||
}
|
||||
|
||||
func loadStashedItem(cc *charm.Client, id int) boba.Cmd {
|
||||
func loadNews(m stashModel) boba.Cmd {
|
||||
return func() boba.Msg {
|
||||
m, err := cc.GetStashMarkdown(id)
|
||||
stash, err := m.cc.GetNews(1) // just fetch the first page
|
||||
if err != nil {
|
||||
return stashErrMsg(err)
|
||||
}
|
||||
return gotStashedItemMsg(m)
|
||||
return gotNewsMsg(stash)
|
||||
}
|
||||
}
|
||||
|
||||
func loadStashedItem(cc *charm.Client, id int, t markdownType) boba.Cmd {
|
||||
return func() boba.Msg {
|
||||
md, err := cc.GetStashMarkdown(id)
|
||||
if err != nil {
|
||||
return stashErrMsg(err)
|
||||
}
|
||||
return gotStashedItemMsg(&markdown{
|
||||
markdownType: t,
|
||||
Markdown: md,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -460,6 +550,16 @@ func deleteStashedItem(cc *charm.Client, id int) boba.Cmd {
|
|||
|
||||
// ETC
|
||||
|
||||
func wrapMarkdowns(t markdownType, md []*charm.Markdown) (m []*markdown) {
|
||||
for _, v := range md {
|
||||
m = append(m, &markdown{
|
||||
markdownType: t,
|
||||
Markdown: v,
|
||||
})
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func truncate(str string, num int) string {
|
||||
s := str
|
||||
if len(str) > num {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue