Most of the new models have been reimplemented

This commit is contained in:
Leon Mika 2022-03-27 23:19:38 +00:00 committed by GitHub
parent 7a5584cf9a
commit aa828df3ae
19 changed files with 226 additions and 156 deletions

View file

@ -22,7 +22,6 @@ import (
"github.com/lmika/awstools/internal/dynamo-browse/ui" "github.com/lmika/awstools/internal/dynamo-browse/ui"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels" "github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/modal" "github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/modal"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/tableselect" "github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/tableselect"
"github.com/lmika/gopkgs/cli" "github.com/lmika/gopkgs/cli"
) )
@ -157,7 +156,8 @@ func newTestModel(descr string) tea.Model {
return nil return nil
}, },
tableselect.ShowTableSelect(func(n string) tea.Cmd { tableselect.ShowTableSelect(func(n string) tea.Cmd {
return statusandprompt.SetStatus("New table = " + n) // return statusandprompt.SetStatus("New table = " + n)
return nil
}), }),
), ),
) )

View file

@ -23,9 +23,9 @@ func NewCommandController(commands map[string]uimodels.Operation) *CommandContro
func (c *CommandController) Prompt() uimodels.Operation { func (c *CommandController) Prompt() uimodels.Operation {
return uimodels.OperationFn(func(ctx context.Context) error { return uimodels.OperationFn(func(ctx context.Context) error {
uiCtx := uimodels.Ctx(ctx) uiCtx := uimodels.Ctx(ctx)
uiCtx.Send(events.PromptForInput{ uiCtx.Send(events.PromptForInputMsg{
Prompt: ":", Prompt: ":",
OnDone: c.Execute(), // OnDone: c.Execute(),
}) })
return nil return nil
}) })

View file

@ -1,10 +1,7 @@
package dispatcher package dispatcher
import ( import (
"fmt"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/lmika/awstools/internal/common/ui/events"
"github.com/lmika/awstools/internal/common/ui/uimodels" "github.com/lmika/awstools/internal/common/ui/uimodels"
) )
@ -13,20 +10,20 @@ type DispatcherContext struct {
} }
func (dc DispatcherContext) Messagef(format string, args ...interface{}) { func (dc DispatcherContext) Messagef(format string, args ...interface{}) {
dc.Publisher.Send(events.Message(fmt.Sprintf(format, args...))) // dc.Publisher.Send(events.Message(fmt.Sprintf(format, args...)))
} }
func (dc DispatcherContext) Send(teaMessage tea.Msg) { func (dc DispatcherContext) Send(teaMessage tea.Msg) {
dc.Publisher.Send(teaMessage) // dc.Publisher.Send(teaMessage)
} }
func (dc DispatcherContext) Message(msg string) { func (dc DispatcherContext) Message(msg string) {
dc.Publisher.Send(events.Message(msg)) // dc.Publisher.Send(events.Message(msg))
} }
func (dc DispatcherContext) Input(prompt string, onDone uimodels.Operation) { func (dc DispatcherContext) Input(prompt string, onDone uimodels.Operation) {
dc.Publisher.Send(events.PromptForInput{ // dc.Publisher.Send(events.PromptForInput{
Prompt: prompt, // Prompt: prompt,
OnDone: onDone, // OnDone: onDone,
}) // })
} }

View file

@ -0,0 +1,26 @@
package events
import tea "github.com/charmbracelet/bubbletea"
func Error(err error) tea.Msg {
return ErrorMsg(err)
}
func SetStatus(msg string) tea.Cmd {
return func() tea.Msg {
return StatusMsg(msg)
}
}
func PromptForInput(prompt string, onDone func(value string) tea.Cmd) tea.Cmd {
return func() tea.Msg {
return PromptForInputMsg{
Prompt: prompt,
OnDone: onDone,
}
}
}
type MessageWithStatus interface {
StatusMessage() string
}

View file

@ -1,17 +1,17 @@
package events package events
import ( import (
"github.com/lmika/awstools/internal/common/ui/uimodels" tea "github.com/charmbracelet/bubbletea"
) )
// Error indicates that an error occurred // Error indicates that an error occurred
type Error error type ErrorMsg error
// Message indicates that a message should be shown to the user // Message indicates that a message should be shown to the user
type Message string type StatusMsg string
// PromptForInput indicates that the context is requesting a line of input // PromptForInput indicates that the context is requesting a line of input
type PromptForInput struct { type PromptForInputMsg struct {
Prompt string Prompt string
OnDone uimodels.Operation OnDone func(value string) tea.Cmd
} }

View file

@ -1,11 +1,25 @@
package controllers package controllers
import "github.com/lmika/awstools/internal/dynamo-browse/models" import (
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/lmika/awstools/internal/dynamo-browse/models"
)
type NewResultSet struct { type NewResultSet struct {
ResultSet *models.ResultSet ResultSet *models.ResultSet
} }
func (rs NewResultSet) StatusMessage() string {
return fmt.Sprintf("%d items returned", len(rs.ResultSet.Items))
}
type SetReadWrite struct { type SetReadWrite struct {
NewValue bool NewValue bool
} }
type PromptForTableMsg struct {
Tables []string
OnSelected func(tableName string) tea.Cmd
}

View file

@ -5,6 +5,7 @@ import (
"log" "log"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/lmika/awstools/internal/common/ui/events"
"github.com/lmika/awstools/internal/dynamo-browse/models" "github.com/lmika/awstools/internal/dynamo-browse/models"
"github.com/lmika/awstools/internal/dynamo-browse/services/tables" "github.com/lmika/awstools/internal/dynamo-browse/services/tables"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -22,22 +23,62 @@ func NewTableReadController(tableService *tables.Service, tableName string) *Tab
} }
} }
func (c *TableReadController) Scan() tea.Cmd { // 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.Cmd {
if c.tableName == "" {
return c.listTables()
} else {
return c.scanTable(c.tableName)
}
}
func (c *TableReadController) listTables() tea.Cmd {
return func() tea.Msg {
tables, err := c.tableService.ListTables(context.Background())
if err != nil {
return events.Error(err)
}
return PromptForTableMsg{
Tables: tables,
OnSelected: func(tableName string) tea.Cmd {
return c.scanTable(tableName)
},
}
}
}
func (c *TableReadController) scanTable(name string) tea.Cmd {
return func() tea.Msg { return func() tea.Msg {
ctx := context.Background() ctx := context.Background()
log.Println("Fetching table info") log.Println("Fetching table info")
tableInfo, err := c.tableInfo(ctx) tableInfo, err := c.tableService.Describe(ctx, name)
if err != nil { if err != nil {
log.Println("error: ", err) return events.Error(errors.Wrapf(err, "cannot describe %v", c.tableName))
return err
} }
log.Println("Scanning") log.Println("Scanning")
resultSet, err := c.tableService.Scan(ctx, tableInfo) resultSet, err := c.tableService.Scan(ctx, tableInfo)
if err != nil { if err != nil {
log.Println("error: ", err) log.Println("error: ", err)
return err return events.Error(err)
}
log.Println("Scan done")
return NewResultSet{resultSet}
}
}
func (c *TableReadController) Rescan(resultSet *models.ResultSet) tea.Cmd {
return func() tea.Msg {
ctx := context.Background()
log.Println("Scanning")
resultSet, err := c.tableService.Scan(ctx, resultSet.TableInfo)
if err != nil {
log.Println("error: ", err)
return events.Error(err)
} }
log.Println("Scan done") log.Println("Scan done")
@ -79,16 +120,16 @@ func (c *TableReadController) doScan(ctx context.Context, quiet bool) (err error
// tableInfo returns the table info from the state if a result set exists. If not, it fetches the // tableInfo returns the table info from the state if a result set exists. If not, it fetches the
// table information from the service. // table information from the service.
func (c *TableReadController) tableInfo(ctx context.Context) (*models.TableInfo, error) { // func (c *TableReadController) tableInfo(ctx context.Context) (*models.TableInfo, error) {
/* // /*
if existingResultSet := CurrentState(ctx).ResultSet; existingResultSet != nil { // if existingResultSet := CurrentState(ctx).ResultSet; existingResultSet != nil {
return existingResultSet.TableInfo, nil // return existingResultSet.TableInfo, nil
} // }
*/ // */
tableInfo, err := c.tableService.Describe(ctx, c.tableName) // tableInfo, err := c.tableService.Describe(ctx, c.tableName)
if err != nil { // if err != nil {
return nil, errors.Wrapf(err, "cannot describe %v", c.tableName) // return nil, errors.Wrapf(err, "cannot describe %v", c.tableName)
} // }
return tableInfo, nil // return tableInfo, nil
} // }

View file

@ -4,7 +4,6 @@ import (
"context" "context"
"github.com/lmika/awstools/internal/common/ui/uimodels" "github.com/lmika/awstools/internal/common/ui/uimodels"
"github.com/lmika/awstools/internal/dynamo-browse/models/modexpr"
"github.com/lmika/awstools/internal/dynamo-browse/services/tables" "github.com/lmika/awstools/internal/dynamo-browse/services/tables"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -41,56 +40,59 @@ func (c *TableWriteController) ToggleReadWrite() uimodels.Operation {
} }
func (c *TableWriteController) Duplicate() uimodels.Operation { func (c *TableWriteController) Duplicate() uimodels.Operation {
return uimodels.OperationFn(func(ctx context.Context) error { return nil
uiCtx := uimodels.Ctx(ctx) /*
state := CurrentState(ctx) return uimodels.OperationFn(func(ctx context.Context) error {
if state.SelectedItem == nil {
return errors.New("no selected item")
} else if !state.InReadWriteMode {
return errors.New("not in read/write mode")
}
uiCtx.Input("Dup: ", uimodels.OperationFn(func(ctx context.Context) error {
modExpr, err := modexpr.Parse(uimodels.PromptValue(ctx))
if err != nil {
return err
}
newItem, err := modExpr.Patch(state.SelectedItem)
if err != nil {
return err
}
// TODO: preview new item
uiCtx := uimodels.Ctx(ctx) uiCtx := uimodels.Ctx(ctx)
uiCtx.Input("Put item? ", uimodels.OperationFn(func(ctx context.Context) error { state := CurrentState(ctx)
if uimodels.PromptValue(ctx) != "y" {
return errors.New("operation aborted")
}
tableInfo, err := c.tableReadControllers.tableInfo(ctx) if state.SelectedItem == nil {
return errors.New("no selected item")
} else if !state.InReadWriteMode {
return errors.New("not in read/write mode")
}
uiCtx.Input("Dup: ", uimodels.OperationFn(func(ctx context.Context) error {
modExpr, err := modexpr.Parse(uimodels.PromptValue(ctx))
if err != nil { if err != nil {
return err return err
} }
// Delete the item newItem, err := modExpr.Patch(state.SelectedItem)
if err := c.tableService.Put(ctx, tableInfo, newItem); err != nil { if err != nil {
return err return err
} }
// Rescan to get updated items // TODO: preview new item
// if err := c.tableReadControllers.doScan(ctx, true); err != nil {
// return err
// }
uiCtx := uimodels.Ctx(ctx)
uiCtx.Input("Put item? ", uimodels.OperationFn(func(ctx context.Context) error {
if uimodels.PromptValue(ctx) != "y" {
return errors.New("operation aborted")
}
tableInfo, err := c.tableReadControllers.tableInfo(ctx)
if err != nil {
return err
}
// Delete the item
if err := c.tableService.Put(ctx, tableInfo, newItem); err != nil {
return err
}
// Rescan to get updated items
// if err := c.tableReadControllers.doScan(ctx, true); err != nil {
// return err
// }
return nil
}))
return nil return nil
})) }))
return nil return nil
})) })
return nil */
})
} }
func (c *TableWriteController) Delete() uimodels.Operation { func (c *TableWriteController) Delete() uimodels.Operation {
@ -111,15 +113,17 @@ func (c *TableWriteController) Delete() uimodels.Operation {
return errors.New("operation aborted") return errors.New("operation aborted")
} }
tableInfo, err := c.tableReadControllers.tableInfo(ctx) /*
if err != nil { tableInfo, err := c.tableReadControllers.tableInfo(ctx)
return err if err != nil {
} return err
}
// Delete the item // Delete the item
if err := c.tableService.Delete(ctx, tableInfo, state.SelectedItem); err != nil { if err := c.tableService.Delete(ctx, tableInfo, state.SelectedItem); err != nil {
return err return err
} }
*/
// Rescan to get updated items // Rescan to get updated items
// if err := c.tableReadControllers.doScan(ctx, true); err != nil { // if err := c.tableReadControllers.doScan(ctx, true); err != nil {

View file

@ -14,6 +14,15 @@ type Provider struct {
client *dynamodb.Client client *dynamodb.Client
} }
func (p *Provider) ListTables(ctx context.Context) ([]string, error) {
out, err := p.client.ListTables(ctx, &dynamodb.ListTablesInput{})
if err != nil {
return nil, errors.Wrapf(err, "cannot list tables")
}
return out.TableNames, nil
}
func (p *Provider) DescribeTable(ctx context.Context, tableName string) (*models.TableInfo, error) { func (p *Provider) DescribeTable(ctx context.Context, tableName string) (*models.TableInfo, error) {
out, err := p.client.DescribeTable(ctx, &dynamodb.DescribeTableInput{ out, err := p.client.DescribeTable(ctx, &dynamodb.DescribeTableInput{
TableName: aws.String(tableName), TableName: aws.String(tableName),

View file

@ -8,6 +8,7 @@ import (
) )
type TableProvider interface { type TableProvider interface {
ListTables(ctx context.Context) ([]string, error)
DescribeTable(ctx context.Context, tableName string) (*models.TableInfo, error) DescribeTable(ctx context.Context, tableName string) (*models.TableInfo, error)
ScanItems(ctx context.Context, tableName string) ([]models.Item, error) ScanItems(ctx context.Context, tableName string) ([]models.Item, error)
DeleteItem(ctx context.Context, tableName string, key map[string]types.AttributeValue) error DeleteItem(ctx context.Context, tableName string, key map[string]types.AttributeValue) error

View file

@ -18,6 +18,10 @@ func NewService(provider TableProvider) *Service {
} }
} }
func (s *Service) ListTables(ctx context.Context) ([]string, error) {
return s.provider.ListTables(ctx)
}
func (s *Service) Describe(ctx context.Context, table string) (*models.TableInfo, error) { func (s *Service) Describe(ctx context.Context, table string) (*models.TableInfo, error) {
return s.provider.DescribeTable(ctx, table) return s.provider.DescribeTable(ctx, table)
} }

View file

@ -33,7 +33,7 @@ func NewModel(rc *controllers.TableReadController) Model {
} }
func (m Model) Init() tea.Cmd { func (m Model) Init() tea.Cmd {
return m.tableReadController.Scan() return m.tableReadController.Init()
} }
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

View file

@ -11,7 +11,6 @@ import (
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
"github.com/lmika/awstools/internal/common/ui/commandctrl" "github.com/lmika/awstools/internal/common/ui/commandctrl"
"github.com/lmika/awstools/internal/common/ui/dispatcher" "github.com/lmika/awstools/internal/common/ui/dispatcher"
"github.com/lmika/awstools/internal/common/ui/events"
"github.com/lmika/awstools/internal/common/ui/uimodels" "github.com/lmika/awstools/internal/common/ui/uimodels"
"github.com/lmika/awstools/internal/dynamo-browse/controllers" "github.com/lmika/awstools/internal/dynamo-browse/controllers"
) )
@ -40,8 +39,8 @@ type uiModel struct {
state controllers.State state controllers.State
message string message string
pendingInput *events.PromptForInput // pendingInput *events.PromptForInput
textInput textinput.Model textInput textinput.Model
dispatcher *dispatcher.Dispatcher dispatcher *dispatcher.Dispatcher
commandController *commandctrl.CommandController commandController *commandctrl.CommandController
@ -152,15 +151,15 @@ func (m uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.state.InReadWriteMode = msg.NewValue m.state.InReadWriteMode = msg.NewValue
// Shared events // Shared events
case events.Error: // case events.Error:
m.message = "Error: " + msg.Error() // m.message = "Error: " + msg.Error()
case events.Message: // case events.Message:
m.message = string(msg) // m.message = string(msg)
case events.PromptForInput: // case events.PromptForInput:
m.textInput.Prompt = msg.Prompt // m.textInput.Prompt = msg.Prompt
m.textInput.Focus() // m.textInput.Focus()
m.textInput.SetValue("") // m.textInput.SetValue("")
m.pendingInput = &msg // m.pendingInput = &msg
// Tea events // Tea events
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
@ -186,18 +185,18 @@ func (m uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyMsg: case tea.KeyMsg:
// If text input in focus, allow that to accept input messages // If text input in focus, allow that to accept input messages
if m.pendingInput != nil { // if m.pendingInput != nil {
switch msg.String() { // switch msg.String() {
case "ctrl+c", "esc": // case "ctrl+c", "esc":
m.pendingInput = nil // m.pendingInput = nil
case "enter": // case "enter":
m.invokeOperation(uimodels.WithPromptValue(context.Background(), m.textInput.Value()), m.pendingInput.OnDone) // m.invokeOperation(uimodels.WithPromptValue(context.Background(), m.textInput.Value()), m.pendingInput.OnDone)
m.pendingInput = nil // m.pendingInput = nil
default: // default:
m.textInput, textInputCommands = m.textInput.Update(msg) // m.textInput, textInputCommands = m.textInput.Update(msg)
} // }
break // break
} // }
switch msg.String() { switch msg.String() {
case "ctrl+c", "q": case "ctrl+c", "q":

View file

@ -57,7 +57,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// TEMP // TEMP
case "s": case "s":
return m, m.tableReadControllers.Scan() return m, m.tableReadControllers.Rescan(m.resultSet)
case "ctrl+c", "esc": case "ctrl+c", "esc":
return m, tea.Quit return m, tea.Quit
} }

View file

@ -1,25 +0,0 @@
package statusandprompt
import tea "github.com/charmbracelet/bubbletea"
type setStatusMsg string
type startPromptMsg struct {
prompt string
onDone func(val string) tea.Cmd
}
func SetStatus(newStatus string) tea.Cmd {
return func() tea.Msg {
return setStatusMsg(newStatus)
}
}
func Prompt(prompt string, onDone func(val string) tea.Cmd) tea.Cmd {
return func() tea.Msg {
return startPromptMsg{
prompt: prompt,
onDone: onDone,
}
}
}

View file

@ -4,6 +4,7 @@ import (
"github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
"github.com/lmika/awstools/internal/common/ui/events"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout" "github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/utils" "github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/utils"
) )
@ -13,7 +14,7 @@ import (
type StatusAndPrompt struct { type StatusAndPrompt struct {
model layout.ResizingModel model layout.ResizingModel
statusMessage string statusMessage string
pendingInput *startPromptMsg pendingInput *events.PromptForInputMsg
textInput textinput.Model textInput textinput.Model
width int width int
} }
@ -29,15 +30,19 @@ func (s StatusAndPrompt) Init() tea.Cmd {
func (s StatusAndPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (s StatusAndPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) { switch msg := msg.(type) {
case setStatusMsg: case events.ErrorMsg:
s.statusMessage = string(msg) s.statusMessage = "Error: " + msg.Error()
case startPromptMsg: case events.StatusMsg:
s.statusMessage = string(s.statusMessage)
case events.MessageWithStatus:
s.statusMessage = msg.StatusMessage()
case events.PromptForInputMsg:
if s.pendingInput != nil { if s.pendingInput != nil {
// ignore, already in an input // ignore, already in an input
return s, nil return s, nil
} }
s.textInput.Prompt = msg.prompt s.textInput.Prompt = msg.Prompt
s.textInput.Focus() s.textInput.Focus()
s.textInput.SetValue("") s.textInput.SetValue("")
s.pendingInput = &msg s.pendingInput = &msg
@ -51,7 +56,7 @@ func (s StatusAndPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
pendingInput := s.pendingInput pendingInput := s.pendingInput
s.pendingInput = nil s.pendingInput = nil
return s, pendingInput.onDone(s.textInput.Value()) return s, pendingInput.OnDone(s.textInput.Value())
} }
} }
} }

View file

@ -15,13 +15,13 @@ func (ti tableItem) Title() string {
} }
func (ti tableItem) Description() string { func (ti tableItem) Description() string {
return "abc" return ""
} }
func toListItems[T list.Item](xs []T) []list.Item { func toListItems(xs []string) []list.Item {
ls := make([]list.Item, len(xs)) ls := make([]list.Item, len(xs))
for i, x := range xs { for i, x := range xs {
ls[i] = x ls[i] = tableItem{name: x}
} }
return ls return ls
} }

View file

@ -20,14 +20,8 @@ type listController struct {
list list.Model list list.Model
} }
func newListController(w, h int) listController { func newListController(tableNames []string, w, h int) listController {
tableItems := []tableItem{ items := toListItems(tableNames)
{name: "alpha"},
{name: "beta"},
{name: "gamma"},
}
items := toListItems(tableItems)
delegate := list.NewDefaultDelegate() delegate := list.NewDefaultDelegate()
delegate.ShowDescription = false delegate.ShowDescription = false

View file

@ -2,13 +2,14 @@ package tableselect
import ( import (
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout" "github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/utils" "github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/utils"
) )
type Model struct { type Model struct {
submodel tea.Model submodel tea.Model
pendingSelection *showTableSelectMsg pendingSelection *controllers.PromptForTableMsg
listController listController listController listController
isLoading bool isLoading bool
w, h int w, h int
@ -25,10 +26,10 @@ func (m Model) Init() tea.Cmd {
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cc utils.CmdCollector var cc utils.CmdCollector
switch msg := msg.(type) { switch msg := msg.(type) {
case showTableSelectMsg: case controllers.PromptForTableMsg:
m.isLoading = false m.isLoading = false
m.pendingSelection = &msg m.pendingSelection = &msg
m.listController = newListController(m.w, m.h) m.listController = newListController(msg.Tables, m.w, m.h)
return m, nil return m, nil
case indicateLoadingTablesMsg: case indicateLoadingTablesMsg:
m.isLoading = true m.isLoading = true
@ -37,10 +38,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.pendingSelection != nil { if m.pendingSelection != nil {
switch msg.String() { switch msg.String() {
case "enter": case "enter":
var sel showTableSelectMsg var sel controllers.PromptForTableMsg
sel, m.pendingSelection = *m.pendingSelection, nil sel, m.pendingSelection = *m.pendingSelection, nil
return m, sel.onSelected(m.listController.list.SelectedItem().(tableItem).name) return m, sel.OnSelected(m.listController.list.SelectedItem().(tableItem).name)
default: default:
m.listController = cc.Collect(m.listController.Update(msg)).(listController) m.listController = cc.Collect(m.listController.Update(msg)).(listController)
return m, cc.Cmd() return m, cc.Cmd()