From 4e6460682ef31f9fc8d282c3d12048c373329b92 Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Tue, 17 Nov 2020 17:37:52 -0500 Subject: [PATCH] Consolidate common model properties to reduce duplication --- ui/ignore_darwin.go | 4 +-- ui/ignore_general.go | 2 +- ui/pager.go | 55 +++++++++++++---------------- ui/stash.go | 52 ++++++++++++--------------- ui/stashitem.go | 2 +- ui/ui.go | 83 ++++++++++++++++++++++---------------------- 6 files changed, 93 insertions(+), 105 deletions(-) diff --git a/ui/ignore_darwin.go b/ui/ignore_darwin.go index 51cdac3..6a0b4ef 100644 --- a/ui/ignore_darwin.go +++ b/ui/ignore_darwin.go @@ -6,8 +6,8 @@ import "path/filepath" func ignorePatterns(m model) []string { return []string{ - filepath.Join(m.cfg.HomeDir, "Library"), - m.cfg.Gopath, + filepath.Join(m.general.cfg.HomeDir, "Library"), + m.general.cfg.Gopath, "node_modules", ".*", } diff --git a/ui/ignore_general.go b/ui/ignore_general.go index 7f1da74..838cea7 100644 --- a/ui/ignore_general.go +++ b/ui/ignore_general.go @@ -4,7 +4,7 @@ package ui func ignorePatterns(m model) []string { return []string{ - m.cfg.Gopath, + m.general.cfg.Gopath, "node_modules", ".*", } diff --git a/ui/pager.go b/ui/pager.go index 0141b1c..bef151f 100644 --- a/ui/pager.go +++ b/ui/pager.go @@ -65,16 +65,12 @@ const ( ) type pagerModel struct { - cfg *Config - cc *charm.Client - authStatus authStatus - viewport viewport.Model - state pagerState - width int - height int - showHelp bool - textInput textinput.Model - spinner spinner.Model + general *general + viewport viewport.Model + state pagerState + showHelp bool + textInput textinput.Model + spinner spinner.Model statusMessage string statusMessageTimer *time.Timer @@ -88,7 +84,7 @@ type pagerModel struct { stashedDocument *markdown } -func newPagerModel(cfg *Config, as authStatus) pagerModel { +func newPagerModel(general *general) pagerModel { // Init viewport vp := viewport.Model{} vp.YPosition = 0 @@ -114,18 +110,15 @@ func newPagerModel(cfg *Config, as authStatus) pagerModel { sp.MinimumLifetime = time.Millisecond * 180 return pagerModel{ - cfg: cfg, - state: pagerStateBrowse, - authStatus: as, - textInput: ti, - viewport: vp, - spinner: sp, + general: general, + state: pagerStateBrowse, + textInput: ti, + viewport: vp, + spinner: sp, } } func (m *pagerModel) setSize(w, h int) { - m.width = w - m.height = h m.viewport.Width = w m.viewport.Height = h - statusBarHeight m.textInput.Width = w - @@ -146,7 +139,7 @@ func (m *pagerModel) setContent(s string) { func (m *pagerModel) toggleHelp() { m.showHelp = !m.showHelp - m.setSize(m.width, m.height) + m.setSize(m.general.width, m.general.height) if m.viewport.PastBottom() { m.viewport.GotoBottom() } @@ -198,7 +191,7 @@ func (m pagerModel) Update(msg tea.Msg) (pagerModel, tea.Cmd) { var cmd tea.Cmd if m.textInput.Value() != m.currentDocument.Note { // don't update if the note didn't change m.currentDocument.Note = m.textInput.Value() // update optimistically - cmd = saveDocumentNote(m.cc, m.currentDocument.ID, m.currentDocument.Note) + cmd = saveDocumentNote(m.general.cc, m.currentDocument.ID, m.currentDocument.Note) } m.state = pagerStateBrowse m.textInput.Reset() @@ -246,7 +239,7 @@ func (m pagerModel) Update(msg tea.Msg) (pagerModel, tea.Cmd) { return m, textinput.Blink case "s": - if m.authStatus != authOK { + if m.general.authStatus != authOK { break } @@ -256,7 +249,7 @@ func (m pagerModel) Update(msg tea.Msg) (pagerModel, tea.Cmd) { m.spinner.Start() cmds = append( cmds, - stashDocument(m.cc, m.currentDocument), + stashDocument(m.general.cc, m.currentDocument), spinner.Tick, ) } @@ -402,7 +395,7 @@ func (m pagerModel) statusBarView(b *strings.Builder) { } } note = truncate(" "+note+" ", max(0, - m.width- + m.general.width- ansi.PrintableRuneWidth(logo)- ansi.PrintableRuneWidth(statusIndicator)- ansi.PrintableRuneWidth(scrollPercent)- @@ -416,7 +409,7 @@ func (m pagerModel) statusBarView(b *strings.Builder) { // Empty space padding := max(0, - m.width- + m.general.width- ansi.PrintableRuneWidth(logo)- ansi.PrintableRuneWidth(statusIndicator)- ansi.PrintableRuneWidth(note)- @@ -447,7 +440,7 @@ func (m pagerModel) setNoteView(b *strings.Builder) { func (m pagerModel) helpView() (s string) { memoOrStash := "m set memo" - if m.authStatus == authOK && m.currentDocument.markdownType == localMarkdown { + if m.general.authStatus == authOK && m.currentDocument.markdownType == localMarkdown { memoOrStash = "s stash this document" } @@ -479,11 +472,11 @@ func (m pagerModel) helpView() (s string) { s = indent(s, 2) // Fill up empty cells with spaces for background coloring - if m.width > 0 { + if m.general.width > 0 { lines := strings.Split(s, "\n") for i := 0; i < len(lines); i++ { l := runewidth.StringWidth(lines[i]) - n := max(m.width-l, 0) + n := max(m.general.width-l, 0) lines[i] += strings.Repeat(" ", n) } @@ -516,13 +509,13 @@ func glamourRender(m pagerModel, markdown string) (string, error) { // initialize glamour var gs glamour.TermRendererOption - if m.cfg.GlamourStyle == "auto" { + if m.general.cfg.GlamourStyle == "auto" { gs = glamour.WithAutoStyle() } else { - gs = glamour.WithStylePath(m.cfg.GlamourStyle) + gs = glamour.WithStylePath(m.general.cfg.GlamourStyle) } - width := max(0, min(int(m.cfg.GlamourMaxWidth), m.viewport.Width)) + width := max(0, min(int(m.general.cfg.GlamourMaxWidth), m.viewport.Width)) r, err := glamour.NewTermRenderer( gs, glamour.WithWordWrap(width), diff --git a/ui/stash.go b/ui/stash.go index 3f7c615..724d550 100644 --- a/ui/stash.go +++ b/ui/stash.go @@ -69,18 +69,13 @@ const ( ) type stashModel struct { - cc *charm.Client - cfg *Config - cwd string - authStatus authStatus + general *general state stashState err error markdowns []*markdown spinner spinner.Model noteInput textinput.Model filterInput textinput.Model - terminalWidth int - terminalHeight int stashFullyLoaded bool // have we loaded all available stashed documents from the server? loadingFromNetwork bool // are we currently loading something from the network? loaded DocumentType // load status for news, stash and local files loading; we find out exactly with bitmasking @@ -110,33 +105,33 @@ type stashModel struct { } func (m stashModel) localOnly() bool { - return m.cfg.DocumentTypes == LocalDocuments + return m.general.cfg.DocumentTypes == LocalDocuments } func (m stashModel) stashedOnly() bool { - return m.cfg.DocumentTypes&LocalDocuments == 0 + return m.general.cfg.DocumentTypes&LocalDocuments == 0 } func (m stashModel) loadingDone() bool { // Do the types loaded match the types we want to have? - return m.loaded == m.cfg.DocumentTypes + return m.loaded == m.general.cfg.DocumentTypes } func (m *stashModel) setSize(width, height int) { - m.terminalWidth = width - m.terminalHeight = height + m.general.width = width + m.general.height = height // Update the paginator m.setTotalPages() - m.noteInput.Width = m.terminalWidth - stashViewHorizontalPadding*2 - ansi.PrintableRuneWidth(m.noteInput.Prompt) - m.filterInput.Width = m.terminalWidth - stashViewHorizontalPadding*2 - ansi.PrintableRuneWidth(m.filterInput.Prompt) + m.noteInput.Width = width - stashViewHorizontalPadding*2 - ansi.PrintableRuneWidth(m.noteInput.Prompt) + m.filterInput.Width = width - stashViewHorizontalPadding*2 - ansi.PrintableRuneWidth(m.filterInput.Prompt) } // Sets the total paginator pages according to the amount of markdowns for the // current state. func (m *stashModel) setTotalPages() { - m.paginator.PerPage = max(1, (m.terminalHeight-stashViewTopPadding-stashViewBottomPadding)/stashViewItemHeight) + m.paginator.PerPage = max(1, (m.general.height-stashViewTopPadding-stashViewBottomPadding)/stashViewItemHeight) if pages := len(m.getNotes()); pages < 1 { m.paginator.SetTotalPages(1) @@ -298,7 +293,7 @@ func (m *stashModel) moveCursorDown() { // INIT -func newStashModel(cfg *Config, as authStatus) stashModel { +func newStashModel(general *general) stashModel { sp := spinner.NewModel() sp.Spinner = spinner.Line sp.ForegroundColor = common.SpinnerColor.String() @@ -323,8 +318,7 @@ func newStashModel(cfg *Config, as authStatus) stashModel { si.Focus() m := stashModel{ - cfg: cfg, - authStatus: as, + general: general, spinner: sp, noteInput: ni, filterInput: si, @@ -475,7 +469,7 @@ func stashUpdate(msg tea.Msg, m stashModel) (stashModel, tea.Cmd) { if md.markdownType == localMarkdown { cmds = append(cmds, loadLocalMarkdown(md)) } else { - cmds = append(cmds, loadRemoteMarkdown(m.cc, md.ID, md.markdownType)) + cmds = append(cmds, loadRemoteMarkdown(m.general.cc, md.ID, md.markdownType)) } cmds = append(cmds, spinner.Tick) @@ -530,7 +524,7 @@ func stashUpdate(msg tea.Msg, m stashModel) (stashModel, tea.Cmd) { // Stash case "s": - if pages == 0 || m.authStatus != authOK || m.selectedMarkdown() == nil { + if pages == 0 || m.general.authStatus != authOK || m.selectedMarkdown() == nil { break } @@ -553,7 +547,7 @@ func stashUpdate(msg tea.Msg, m stashModel) (stashModel, tea.Cmd) { // Checks passed; perform the stash m.filesStashing[md.localPath] = struct{}{} - cmds = append(cmds, stashDocument(m.cc, *md)) + cmds = append(cmds, stashDocument(m.general.cc, *md)) if m.loadingDone() && !m.spinner.Visible() { m.spinner.Start() @@ -635,7 +629,7 @@ func stashUpdate(msg tea.Msg, m stashModel) (stashModel, tea.Cmd) { // If document was stashed during this session, convert it // back to a local file. md.markdownType = localMarkdown - md.Note = stripAbsolutePath(m.markdowns[i].localPath, m.cwd) + md.Note = stripAbsolutePath(m.markdowns[i].localPath, m.general.cwd) } else { // Delete optimistically and remove the stashed item // before we've received a success response. @@ -651,7 +645,7 @@ func stashUpdate(msg tea.Msg, m stashModel) (stashModel, tea.Cmd) { // Update pagination m.setTotalPages() - return m, deleteStashedItem(m.cc, smd.ID) + return m, deleteStashedItem(m.general.cc, smd.ID) default: m.state = stashStateReady @@ -729,7 +723,7 @@ func stashUpdate(msg tea.Msg, m stashModel) (stashModel, tea.Cmd) { // Set new note md := m.selectedMarkdown() newNote := m.noteInput.Value() - cmd := saveDocumentNote(m.cc, md.ID, newNote) + cmd := saveDocumentNote(m.general.cc, md.ID, newNote) md.Note = newNote m.noteInput.Reset() m.state = stashStateReady @@ -775,7 +769,7 @@ func stashView(m stashModel) string { // We need to fill any empty height with newlines so the footer reaches // the bottom. - numBlankLines := max(0, (m.terminalHeight-stashViewTopPadding-stashViewBottomPadding)%stashViewItemHeight) + numBlankLines := max(0, (m.general.height-stashViewTopPadding-stashViewBottomPadding)%stashViewItemHeight) blankLines := "" if numBlankLines > 0 { blankLines = strings.Repeat("\n", numBlankLines) @@ -812,7 +806,7 @@ func stashView(m stashModel) string { // If the dot pagination is wider than the width of the window // switch to the arabic paginator. - if ansi.PrintableRuneWidth(pagination) > m.terminalWidth-stashViewHorizontalPadding { + if ansi.PrintableRuneWidth(pagination) > m.general.width-stashViewHorizontalPadding { m.paginator.Type = paginator.Arabic pagination = common.Subtle(m.paginator.View()) } @@ -848,12 +842,12 @@ func stashHeaderView(m stashModel) string { loading := !m.loadingDone() noMarkdowns := len(m.markdowns) == 0 - if m.authStatus == authFailed && m.stashedOnly() { + if m.general.authStatus == authFailed && m.stashedOnly() { return common.Subtle("Can’t load stash. Are you offline?") } var maybeOffline string - if m.authStatus == authFailed { + if m.general.authStatus == authFailed { maybeOffline = " " + offlineHeaderNote } @@ -976,7 +970,7 @@ func stashHelpView(m stashModel) string { } if isStashed { h = append(h, "x: delete", "m: set memo") - } else if isLocal && m.authStatus == authOK { + } else if isLocal && m.general.authStatus == authOK { h = append(h, "s: stash") } if m.err != nil { @@ -985,7 +979,7 @@ func stashHelpView(m stashModel) string { h = append(h, "/: filter") h = append(h, "q: quit") } - return stashHelpViewBuilder(m.terminalWidth, h...) + return stashHelpViewBuilder(m.general.width, h...) } // builds the help view from various sections pieces, truncating it if the view diff --git a/ui/stashitem.go b/ui/stashitem.go index f74ed17..8c26fee 100644 --- a/ui/stashitem.go +++ b/ui/stashitem.go @@ -20,7 +20,7 @@ const ( func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { var ( - truncateTo = m.terminalWidth - stashViewHorizontalPadding*2 + truncateTo = m.general.width - stashViewHorizontalPadding*2 gutter string title = md.Note date = relativeTime(md.CreatedAt) diff --git a/ui/ui.go b/ui/ui.go index b8a83c8..683dff2 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -134,18 +134,25 @@ const ( keygenFinished ) +// General stuff we'll need to access in all models +type general struct { + cfg Config + cc *charm.Client + cwd string + authStatus authStatus + width int + height int +} + type model struct { - cfg *Config - cc *charm.Client - authStatus authStatus - keygenState keygenState - state state - fatalErr error - stash stashModel - pager pagerModel - terminalWidth int - terminalHeight int - cwd string // directory from which we're running Glow + general *general + keygenState keygenState + state state + fatalErr error + + // Sub-models + stash stashModel + pager pagerModel // Channel that receives paths to local markdown files // (via the github.com/muesli/gitcha package) @@ -176,12 +183,6 @@ func (m *model) unloadDocument() []tea.Cmd { return batch } -func (m *model) setAuthStatus(as authStatus) { - m.authStatus = as - m.stash.authStatus = as - m.pager.authStatus = as -} - func newModel(cfg Config) tea.Model { if cfg.GlamourStyle == "auto" { dbg := te.HasDarkBackground() @@ -196,28 +197,31 @@ func newModel(cfg Config) tea.Model { cfg.DocumentTypes = LocalDocuments | StashedDocuments | NewsDocuments } - as := authConnecting + general := general{ + cfg: cfg, + authStatus: authConnecting, + } + return model{ - cfg: &cfg, + general: &general, state: stateShowStash, - authStatus: as, keygenState: keygenUnstarted, - pager: newPagerModel(&cfg, as), - stash: newStashModel(&cfg, as), + pager: newPagerModel(&general), + stash: newStashModel(&general), } } func (m model) Init() tea.Cmd { var cmds []tea.Cmd - if m.cfg.DocumentTypes&StashedDocuments != 0 || m.cfg.DocumentTypes&NewsDocuments != 0 { + if m.general.cfg.DocumentTypes&StashedDocuments != 0 || m.general.cfg.DocumentTypes&NewsDocuments != 0 { cmds = append(cmds, newCharmClient, spinner.Tick, ) } - if m.cfg.DocumentTypes&LocalDocuments != 0 { + if m.general.cfg.DocumentTypes&LocalDocuments != 0 { cmds = append(cmds, findLocalFiles(m)) } @@ -306,8 +310,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Window size is received when starting up and on every resize case tea.WindowSizeMsg: - m.terminalWidth = msg.Width - m.terminalHeight = msg.Height + m.general.width = msg.Width + m.general.height = msg.Height m.stash.setSize(msg.Width, msg.Height) m.pager.setSize(msg.Width, msg.Height) @@ -316,12 +320,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case initLocalFileSearchMsg: m.localFileFinder = msg.ch - m.cwd = msg.cwd - m.stash.cwd = msg.cwd + m.general.cwd = msg.cwd cmds = append(cmds, findNextLocalFile(m)) case foundLocalFileMsg: - newMd := localFileToMarkdown(m.cwd, gitcha.SearchResult(msg)) + newMd := localFileToMarkdown(m.general.cwd, gitcha.SearchResult(msg)) m.stash.addMarkdowns(newMd) cmds = append(cmds, findNextLocalFile(m)) @@ -331,7 +334,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmds = append(cmds, generateSSHKeys) } else { // The keygen ran but things still didn't work and we can't auth - m.setAuthStatus(authFailed) + m.general.authStatus = authFailed m.stash.err = errors.New("SSH authentication failed; we tried ssh-agent, loading keys from disk, and generating SSH keys") if debug { log.Println("entering offline mode;", m.stash.err) @@ -344,7 +347,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case keygenFailedMsg: // Keygen failed. That sucks. - m.setAuthStatus(authFailed) + m.general.authStatus = authFailed m.stash.err = errors.New("could not authenticate; could not generate SSH keys") if debug { log.Println("entering offline mode;", m.stash.err) @@ -362,14 +365,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmds = append(cmds, newCharmClient) case newCharmClientMsg: - m.cc = msg - m.setAuthStatus(authOK) - m.stash.cc = msg - m.pager.cc = msg + m.general.cc = msg + m.general.authStatus = authOK cmds = append(cmds, loadStash(m.stash), loadNews(m.stash)) case stashLoadErrMsg: - m.setAuthStatus(authFailed) + m.general.authStatus = authFailed case fetchedMarkdownMsg: m.pager.currentDocument = *msg @@ -465,7 +466,7 @@ func findLocalFiles(m model) tea.Cmd { } var ignore []string - if !m.cfg.ShowAllFiles { + if !m.general.cfg.ShowAllFiles { ignore = ignorePatterns(m) } @@ -521,14 +522,14 @@ func newCharmClient() tea.Msg { func loadStash(m stashModel) tea.Cmd { return func() tea.Msg { - if m.cc == nil { + if m.general.cc == nil { err := errors.New("no charm client") if debug { log.Println("error loading stash:", err) } return stashLoadErrMsg{err} } - stash, err := m.cc.GetStash(m.page) + stash, err := m.general.cc.GetStash(m.page) if err != nil { if debug { if _, ok := err.(charm.ErrAuthFailed); ok { @@ -545,14 +546,14 @@ func loadStash(m stashModel) tea.Cmd { func loadNews(m stashModel) tea.Cmd { return func() tea.Msg { - if m.cc == nil { + if m.general.cc == nil { err := errors.New("no charm client") if debug { log.Println("error loading news:", err) } return newsLoadErrMsg{err} } - news, err := m.cc.GetNews(1) // just fetch the first page + news, err := m.general.cc.GetNews(1) // just fetch the first page if err != nil { if debug { log.Println("error loading news:", err)