Only show news in news section

This commit is contained in:
Christian Rocha 2020-11-25 11:40:24 -05:00
commit 40a02259fc
6 changed files with 471 additions and 420 deletions

View file

@ -294,9 +294,9 @@ func runTUI(stashedOnly bool) error {
cfg.GlamourStyle = style
if stashedOnly {
cfg.DocumentTypes = ui.StashedDocuments | ui.NewsDocuments
cfg.DocumentTypes = ui.StashedDocument | ui.NewsDocument
} else if localOnly {
cfg.DocumentTypes = ui.LocalDocuments
cfg.DocumentTypes = ui.LocalDocument
}
// Run Bubble Tea program

View file

@ -2,25 +2,29 @@ package ui
import (
"log"
"math"
"strings"
"time"
"unicode"
"github.com/charmbracelet/charm"
"github.com/dustin/go-humanize"
"golang.org/x/text/transform"
"golang.org/x/text/unicode/norm"
)
// markdownType allows us to differentiate between the types of markdown
// documents we're dealing with.
type markdownType int
type DocumentType byte
const (
stashedMarkdown markdownType = iota
newsMarkdown
localMarkdown
convertedMarkdown // used to be local, now its stashed
LocalDocument DocumentType = 1 << iota
StashedDocument
ConvertedDocument
NewsDocument
)
// markdown wraps charm.Markdown.
type markdown struct {
markdownType markdownType
markdownType DocumentType
// Full path of a local markdown file. Only relevant to local documents and
// those that have been stashed in this session.
@ -43,7 +47,7 @@ func (m *markdown) buildFilterValue() {
m.filterValue = m.Note
}
if m.markdownType == newsMarkdown {
if m.markdownType == NewsDocument {
m.filterValue = "News: " + note
return
}
@ -54,7 +58,7 @@ func (m *markdown) buildFilterValue() {
// sortAsLocal returns whether or not this markdown should be sorted as though
// it's a local markdown document.
func (m markdown) sortAsLocal() bool {
return m.markdownType == localMarkdown || m.markdownType == convertedMarkdown
return m.markdownType == LocalDocument || m.markdownType == ConvertedDocument
}
// Sort documents with local files first, then by date.
@ -88,3 +92,64 @@ func (m markdownsByLocalFirst) Less(i, j int) bool {
// If the timestamps also match, sort by ID.
return m[i].ID > m[j].ID
}
func (m markdown) relativeTime() string {
return relativeTime(m.CreatedAt)
}
// Normalize text to aid in the filtering process. In particular, we remove
// diacritics, "ö" becomes "o".
func normalize(in string) (string, error) {
t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
out, _, err := transform.String(t, in)
return out, err
}
// Returns whether a given rune is a nonspacing mark (Mn is the key for
// nonspacing marks)
func isMn(r rune) bool {
return unicode.Is(unicode.Mn, r)
}
// wrapMarkdowns wraps a *charm.Markdown with a *markdown in order to add some
// extra metadata.
func wrapMarkdowns(t DocumentType, md []*charm.Markdown) (m []*markdown) {
for _, v := range md {
m = append(m, &markdown{
markdownType: t,
Markdown: *v,
})
}
return m
}
func relativeTime(then time.Time) string {
now := time.Now()
ago := now.Sub(then)
if ago < time.Minute {
return "just now"
} else if ago < humanize.Week {
return humanize.CustomRelTime(then, now, "ago", "from now", magnitudes)
}
return then.Format("02 Jan 2006 15:04 MST")
}
var magnitudes = []humanize.RelTimeMagnitude{
{D: time.Second, Format: "now", DivBy: time.Second},
{D: 2 * time.Second, Format: "1 second %s", DivBy: 1},
{D: time.Minute, Format: "%d seconds %s", DivBy: time.Second},
{D: 2 * time.Minute, Format: "1 minute %s", DivBy: 1},
{D: time.Hour, Format: "%d minutes %s", DivBy: time.Minute},
{D: 2 * time.Hour, Format: "1 hour %s", DivBy: 1},
{D: humanize.Day, Format: "%d hours %s", DivBy: time.Hour},
{D: 2 * humanize.Day, Format: "1 day %s", DivBy: 1},
{D: humanize.Week, Format: "%d days %s", DivBy: humanize.Day},
{D: 2 * humanize.Week, Format: "1 week %s", DivBy: 1},
{D: humanize.Month, Format: "%d weeks %s", DivBy: humanize.Week},
{D: 2 * humanize.Month, Format: "1 month %s", DivBy: 1},
{D: humanize.Year, Format: "%d months %s", DivBy: humanize.Month},
{D: 18 * humanize.Month, Format: "1 year %s", DivBy: 1},
{D: 2 * humanize.Year, Format: "2 years %s", DivBy: 1},
{D: humanize.LongTime, Format: "%d years %s", DivBy: humanize.Year},
{D: math.MaxInt64, Format: "a long while %s", DivBy: 1},
}

View file

@ -173,7 +173,7 @@ func (m *pagerModel) unload() {
m.textInput.Reset()
}
func (m pagerModel) Update(msg tea.Msg) (pagerModel, tea.Cmd) {
func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) {
var (
cmd tea.Cmd
cmds []tea.Cmd
@ -215,8 +215,8 @@ func (m pagerModel) Update(msg tea.Msg) (pagerModel, tea.Cmd) {
cmds = append(cmds, viewport.Sync(m.viewport))
}
case "m":
isStashed := m.currentDocument.markdownType == stashedMarkdown ||
m.currentDocument.markdownType == convertedMarkdown
isStashed := m.currentDocument.markdownType == StashedDocument ||
m.currentDocument.markdownType == ConvertedDocument
// Users can only set the note on user-stashed markdown
if !isStashed {
@ -244,7 +244,7 @@ func (m pagerModel) Update(msg tea.Msg) (pagerModel, tea.Cmd) {
}
// Stash a local document
if m.state != pagerStateStashing && m.currentDocument.markdownType == localMarkdown {
if m.state != pagerStateStashing && m.currentDocument.markdownType == LocalDocument {
m.state = pagerStateStashing
m.spinner.Start()
cmds = append(
@ -348,7 +348,7 @@ func (m pagerModel) statusBarView(b *strings.Builder) {
percentToStringMagnitude float64 = 100.0
)
var (
isStashed bool = m.currentDocument.markdownType == stashedMarkdown || m.currentDocument.markdownType == convertedMarkdown
isStashed bool = m.currentDocument.markdownType == StashedDocument || m.currentDocument.markdownType == ConvertedDocument
showStatusMessage bool = m.state == pagerStateStatusMessage
)
@ -440,7 +440,7 @@ func (m pagerModel) setNoteView(b *strings.Builder) {
func (m pagerModel) helpView() (s string) {
memoOrStash := "m set memo"
if m.general.authStatus == authOK && m.currentDocument.markdownType == localMarkdown {
if m.general.authStatus == authOK && m.currentDocument.markdownType == LocalDocument {
memoOrStash = "s stash this document"
}
@ -453,7 +453,7 @@ func (m pagerModel) helpView() (s string) {
"q quit",
}
if m.currentDocument.markdownType == newsMarkdown {
if m.currentDocument.markdownType == NewsDocument {
deleteFromStringSlice(col1, 3)
}

File diff suppressed because it is too large Load diff

View file

@ -23,18 +23,18 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) {
truncateTo = m.general.width - stashViewHorizontalPadding*2
gutter string
title = md.Note
date = relativeTime(md.CreatedAt)
date = md.relativeTime()
icon = ""
)
switch md.markdownType {
case newsMarkdown:
case NewsDocument:
if title == "" {
title = "News"
} else {
title = newsPrefix + truncate(title, truncateTo-rw.StringWidth(newsPrefix))
}
case stashedMarkdown, convertedMarkdown:
case StashedDocument, ConvertedDocument:
icon = fileListingStashIcon
if title == "" {
title = noMemoTitle
@ -81,7 +81,7 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) {
} else {
// Regular (non-selected) items
if md.markdownType == newsMarkdown {
if md.markdownType == NewsDocument {
gutter = " "
if isFilteringNotes && m.filterInput.Value() == "" {

View file

@ -16,6 +16,7 @@ import (
"github.com/charmbracelet/charm/keygen"
"github.com/charmbracelet/charm/ui/common"
"github.com/charmbracelet/glow/utils"
runewidth "github.com/mattn/go-runewidth"
"github.com/muesli/gitcha"
te "github.com/muesli/termenv"
)
@ -194,7 +195,7 @@ func newModel(cfg Config) tea.Model {
}
if cfg.DocumentTypes == 0 {
cfg.DocumentTypes = LocalDocuments | StashedDocuments | NewsDocuments
cfg.DocumentTypes = LocalDocument | StashedDocument | NewsDocument
}
general := general{
@ -213,15 +214,16 @@ func newModel(cfg Config) tea.Model {
func (m model) Init() tea.Cmd {
var cmds []tea.Cmd
d := &m.general.cfg.DocumentTypes
if m.general.cfg.DocumentTypes&StashedDocuments != 0 || m.general.cfg.DocumentTypes&NewsDocuments != 0 {
if *d&StashedDocument != 0 || *d&NewsDocument != 0 {
cmds = append(cmds,
newCharmClient,
spinner.Tick,
)
}
if m.general.cfg.DocumentTypes&LocalDocuments != 0 {
if *d&LocalDocument != 0 {
cmds = append(cmds, findLocalFiles(m))
}
@ -265,7 +267,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, tea.Quit
}
m.stash, cmd = stashUpdate(msg, m.stash)
m.stash, cmd = m.stash.update(msg)
return m, cmd
}
@ -275,7 +277,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// If setting a note send all keys straight through
case pagerStateSetNote:
var batch []tea.Cmd
newPagerModel, cmd := m.pager.Update(msg)
newPagerModel, cmd := m.pager.update(msg)
m.pager = newPagerModel
batch = append(batch, cmd)
return m, tea.Batch(batch...)
@ -337,7 +339,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
// Even though it failed, news/stash loading is finished
m.stash.loaded |= StashedDocuments | NewsDocuments
m.stash.loaded |= StashedDocument | NewsDocument
m.stash.loadingFromNetwork = false
}
@ -352,7 +354,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.keygenState = keygenFinished
// Even though it failed, news/stash loading is finished
m.stash.loaded |= StashedDocuments | NewsDocuments
m.stash.loaded |= StashedDocument | NewsDocument
m.stash.loadingFromNetwork = false
case keygenSuccessMsg:
@ -381,7 +383,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// A note was saved to a document. This will have been done in the
// pager, so we'll need to find the corresponding note in the stash.
// So, pass the message to the stash for processing.
stashModel, cmd := stashUpdate(msg, m.stash)
stashModel, cmd := m.stash.update(msg)
m.stash = stashModel
return m, cmd
@ -389,7 +391,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// Always pass these messages to the stash so we can keep it updated
// about network activity, even if the user isn't currently viewing
// the stash.
stashModel, cmd := stashUpdate(msg, m.stash)
stashModel, cmd := m.stash.update(msg)
m.stash = stashModel
return m, cmd
@ -417,12 +419,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// Process children
switch m.state {
case stateShowStash:
newStashModel, cmd := stashUpdate(msg, m.stash)
newStashModel, cmd := m.stash.update(msg)
m.stash = newStashModel
cmds = append(cmds, cmd)
case stateShowDocument:
newPagerModel, cmd := m.pager.Update(msg)
newPagerModel, cmd := m.pager.update(msg)
m.pager = newPagerModel
cmds = append(cmds, cmd)
}
@ -439,7 +441,7 @@ func (m model) View() string {
case stateShowDocument:
return m.pager.View()
default:
return stashView(m.stash)
return m.stash.view()
}
}
@ -637,7 +639,7 @@ func stashDocument(cc *charm.Client, md markdown) tea.Cmd {
}
// Turn local markdown into a newly stashed (converted) markdown
md.markdownType = convertedMarkdown
md.markdownType = ConvertedDocument
md.CreatedAt = time.Now()
// Set the note as the filename without the extension
@ -673,7 +675,7 @@ func waitForStatusMessageTimeout(appCtx applicationContext, t *time.Timer) tea.C
// already done that.
func localFileToMarkdown(cwd string, res gitcha.SearchResult) *markdown {
md := &markdown{
markdownType: localMarkdown,
markdownType: LocalDocument,
localPath: res.Path,
Markdown: charm.Markdown{
Note: stripAbsolutePath(res.Path, cwd),
@ -702,6 +704,10 @@ func indent(s string, n int) string {
return b.String()
}
func truncate(str string, num int) string {
return runewidth.Truncate(str, num, "…")
}
func min(a, b int) int {
if a < b {
return a