diff --git a/internal/app/app.go b/internal/app/app.go index 2672a06..a14866b 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -1,11 +1,15 @@ package app import ( + "sync" + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/table" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/hedhyw/json-log-viewer/internal/keymap" + "github.com/hedhyw/json-log-viewer/internal/pkg/events" "github.com/hedhyw/json-log-viewer/internal/pkg/source" "github.com/hedhyw/json-log-viewer/internal/pkg/config" @@ -13,14 +17,16 @@ import ( // Application global state. type Application struct { + lock *sync.Mutex + FileName string Config *config.Config BaseStyle lipgloss.Style FooterStyle lipgloss.Style - LastWindowSize tea.WindowSizeMsg - Entries source.LazyLogEntries + lastWindowSize tea.WindowSizeMsg + entries source.LazyLogEntries Version string keys keymap.KeyMap @@ -38,13 +44,15 @@ func newApplication( ) return Application{ + lock: &sync.Mutex{}, + FileName: fileName, Config: config, BaseStyle: getBaseStyle(), FooterStyle: getFooterStyle(), - LastWindowSize: tea.WindowSizeMsg{ + lastWindowSize: tea.WindowSizeMsg{ Width: initialWidth, Height: initialHeight, }, @@ -66,3 +74,51 @@ func NewModel( return newStateInitial(&application) } + +func (app *Application) getLogLevelStyle( + renderedRows []table.Row, + baseStyle lipgloss.Style, + rowID int, +) lipgloss.Style { + if rowID < 0 || rowID >= len(renderedRows) { + return baseStyle + } + + row := renderedRows[rowID] + + color := getColorForLogLevel(app.getLogLevelFromLogRow(row)) + if color == "" { + return baseStyle + } + + return baseStyle.Foreground(color) +} + +// Update application state. +func (app *Application) Update(msg tea.Msg) { + app.lock.Lock() + defer app.lock.Unlock() + + switch msg := msg.(type) { + case tea.WindowSizeMsg: + app.lastWindowSize = msg + case events.LogEntriesUpdateMsg: + app.entries = source.LazyLogEntries(msg) + } +} + +// Entries getter. +func (app *Application) Entries() source.LazyLogEntries { + app.lock.Lock() + defer app.lock.Unlock() + + return app.entries +} + +// LastWindowSize getter. +func (app *Application) LastWindowSize() tea.WindowSizeMsg { + app.lock.Lock() + defer app.lock.Unlock() + + return app.lastWindowSize +} diff --git a/internal/app/helper.go b/internal/app/helper.go index 88f7320..7bd7847 100644 --- a/internal/app/helper.go +++ b/internal/app/helper.go @@ -14,35 +14,6 @@ import ( "github.com/hedhyw/json-log-viewer/internal/pkg/source" ) -func (app *Application) getLogLevelStyle( - renderedRows []table.Row, - baseStyle lipgloss.Style, - rowID int, -) lipgloss.Style { - if rowID < 0 || rowID >= len(renderedRows) { - return baseStyle - } - - row := renderedRows[rowID] - - color := getColorForLogLevel(app.getLogLevelFromLogRow(row)) - if color == "" { - return baseStyle - } - - return baseStyle.Foreground(color) -} - -// Update application state. -func (app *Application) Update(msg tea.Msg) { - switch msg := msg.(type) { - case tea.WindowSizeMsg: - app.LastWindowSize = msg - case events.LogEntriesUpdateMsg: - app.Entries = source.LazyLogEntries(msg) - } -} - func getColorForLogLevel(level source.Level) lipgloss.Color { switch level { case source.LevelTrace: diff --git a/internal/app/lazytable.go b/internal/app/lazytable.go index 6a86ed3..2d3c9c4 100644 --- a/internal/app/lazytable.go +++ b/internal/app/lazytable.go @@ -119,9 +119,6 @@ func (m lazyTableModel) handleKey(msg tea.KeyMsg, render bool) (lazyTableModel, if offset != m.offset { m.offset = offset render = true - } else { - // we were at the first item, so we should follow the log - m.follow = true } } diff --git a/internal/app/logstable.go b/internal/app/logstable.go index ca53b92..8145ac7 100644 --- a/internal/app/logstable.go +++ b/internal/app/logstable.go @@ -19,11 +19,16 @@ type logsTableModel struct { logEntries source.LazyLogEntries } -func newLogsTableModel(application *Application, logEntries source.LazyLogEntries) logsTableModel { +func newLogsTableModel( + application *Application, + logEntries source.LazyLogEntries, + follow bool, + reverse bool, +) logsTableModel { tableLogs := table.New( - table.WithColumns(getColumns(application.LastWindowSize.Width, application.Config)), + table.WithColumns(getColumns(application.LastWindowSize().Width, application.Config)), table.WithFocused(true), - table.WithHeight(application.LastWindowSize.Height), + table.WithHeight(application.LastWindowSize().Height), ) tableLogs.KeyMap.LineUp = application.keys.Up tableLogs.KeyMap.LineDown = application.keys.Down @@ -34,8 +39,8 @@ func newLogsTableModel(application *Application, logEntries source.LazyLogEntrie lazyTable := lazyTableModel{ Application: application, - reverse: true, - follow: true, + reverse: reverse, + follow: follow, table: tableLogs, entries: logEntries, lastCursor: 0, @@ -47,7 +52,7 @@ func newLogsTableModel(application *Application, logEntries source.LazyLogEntrie lazyTable: lazyTable, logEntries: logEntries, footerSize: 1, - }.handleWindowSizeMsg(application.LastWindowSize) + }.handleWindowSizeMsg(application.LastWindowSize()) return msg } diff --git a/internal/app/statefiltered.go b/internal/app/statefiltered.go index 52c6611..efbac3e 100644 --- a/internal/app/statefiltered.go +++ b/internal/app/statefiltered.go @@ -60,8 +60,8 @@ func (s StateFilteredModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { s, msg = s.handleStateFilteredModel() } - if _, ok := msg.(*events.LogEntriesUpdateMsg); ok { - s, msg = s.handleLogEntriesUpdateMsg() + if _, ok := msg.(events.LogEntriesUpdateMsg); ok { + return s, nil } switch typedMsg := msg.(type) { @@ -73,8 +73,6 @@ func (s StateFilteredModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if mdl, cmd := s.handleKeyMsg(typedMsg); mdl != nil { return mdl, cmd } - default: - s.table, cmdBatch = batched(s.table.Update(typedMsg))(cmdBatch) } s.table, cmdBatch = batched(s.table.Update(msg))(cmdBatch) @@ -95,27 +93,21 @@ func (s StateFilteredModel) handleKeyMsg(msg tea.KeyMsg) (tea.Model, tea.Cmd) { } } -func (s StateFilteredModel) handleLogEntriesUpdateMsg() (StateFilteredModel, tea.Msg) { - entries, err := s.Application.Entries.Filter(s.filterText) - if err != nil { - return s, events.ShowError(err)() - } - - s.logEntries = entries - - return s, events.LogEntriesUpdateMsg(entries) -} - func (s StateFilteredModel) handleStateFilteredModel() (StateFilteredModel, tea.Msg) { - entries, err := s.Application.Entries.Filter(s.filterText) + entries, err := s.Application.Entries().Filter(s.filterText) if err != nil { return s, events.ShowError(err)() } s.logEntries = entries - s.table = newLogsTableModel(s.Application, entries) + s.table = newLogsTableModel( + s.Application, + entries, + false, // follow. + s.previousState.table.lazyTable.reverse, + ) - return s, events.LogEntriesUpdateMsg(entries) + return s, nil } func (s StateFilteredModel) handleFilterKeyClickedMsg() (tea.Model, tea.Cmd) { @@ -136,7 +128,7 @@ func (s StateFilteredModel) getApplication() *Application { } func (s StateFilteredModel) refresh() (_ stateModel, cmd tea.Cmd) { - s.table, cmd = s.table.Update(s.Application.LastWindowSize) + s.table, cmd = s.table.Update(s.Application.LastWindowSize()) return s, cmd } diff --git a/internal/app/statefiltering.go b/internal/app/statefiltering.go index b5fd0c0..adcc1f5 100644 --- a/internal/app/statefiltering.go +++ b/internal/app/statefiltering.go @@ -71,7 +71,7 @@ func (s StateFilteringModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (s StateFilteringModel) handleKeyMsg(msg tea.KeyMsg) (tea.Model, tea.Cmd) { switch { - case key.Matches(msg, s.keys.Back): + case key.Matches(msg, s.keys.Back) && string(msg.Runes) != "q": return s.previousState.refresh() case key.Matches(msg, s.keys.Open): return s.handleEnterKeyClickedMsg() diff --git a/internal/app/stateloaded.go b/internal/app/stateloaded.go index 9ebf4b9..664c9be 100644 --- a/internal/app/stateloaded.go +++ b/internal/app/stateloaded.go @@ -23,7 +23,12 @@ func newStateViewLogs( application *Application, logEntries source.LazyLogEntries, ) StateLoadedModel { - table := newLogsTableModel(application, logEntries) + table := newLogsTableModel( + application, + logEntries, + true, // follow. + true, // reverse. + ) return StateLoadedModel{ Application: application, @@ -77,7 +82,7 @@ func (s StateLoadedModel) viewHelp() string { Padding(0, 1). Render(s.Version) - width := s.Application.LastWindowSize.Width + width := s.Application.LastWindowSize().Width fillerText := lipgloss.NewStyle(). Background(lipgloss.Color("#353533")). Width(width - lipgloss.Width(toggleText) - lipgloss.Width(versionText)). @@ -141,7 +146,7 @@ func (s StateLoadedModel) handleKeyMsg(msg tea.KeyMsg) []tea.Cmd { } func (s StateLoadedModel) handleRequestOpenJSON() (tea.Model, tea.Cmd) { - return s, events.OpenJSONRowRequested(s.Entries, s.table.Cursor()) + return s, events.OpenJSONRowRequested(s.entries, s.table.Cursor()) } func (s StateLoadedModel) handleFilterKeyClickedMsg() (tea.Model, tea.Cmd) { @@ -153,9 +158,12 @@ func (s StateLoadedModel) getApplication() *Application { } func (s StateLoadedModel) refresh() (_ stateModel, cmd tea.Cmd) { - s.table, cmd = s.table.Update(s.Application.LastWindowSize) + var cmdFirst, cmdSecond tea.Cmd - return s, cmd + s.table, cmdSecond = s.table.Update(s.Application.LastWindowSize()) + s.table, cmdFirst = s.table.Update(events.LogEntriesUpdateMsg(s.Application.Entries())) + + return s, tea.Batch(cmdFirst, cmdSecond) } // String implements fmt.Stringer. diff --git a/internal/app/stateviewrow.go b/internal/app/stateviewrow.go index 941e595..e72ab53 100644 --- a/internal/app/stateviewrow.go +++ b/internal/app/stateviewrow.go @@ -30,7 +30,7 @@ func newStateViewRow( jsonViewModel, cmd := widgets.NewJSONViewModel( logEntry.Line, - app.LastWindowSize, + app.LastWindowSize(), app.keys, )