From 4c187ebb4db58f82ed413f3e4e07df04a8504b3d Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Tue, 23 Aug 2022 22:32:27 +1000 Subject: [PATCH] issue-8: added going forward in backstack and restoring last view --- cmd/dynamo-browse/main.go | 2 +- .../dynamo-browse/controllers/tableread.go | 36 +++++++- .../controllers/tableread_test.go | 16 ++-- .../controllers/tablewrite_test.go | 34 +++---- .../models/serialisable/viewsnapshot.go | 1 + .../workspacestore/resultsetsnapshot.go | 66 +++++++++++++ .../services/workspaces/iface.go | 4 + .../services/workspaces/service.go | 92 ++++++++++++++----- internal/dynamo-browse/ui/model.go | 3 + 9 files changed, 204 insertions(+), 50 deletions(-) diff --git a/cmd/dynamo-browse/main.go b/cmd/dynamo-browse/main.go index 1f0f091..cafbe4f 100644 --- a/cmd/dynamo-browse/main.go +++ b/cmd/dynamo-browse/main.go @@ -77,7 +77,7 @@ func main() { itemRendererService := itemrenderer.NewService(uiStyles.ItemView.FieldType, uiStyles.ItemView.MetaInfo) state := controllers.NewState() - tableReadController := controllers.NewTableReadController(state, tableService, workspaceService, itemRendererService, *flagTable) + tableReadController := controllers.NewTableReadController(state, tableService, workspaceService, itemRendererService, *flagTable, true) tableWriteController := controllers.NewTableWriteController(state, tableService, tableReadController) commandController := commandctrl.NewCommandController() diff --git a/internal/dynamo-browse/controllers/tableread.go b/internal/dynamo-browse/controllers/tableread.go index 0a4bac0..c4d4be0 100644 --- a/internal/dynamo-browse/controllers/tableread.go +++ b/internal/dynamo-browse/controllers/tableread.go @@ -8,6 +8,7 @@ import ( "github.com/lmika/audax/internal/common/ui/events" "github.com/lmika/audax/internal/dynamo-browse/models" "github.com/lmika/audax/internal/dynamo-browse/models/queryexpr" + "github.com/lmika/audax/internal/dynamo-browse/models/serialisable" "github.com/lmika/audax/internal/dynamo-browse/services/itemrenderer" "github.com/lmika/audax/internal/dynamo-browse/services/workspaces" "github.com/pkg/errors" @@ -23,6 +24,7 @@ type TableReadController struct { workspaceService *workspaces.ViewSnapshotService itemRendererService *itemrenderer.Service tableName string + loadFromLastView bool // state mutex *sync.Mutex @@ -36,6 +38,7 @@ func NewTableReadController( workspaceService *workspaces.ViewSnapshotService, itemRendererService *itemrenderer.Service, tableName string, + loadFromLastView bool, ) *TableReadController { return &TableReadController{ state: state, @@ -49,6 +52,13 @@ func NewTableReadController( // Init does an initial scan of the table. If no table is specified, it prompts for a table, then does a scan. func (c *TableReadController) Init() tea.Msg { + // Restore previous view + if c.loadFromLastView { + if vs, err := c.workspaceService.ViewRestore(); err == nil && vs != nil { + return c.updateViewToSnapshot(vs) + } + } + if c.tableName == "" { return c.ListTables() } else { @@ -234,15 +244,39 @@ func (c *TableReadController) Filter() tea.Msg { } func (c *TableReadController) ViewBack() tea.Msg { - viewSnapshot, err := c.workspaceService.PopSnapshot() + viewSnapshot, err := c.workspaceService.ViewBack() if err != nil { return events.Error(err) } else if viewSnapshot == nil { return events.StatusMsg("Backstack is empty") } + return c.updateViewToSnapshot(viewSnapshot) +} + +func (c *TableReadController) ViewForward() tea.Msg { + viewSnapshot, err := c.workspaceService.ViewForward() + if err != nil { + return events.Error(err) + } else if viewSnapshot == nil { + return events.StatusMsg("At top of view stack") + } + + return c.updateViewToSnapshot(viewSnapshot) +} + +func (c *TableReadController) updateViewToSnapshot(viewSnapshot *serialisable.ViewSnapshot) tea.Msg { + var err error currentResultSet := c.state.ResultSet() + if currentResultSet == nil { + tableInfo, err := c.tableService.Describe(context.Background(), viewSnapshot.TableName) + if err != nil { + return events.Error(err) + } + return c.runQuery(tableInfo, viewSnapshot.Query, viewSnapshot.Filter, false) + } + var currentQueryExpr string if currentResultSet.Query != nil { currentQueryExpr = currentResultSet.Query.String() diff --git a/internal/dynamo-browse/controllers/tableread_test.go b/internal/dynamo-browse/controllers/tableread_test.go index 5290118..cc5ffd9 100644 --- a/internal/dynamo-browse/controllers/tableread_test.go +++ b/internal/dynamo-browse/controllers/tableread_test.go @@ -29,7 +29,7 @@ func TestTableReadController_InitTable(t *testing.T) { service := tables.NewService(provider) t.Run("should prompt for table if no table name provided", func(t *testing.T) { - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "", false) event := readController.Init() @@ -37,7 +37,7 @@ func TestTableReadController_InitTable(t *testing.T) { }) t.Run("should scan table if table name provided", func(t *testing.T) { - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "", false) event := readController.Init() @@ -54,7 +54,7 @@ func TestTableReadController_ListTables(t *testing.T) { provider := dynamo.NewProvider(client) service := tables.NewService(provider) - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "", false) t.Run("returns a list of tables", func(t *testing.T) { event := readController.ListTables().(controllers.PromptForTableMsg) @@ -80,7 +80,7 @@ func TestTableReadController_Rescan(t *testing.T) { provider := dynamo.NewProvider(client) service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "bravo-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "bravo-table", false) t.Run("should perform a rescan", func(t *testing.T) { invokeCommand(t, readController.Init()) @@ -117,7 +117,7 @@ func TestTableReadController_ExportCSV(t *testing.T) { provider := dynamo.NewProvider(client) service := tables.NewService(provider) - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "bravo-table") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "bravo-table", false) t.Run("should export result set to CSV file", func(t *testing.T) { tempFile := tempFile(t) @@ -138,7 +138,7 @@ func TestTableReadController_ExportCSV(t *testing.T) { t.Run("should return error if result set is not set", func(t *testing.T) { tempFile := tempFile(t) - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "non-existant-table") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "non-existant-table", false) invokeCommandExpectingError(t, readController.Init()) invokeCommandExpectingError(t, readController.ExportCSV(tempFile)) @@ -156,7 +156,7 @@ func TestTableReadController_Query(t *testing.T) { provider := dynamo.NewProvider(client) service := tables.NewService(provider) - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "bravo-table") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "bravo-table", false) t.Run("should run scan with filter based on user query", func(t *testing.T) { tempFile := tempFile(t) @@ -176,7 +176,7 @@ func TestTableReadController_Query(t *testing.T) { t.Run("should return error if result set is not set", func(t *testing.T) { tempFile := tempFile(t) - readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "non-existant-table") + readController := controllers.NewTableReadController(controllers.NewState(), service, workspaceService, itemRendererService, "non-existant-table", false) invokeCommandExpectingError(t, readController.Init()) invokeCommandExpectingError(t, readController.ExportCSV(tempFile)) diff --git a/internal/dynamo-browse/controllers/tablewrite_test.go b/internal/dynamo-browse/controllers/tablewrite_test.go index e457a84..9118ea8 100644 --- a/internal/dynamo-browse/controllers/tablewrite_test.go +++ b/internal/dynamo-browse/controllers/tablewrite_test.go @@ -27,7 +27,7 @@ func TestTableWriteController_NewItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -91,7 +91,7 @@ func TestTableWriteController_SetAttributeValue(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -111,7 +111,7 @@ func TestTableWriteController_SetAttributeValue(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -165,7 +165,7 @@ func TestTableWriteController_SetAttributeValue(t *testing.T) { for _, scenario := range scenarios { t.Run(fmt.Sprintf("should change the value of a field to type %v", scenario.attrType), func(t *testing.T) { state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -186,7 +186,7 @@ func TestTableWriteController_SetAttributeValue(t *testing.T) { t.Run(fmt.Sprintf("should change value of nested field to type %v", scenario.attrType), func(t *testing.T) { state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -225,7 +225,7 @@ func TestTableWriteController_DeleteAttribute(t *testing.T) { t.Run("should delete top level attribute", func(t *testing.T) { state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -241,7 +241,7 @@ func TestTableWriteController_DeleteAttribute(t *testing.T) { t.Run("should delete attribute of map", func(t *testing.T) { state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -273,7 +273,7 @@ func TestTableWriteController_PutItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) // Read the table @@ -300,7 +300,7 @@ func TestTableWriteController_PutItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) // Read the table @@ -331,7 +331,7 @@ func TestTableWriteController_PutItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) // Read the table @@ -356,7 +356,7 @@ func TestTableWriteController_PutItems(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -384,7 +384,7 @@ func TestTableWriteController_PutItems(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -420,7 +420,7 @@ func TestTableWriteController_PutItems(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) invokeCommand(t, readController.Init()) @@ -462,7 +462,7 @@ func TestTableWriteController_TouchItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) // Read the table @@ -488,7 +488,7 @@ func TestTableWriteController_TouchItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) // Read the table @@ -515,7 +515,7 @@ func TestTableWriteController_NoisyTouchItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) // Read the table @@ -541,7 +541,7 @@ func TestTableWriteController_NoisyTouchItem(t *testing.T) { service := tables.NewService(provider) state := controllers.NewState() - readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table") + readController := controllers.NewTableReadController(state, service, workspaceService, itemRendererService, "alpha-table", false) writeController := controllers.NewTableWriteController(state, service, readController) // Read the table diff --git a/internal/dynamo-browse/models/serialisable/viewsnapshot.go b/internal/dynamo-browse/models/serialisable/viewsnapshot.go index 80cd04a..e04edd9 100644 --- a/internal/dynamo-browse/models/serialisable/viewsnapshot.go +++ b/internal/dynamo-browse/models/serialisable/viewsnapshot.go @@ -7,6 +7,7 @@ import ( type ViewSnapshot struct { ID int64 `storm:"id,increment"` BackLink int64 `storm:"index"` + ForeLink int64 `storm:"index"` Time time.Time TableName string Query string diff --git a/internal/dynamo-browse/providers/workspacestore/resultsetsnapshot.go b/internal/dynamo-browse/providers/workspacestore/resultsetsnapshot.go index 09ef0cf..0867b24 100644 --- a/internal/dynamo-browse/providers/workspacestore/resultsetsnapshot.go +++ b/internal/dynamo-browse/providers/workspacestore/resultsetsnapshot.go @@ -43,6 +43,55 @@ func (s *ResultSetSnapshotStore) SetAsHead(resultSetID int64) error { return nil } +func (s *ResultSetSnapshotStore) CurrentlyViewedSnapshot() (*serialisable.ViewSnapshot, error) { + var resultSetID int64 + if err := s.ws.Get("viewIds", "current", &resultSetID); err != nil { + if errors.Is(err, storm.ErrNotFound) { + return nil, nil + } + + return nil, errors.Wrap(err, "cannot get head") + } + + var rss serialisable.ViewSnapshot + if err := s.ws.One("ID", resultSetID, &rss); err != nil { + if errors.Is(err, storm.ErrNotFound) { + return nil, nil + } else { + return nil, errors.Wrap(err, "cannot get head") + } + } + + return &rss, nil +} + +func (s *ResultSetSnapshotStore) SetCurrentlyViewedSnapshot(resultSetID int64) error { + if resultSetID == 0 { + if err := s.ws.Delete("viewIds", "current"); err != nil { + return errors.Wrap(err, "cannot remove head") + } + return nil + } + + if err := s.ws.Set("viewIds", "current", resultSetID); err != nil { + return errors.Wrap(err, "cannot set as head") + } + return nil +} + +func (s *ResultSetSnapshotStore) Find(resultSetID int64) (*serialisable.ViewSnapshot, error) { + var rss serialisable.ViewSnapshot + if err := s.ws.One("ID", resultSetID, &rss); err != nil { + if errors.Is(err, storm.ErrNotFound) { + return nil, nil + } else { + return nil, errors.Wrap(err, "cannot get head") + } + } + + return &rss, nil +} + func (s *ResultSetSnapshotStore) Head() (*serialisable.ViewSnapshot, error) { var headResultSetID int64 if err := s.ws.Get("head", "id", &headResultSetID); err != nil && !errors.Is(err, storm.ErrNotFound) { @@ -61,6 +110,23 @@ func (s *ResultSetSnapshotStore) Head() (*serialisable.ViewSnapshot, error) { return &rss, nil } +func (s *ResultSetSnapshotStore) Dehead(fromNode *serialisable.ViewSnapshot) error { + n := fromNode.ForeLink + for n != 0 { + node, err := s.Find(n) + if err != nil { + return errors.Wrapf(err, "cannot get node with ID: %v", n) + } else if node == nil { + return errors.Errorf("expected node with ID %v, but did not find it", n) + } + if err := s.Remove(node.ID); err != nil { + log.Printf("warn: cannot delete node with ID %v", node.ID) + } + n = node.ForeLink + } + return nil +} + func (s *ResultSetSnapshotStore) Remove(resultSetId int64) error { var rss serialisable.ViewSnapshot if err := s.ws.One("ID", resultSetId, &rss); err != nil { diff --git a/internal/dynamo-browse/services/workspaces/iface.go b/internal/dynamo-browse/services/workspaces/iface.go index 75a151a..b2aebbe 100644 --- a/internal/dynamo-browse/services/workspaces/iface.go +++ b/internal/dynamo-browse/services/workspaces/iface.go @@ -5,6 +5,10 @@ import "github.com/lmika/audax/internal/dynamo-browse/models/serialisable" type ViewSnapshotStore interface { Save(rs *serialisable.ViewSnapshot) error SetAsHead(resultSetId int64) error + CurrentlyViewedSnapshot() (*serialisable.ViewSnapshot, error) + SetCurrentlyViewedSnapshot(resultSetId int64) error + Find(resultSetID int64) (*serialisable.ViewSnapshot, error) Head() (*serialisable.ViewSnapshot, error) Remove(resultSetId int64) error + Dehead(fromNode *serialisable.ViewSnapshot) error } diff --git a/internal/dynamo-browse/services/workspaces/service.go b/internal/dynamo-browse/services/workspaces/service.go index 34854dc..ad40fe0 100644 --- a/internal/dynamo-browse/services/workspaces/service.go +++ b/internal/dynamo-browse/services/workspaces/service.go @@ -27,43 +27,89 @@ func (s *ViewSnapshotService) PushSnapshot(rs *models.ResultSet, filter string) } newSnapshot.Filter = filter - if head, err := s.store.Head(); head != nil { - newSnapshot.BackLink = head.ID - } else if err != nil { - return errors.Wrap(err, "cannot get head result set") + oldHead, err := s.store.CurrentlyViewedSnapshot() + if err != nil { + return errors.Wrap(err, "cannot get snapshot head") + } + + if oldHead != nil { + newSnapshot.BackLink = oldHead.ID + + // Remove all nodes from this point on the head + if err := s.store.Dehead(oldHead); err != nil { + return errors.Wrap(err, "cannot remove head") + } } if err := s.store.Save(newSnapshot); err != nil { return errors.Wrap(err, "cannot save snapshot") } + + if oldHead != nil { + oldHead.ForeLink = newSnapshot.ID + if err := s.store.Save(oldHead); err != nil { + return errors.Wrap(err, "cannot update old head") + } + } + if err := s.store.SetAsHead(newSnapshot.ID); err != nil { return errors.Wrap(err, "cannot set new snapshot as head") } + if err := s.store.SetCurrentlyViewedSnapshot(newSnapshot.ID); err != nil { + return errors.Wrap(err, "cannot set new snapshot as head") + } return nil } -func (s *ViewSnapshotService) PopSnapshot() (*serialisable.ViewSnapshot, error) { - vs, err := s.store.Head() +func (s *ViewSnapshotService) ViewRestore() (*serialisable.ViewSnapshot, error) { + vs, err := s.store.CurrentlyViewedSnapshot() if err != nil { return nil, errors.Wrap(err, "cannot get snapshot head") - } else if vs == nil || vs.BackLink == 0 { - return nil, nil } - - if err := s.store.SetAsHead(vs.BackLink); err != nil { - return nil, errors.Wrap(err, "cannot set new head") - } - if err := s.store.Remove(vs.ID); err != nil { - return nil, errors.Wrap(err, "cannot remove old ID") - } - - vs, err = s.store.Head() - if err != nil { - return nil, errors.Wrap(err, "cannot get snapshot head") - } else if vs == nil || vs.BackLink == 0 { - return nil, nil - } - return vs, nil } + +func (s *ViewSnapshotService) ViewBack() (*serialisable.ViewSnapshot, error) { + vs, err := s.store.CurrentlyViewedSnapshot() + if err != nil { + return nil, errors.Wrap(err, "cannot get snapshot head") + } else if vs == nil || vs.BackLink == 0 { + return nil, nil + } + + vsToReturn, err := s.store.Find(vs.BackLink) + if err != nil { + return nil, errors.Wrap(err, "cannot get snapshot head") + } else if vsToReturn == nil { + return nil, nil + } + + if err := s.store.SetCurrentlyViewedSnapshot(vsToReturn.ID); err != nil { + return nil, errors.Wrap(err, "cannot set new head") + } + + return vsToReturn, nil +} + +func (s *ViewSnapshotService) ViewForward() (*serialisable.ViewSnapshot, error) { + vs, err := s.store.CurrentlyViewedSnapshot() + if err != nil { + return nil, errors.Wrap(err, "cannot get snapshot head") + } else if vs == nil || vs.ForeLink == 0 { + return nil, nil + } + + vsToReturn, err := s.store.Find(vs.ForeLink) + if err != nil { + return nil, errors.Wrap(err, "cannot get snapshot head") + } else if vsToReturn == nil { + return nil, nil + } + + if err := s.store.SetCurrentlyViewedSnapshot(vsToReturn.ID); err != nil { + return nil, errors.Wrap(err, "cannot set new head") + } + + return vsToReturn, nil +} diff --git a/internal/dynamo-browse/ui/model.go b/internal/dynamo-browse/ui/model.go index 9d7d5e7..c5584c3 100644 --- a/internal/dynamo-browse/ui/model.go +++ b/internal/dynamo-browse/ui/model.go @@ -163,6 +163,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, m.tableView.Refresh() case tea.KeyMsg: if !m.statusAndPrompt.InPrompt() && !m.tableSelect.Visible() { + log.Printf("key = %+v", msg) switch msg.String() { case "m": if idx := m.tableView.SelectedItemIndex(); idx >= 0 { @@ -180,6 +181,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, m.tableReadController.Filter case "backspace": return m, m.tableReadController.ViewBack + case "\\": + return m, m.tableReadController.ViewForward case "w": return m, func() tea.Msg { return controllers.SetTableItemView{ViewIndex: utils.Cycle(m.mainViewIndex, 1, ViewModeCount)}