table-select: cleanup
This commit is contained in:
parent
6f323fa4cf
commit
9709e6aed1
|
@ -4,25 +4,18 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/config"
|
"github.com/aws/aws-sdk-go-v2/config"
|
||||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
|
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
|
||||||
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/commandctrl"
|
"github.com/lmika/awstools/internal/common/ui/commandctrl"
|
||||||
"github.com/lmika/awstools/internal/common/ui/dispatcher"
|
|
||||||
"github.com/lmika/awstools/internal/common/ui/uimodels"
|
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
|
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/providers/dynamo"
|
"github.com/lmika/awstools/internal/dynamo-browse/providers/dynamo"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/services/tables"
|
"github.com/lmika/awstools/internal/dynamo-browse/services/tables"
|
||||||
"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/modal"
|
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/tableselect"
|
|
||||||
"github.com/lmika/gopkgs/cli"
|
"github.com/lmika/gopkgs/cli"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -32,10 +25,7 @@ func main() {
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// TEMP
|
|
||||||
cfg, err := config.LoadDefaultConfig(ctx)
|
cfg, err := config.LoadDefaultConfig(ctx)
|
||||||
|
|
||||||
// END TEMP
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cli.Fatalf("cannot load AWS config: %v", err)
|
cli.Fatalf("cannot load AWS config: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -52,70 +42,22 @@ func main() {
|
||||||
|
|
||||||
tableService := tables.NewService(dynamoProvider)
|
tableService := tables.NewService(dynamoProvider)
|
||||||
|
|
||||||
loopback := &msgLoopback{}
|
|
||||||
uiDispatcher := dispatcher.NewDispatcher(loopback)
|
|
||||||
|
|
||||||
tableReadController := controllers.NewTableReadController(tableService, *flagTable)
|
tableReadController := controllers.NewTableReadController(tableService, *flagTable)
|
||||||
tableWriteController := controllers.NewTableWriteController(tableService, tableReadController, *flagTable)
|
tableWriteController := controllers.NewTableWriteController(tableService, tableReadController, *flagTable)
|
||||||
|
_ = tableWriteController
|
||||||
|
|
||||||
commandController := commandctrl.NewCommandController(map[string]uimodels.Operation{
|
commandController := commandctrl.NewCommandController(map[string]commandctrl.Command{
|
||||||
// "scan": tableReadController.Scan(),
|
"q": commandctrl.NoArgCommand(tea.Quit),
|
||||||
"rw": tableWriteController.ToggleReadWrite(),
|
//"rw": tableWriteController.ToggleReadWrite(),
|
||||||
"dup": tableWriteController.Duplicate(),
|
//"dup": tableWriteController.Duplicate(),
|
||||||
})
|
})
|
||||||
|
|
||||||
_ = uiDispatcher
|
model := ui.NewModel(tableReadController, commandController)
|
||||||
_ = commandController
|
|
||||||
|
|
||||||
// uiModel := ui.NewModel(uiDispatcher, commandController, tableReadController, tableWriteController)
|
|
||||||
|
|
||||||
// TEMP
|
|
||||||
// _ = uiModel
|
|
||||||
// END TEMP
|
|
||||||
|
|
||||||
/*
|
|
||||||
var model tea.Model = statusandprompt.New(
|
|
||||||
layout.NewVBox(
|
|
||||||
layout.LastChildFixedAt(11),
|
|
||||||
dynamotableview.New(tableReadController),
|
|
||||||
dynamoitemview.New(),
|
|
||||||
),
|
|
||||||
"Hello world",
|
|
||||||
)
|
|
||||||
model = layout.FullScreen(tableselect.New(model))
|
|
||||||
*/
|
|
||||||
model := ui.NewModel(tableReadController)
|
|
||||||
|
|
||||||
// Pre-determine if layout has dark background. This prevents calls for creating a list to hang.
|
// Pre-determine if layout has dark background. This prevents calls for creating a list to hang.
|
||||||
lipgloss.HasDarkBackground()
|
lipgloss.HasDarkBackground()
|
||||||
|
|
||||||
//frameSet := frameset.New([]frameset.Frame{
|
|
||||||
// {
|
|
||||||
// Header: "Frame 1",
|
|
||||||
// Model: newTestModel("this is model 1"),
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// Header: "Frame 2",
|
|
||||||
// Model: newTestModel("this is model 2"),
|
|
||||||
// },
|
|
||||||
//})
|
|
||||||
//
|
|
||||||
//modal := modal.New(frameSet)
|
|
||||||
|
|
||||||
p := tea.NewProgram(model, tea.WithAltScreen())
|
p := tea.NewProgram(model, tea.WithAltScreen())
|
||||||
//loopback.program = p
|
|
||||||
|
|
||||||
// TEMP -- profiling
|
|
||||||
//cf, err := os.Create("trace.out")
|
|
||||||
//if err != nil {
|
|
||||||
// log.Fatal("could not create CPU profile: ", err)
|
|
||||||
//}
|
|
||||||
//defer cf.Close() // error handling omitted for example
|
|
||||||
//if err := trace.Start(cf); err != nil {
|
|
||||||
// log.Fatal("could not start CPU profile: ", err)
|
|
||||||
//}
|
|
||||||
//defer trace.Stop()
|
|
||||||
// END TEMP
|
|
||||||
|
|
||||||
f, err := tea.LogToFile("debug.log", "debug")
|
f, err := tea.LogToFile("debug.log", "debug")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -131,37 +73,11 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type msgLoopback struct {
|
//
|
||||||
program *tea.Program
|
//type msgLoopback struct {
|
||||||
}
|
// program *tea.Program
|
||||||
|
//}
|
||||||
func (m *msgLoopback) Send(msg tea.Msg) {
|
//
|
||||||
m.program.Send(msg)
|
//func (m *msgLoopback) Send(msg tea.Msg) {
|
||||||
}
|
// m.program.Send(msg)
|
||||||
|
//}
|
||||||
func newTestModel(descr string) tea.Model {
|
|
||||||
return teamodels.TestModel{
|
|
||||||
Message: descr,
|
|
||||||
OnKeyPressed: func(k string) tea.Cmd {
|
|
||||||
log.Println("got key press: " + k)
|
|
||||||
if k == "enter" {
|
|
||||||
return tea.Batch(
|
|
||||||
tableselect.IndicateLoadingTables(),
|
|
||||||
tea.Sequentially(
|
|
||||||
func() tea.Msg {
|
|
||||||
<-time.After(2 * time.Second)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
tableselect.ShowTableSelect(func(n string) tea.Cmd {
|
|
||||||
// return statusandprompt.SetStatus("New table = " + n)
|
|
||||||
return nil
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
} else if k == "k" {
|
|
||||||
return modal.PopMode
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,49 +1,45 @@
|
||||||
package commandctrl
|
package commandctrl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/lmika/awstools/internal/common/ui/events"
|
"github.com/lmika/awstools/internal/common/ui/events"
|
||||||
"github.com/lmika/awstools/internal/common/ui/uimodels"
|
|
||||||
"github.com/lmika/shellwords"
|
"github.com/lmika/shellwords"
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandController struct {
|
type CommandController struct {
|
||||||
commands map[string]uimodels.Operation
|
commands map[string]Command
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommandController(commands map[string]uimodels.Operation) *CommandController {
|
func NewCommandController(commands map[string]Command) *CommandController {
|
||||||
return &CommandController{
|
return &CommandController{
|
||||||
commands: commands,
|
commands: commands,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CommandController) Prompt() uimodels.Operation {
|
func (c *CommandController) Prompt() tea.Cmd {
|
||||||
return uimodels.OperationFn(func(ctx context.Context) error {
|
return func() tea.Msg {
|
||||||
uiCtx := uimodels.Ctx(ctx)
|
return events.PromptForInputMsg{
|
||||||
uiCtx.Send(events.PromptForInputMsg{
|
|
||||||
Prompt: ":",
|
Prompt: ":",
|
||||||
// OnDone: c.Execute(),
|
OnDone: func(value string) tea.Cmd {
|
||||||
})
|
return c.Execute(value)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommandController) Execute(commandInput string) tea.Cmd {
|
||||||
|
input := strings.TrimSpace(commandInput)
|
||||||
|
if input == "" {
|
||||||
return nil
|
return nil
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
tokens := shellwords.Split(input)
|
||||||
func (c *CommandController) Execute() uimodels.Operation {
|
command, ok := c.commands[tokens[0]]
|
||||||
return uimodels.OperationFn(func(ctx context.Context) error {
|
if !ok {
|
||||||
input := strings.TrimSpace(uimodels.PromptValue(ctx))
|
return events.SetStatus("no such command: " + tokens[0])
|
||||||
if input == "" {
|
}
|
||||||
return nil
|
|
||||||
}
|
return command(tokens)
|
||||||
|
|
||||||
tokens := shellwords.Split(input)
|
|
||||||
command, ok := c.commands[tokens[0]]
|
|
||||||
if !ok {
|
|
||||||
return errors.New("no such command: " + tokens[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
return command.Execute(WithCommandArgs(ctx, tokens[1:]))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
11
internal/common/ui/commandctrl/types.go
Normal file
11
internal/common/ui/commandctrl/types.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package commandctrl
|
||||||
|
|
||||||
|
import tea "github.com/charmbracelet/bubbletea"
|
||||||
|
|
||||||
|
type Command func(args []string) tea.Cmd
|
||||||
|
|
||||||
|
func NoArgCommand(cmd tea.Cmd) Command {
|
||||||
|
return func(args []string) tea.Cmd {
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,6 @@ package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
|
||||||
|
|
||||||
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/events"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
||||||
|
@ -52,20 +50,16 @@ 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")
|
|
||||||
tableInfo, err := c.tableService.Describe(ctx, name)
|
tableInfo, err := c.tableService.Describe(ctx, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return events.Error(errors.Wrapf(err, "cannot describe %v", c.tableName))
|
return events.Error(errors.Wrapf(err, "cannot describe %v", c.tableName))
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Scan done")
|
|
||||||
return NewResultSet{resultSet}
|
return NewResultSet{resultSet}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,14 +68,11 @@ func (c *TableReadController) Rescan(resultSet *models.ResultSet) tea.Cmd {
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
log.Println("Scanning")
|
|
||||||
resultSet, err := c.tableService.Scan(ctx, resultSet.TableInfo)
|
resultSet, err := c.tableService.Scan(ctx, resultSet.TableInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("error: ", err)
|
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Scan done")
|
|
||||||
return NewResultSet{resultSet}
|
return NewResultSet{resultSet}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package ui
|
|
||||||
|
|
||||||
import tea "github.com/charmbracelet/bubbletea"
|
|
||||||
|
|
||||||
type MessagePublisher interface {
|
|
||||||
Send(msg tea.Msg)
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/lmika/awstools/internal/common/ui/commandctrl"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
|
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/dynamoitemview"
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/dynamoitemview"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/dynamotableview"
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/dynamotableview"
|
||||||
|
@ -12,12 +13,13 @@ import (
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
tableReadController *controllers.TableReadController
|
tableReadController *controllers.TableReadController
|
||||||
|
commandController *commandctrl.CommandController
|
||||||
|
|
||||||
root tea.Model
|
root tea.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModel(rc *controllers.TableReadController) Model {
|
func NewModel(rc *controllers.TableReadController, cc *commandctrl.CommandController) Model {
|
||||||
dtv := dynamotableview.New(rc)
|
dtv := dynamotableview.New(rc, cc)
|
||||||
div := dynamoitemview.New()
|
div := dynamoitemview.New()
|
||||||
|
|
||||||
m := statusandprompt.New(
|
m := statusandprompt.New(
|
||||||
|
@ -28,6 +30,7 @@ func NewModel(rc *controllers.TableReadController) Model {
|
||||||
|
|
||||||
return Model{
|
return Model{
|
||||||
tableReadController: rc,
|
tableReadController: rc,
|
||||||
|
commandController: cc,
|
||||||
root: root,
|
root: root,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,303 +0,0 @@
|
||||||
package ui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
table "github.com/calyptia/go-bubble-table"
|
|
||||||
"github.com/charmbracelet/bubbles/textinput"
|
|
||||||
"github.com/charmbracelet/bubbles/viewport"
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
|
||||||
"github.com/charmbracelet/lipgloss"
|
|
||||||
"github.com/lmika/awstools/internal/common/ui/commandctrl"
|
|
||||||
"github.com/lmika/awstools/internal/common/ui/dispatcher"
|
|
||||||
"github.com/lmika/awstools/internal/common/ui/uimodels"
|
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
activeHeaderStyle = lipgloss.NewStyle().
|
|
||||||
Bold(true).
|
|
||||||
Foreground(lipgloss.Color("#ffffff")).
|
|
||||||
Background(lipgloss.Color("#4479ff"))
|
|
||||||
|
|
||||||
inactiveHeaderStyle = lipgloss.NewStyle().
|
|
||||||
Foreground(lipgloss.Color("#000000")).
|
|
||||||
Background(lipgloss.Color("#d1d1d1"))
|
|
||||||
)
|
|
||||||
|
|
||||||
type uiModel struct {
|
|
||||||
table table.Model
|
|
||||||
viewport viewport.Model
|
|
||||||
|
|
||||||
// TEMP
|
|
||||||
tableSelect tea.Model
|
|
||||||
|
|
||||||
tableWidth, tableHeight int
|
|
||||||
|
|
||||||
ready bool
|
|
||||||
state controllers.State
|
|
||||||
message string
|
|
||||||
|
|
||||||
// pendingInput *events.PromptForInput
|
|
||||||
textInput textinput.Model
|
|
||||||
|
|
||||||
dispatcher *dispatcher.Dispatcher
|
|
||||||
commandController *commandctrl.CommandController
|
|
||||||
tableReadController *controllers.TableReadController
|
|
||||||
tableWriteController *controllers.TableWriteController
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewModelOld(dispatcher *dispatcher.Dispatcher, commandController *commandctrl.CommandController, tableReadController *controllers.TableReadController, tableWriteController *controllers.TableWriteController) tea.Model {
|
|
||||||
tbl := table.New([]string{"pk", "sk"}, 100, 20)
|
|
||||||
rows := make([]table.Row, 0)
|
|
||||||
tbl.SetRows(rows)
|
|
||||||
|
|
||||||
textInput := textinput.New()
|
|
||||||
|
|
||||||
model := uiModel{
|
|
||||||
table: tbl,
|
|
||||||
message: "Press s to scan",
|
|
||||||
textInput: textInput,
|
|
||||||
|
|
||||||
// TEMP
|
|
||||||
tableSelect: newSizeWaitModel(func(w, h int) tea.Model {
|
|
||||||
return newTableSelectModel(w, h)
|
|
||||||
}),
|
|
||||||
|
|
||||||
dispatcher: dispatcher,
|
|
||||||
commandController: commandController,
|
|
||||||
tableReadController: tableReadController,
|
|
||||||
tableWriteController: tableWriteController,
|
|
||||||
}
|
|
||||||
|
|
||||||
return model
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m uiModel) Init() tea.Cmd {
|
|
||||||
//m.invokeOperation(context.Background(), m.tableReadController.Scan())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
func (m *uiModel) updateTable() {
|
|
||||||
if !m.ready {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resultSet := m.state.ResultSet
|
|
||||||
newTbl := table.New(resultSet.Columns, m.tableWidth, m.tableHeight)
|
|
||||||
newRows := make([]table.Row, len(resultSet.Items))
|
|
||||||
for i, r := range resultSet.Items {
|
|
||||||
newRows[i] = itemTableRow{resultSet, r}
|
|
||||||
}
|
|
||||||
newTbl.SetRows(newRows)
|
|
||||||
|
|
||||||
m.table = newTbl
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func (m *uiModel) selectedItem() (itemTableRow, bool) {
|
|
||||||
resultSet := m.state.ResultSet
|
|
||||||
if m.ready && resultSet != nil && len(resultSet.Items) > 0 {
|
|
||||||
selectedItem, ok := m.table.SelectedRow().(itemTableRow)
|
|
||||||
if ok {
|
|
||||||
return selectedItem, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return itemTableRow{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *uiModel) updateViewportToSelectedMessage() {
|
|
||||||
selectedItem, ok := m.selectedItem()
|
|
||||||
if !ok {
|
|
||||||
m.viewport.SetContent("(no row selected)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
viewportContent := &strings.Builder{}
|
|
||||||
tabWriter := tabwriter.NewWriter(viewportContent, 0, 1, 1, ' ', 0)
|
|
||||||
for _, colName := range selectedItem.resultSet.Columns {
|
|
||||||
switch colVal := selectedItem.item[colName].(type) {
|
|
||||||
case nil:
|
|
||||||
break
|
|
||||||
case *types.AttributeValueMemberS:
|
|
||||||
fmt.Fprintf(tabWriter, "%v\tS\t%s\n", colName, colVal.Value)
|
|
||||||
case *types.AttributeValueMemberN:
|
|
||||||
fmt.Fprintf(tabWriter, "%v\tN\t%s\n", colName, colVal.Value)
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(tabWriter, "%v\t?\t%s\n", colName, "(other)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tabWriter.Flush()
|
|
||||||
m.viewport.SetContent(viewportContent.String())
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (m uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
||||||
var textInputCommands tea.Cmd
|
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
|
|
||||||
// Local events
|
|
||||||
case controllers.NewResultSet:
|
|
||||||
m.state.ResultSet = msg.ResultSet
|
|
||||||
// m.updateTable()
|
|
||||||
// m.updateViewportToSelectedMessage()
|
|
||||||
case controllers.SetReadWrite:
|
|
||||||
m.state.InReadWriteMode = msg.NewValue
|
|
||||||
|
|
||||||
// Shared events
|
|
||||||
// case events.Error:
|
|
||||||
// m.message = "Error: " + msg.Error()
|
|
||||||
// case events.Message:
|
|
||||||
// m.message = string(msg)
|
|
||||||
// case events.PromptForInput:
|
|
||||||
// m.textInput.Prompt = msg.Prompt
|
|
||||||
// m.textInput.Focus()
|
|
||||||
// m.textInput.SetValue("")
|
|
||||||
// m.pendingInput = &msg
|
|
||||||
|
|
||||||
// Tea events
|
|
||||||
case tea.WindowSizeMsg:
|
|
||||||
fixedViewsHeight := lipgloss.Height(m.headerView()) + lipgloss.Height(m.splitterView()) + lipgloss.Height(m.footerView())
|
|
||||||
viewportHeight := msg.Height / 2 // TODO: make this dynamic
|
|
||||||
if viewportHeight > 15 {
|
|
||||||
viewportHeight = 15
|
|
||||||
}
|
|
||||||
tableHeight := msg.Height - fixedViewsHeight - viewportHeight
|
|
||||||
|
|
||||||
if !m.ready {
|
|
||||||
m.viewport = viewport.New(msg.Width, viewportHeight)
|
|
||||||
m.viewport.SetContent("(no message selected)")
|
|
||||||
m.ready = true
|
|
||||||
} else {
|
|
||||||
m.viewport.Width = msg.Width
|
|
||||||
m.viewport.Height = msg.Height - tableHeight - fixedViewsHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
m.tableWidth, m.tableHeight = msg.Width, tableHeight
|
|
||||||
m.table.SetSize(m.tableWidth, m.tableHeight)
|
|
||||||
|
|
||||||
case tea.KeyMsg:
|
|
||||||
|
|
||||||
// If text input in focus, allow that to accept input messages
|
|
||||||
// if m.pendingInput != nil {
|
|
||||||
// switch msg.String() {
|
|
||||||
// case "ctrl+c", "esc":
|
|
||||||
// m.pendingInput = nil
|
|
||||||
// case "enter":
|
|
||||||
// m.invokeOperation(uimodels.WithPromptValue(context.Background(), m.textInput.Value()), m.pendingInput.OnDone)
|
|
||||||
// m.pendingInput = nil
|
|
||||||
// default:
|
|
||||||
// m.textInput, textInputCommands = m.textInput.Update(msg)
|
|
||||||
// }
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
|
|
||||||
switch msg.String() {
|
|
||||||
case "ctrl+c", "q":
|
|
||||||
return m, tea.Quit
|
|
||||||
case "up", "i":
|
|
||||||
m.table.GoUp()
|
|
||||||
// m.updateViewportToSelectedMessage()
|
|
||||||
case "down", "k":
|
|
||||||
m.table.GoDown()
|
|
||||||
// m.updateViewportToSelectedMessage()
|
|
||||||
|
|
||||||
// TODO: these should be moved somewhere else
|
|
||||||
case ":":
|
|
||||||
m.invokeOperation(context.Background(), m.commandController.Prompt())
|
|
||||||
// case "s":
|
|
||||||
// m.invokeOperation(context.Background(), m.tableReadController.Scan())
|
|
||||||
case "D":
|
|
||||||
m.invokeOperation(context.Background(), m.tableWriteController.Delete())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
m.textInput, textInputCommands = m.textInput.Update(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedTable, tableMsgs := m.table.Update(msg)
|
|
||||||
updatedViewport, viewportMsgs := m.viewport.Update(msg)
|
|
||||||
updatedTableSelectModel, tableSelectMsgs := m.tableSelect.Update(msg)
|
|
||||||
|
|
||||||
m.table = updatedTable
|
|
||||||
m.viewport = updatedViewport
|
|
||||||
m.tableSelect = updatedTableSelectModel
|
|
||||||
|
|
||||||
return m, tea.Batch(textInputCommands, tableMsgs, viewportMsgs, tableSelectMsgs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m uiModel) invokeOperation(ctx context.Context, op uimodels.Operation) {
|
|
||||||
state := m.state
|
|
||||||
// if selectedItem, ok := m.selectedItem(); ok {
|
|
||||||
// state.SelectedItem = selectedItem.item
|
|
||||||
// }
|
|
||||||
|
|
||||||
ctx = controllers.ContextWithState(ctx, state)
|
|
||||||
m.dispatcher.Start(ctx, op)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m uiModel) View() string {
|
|
||||||
// TEMP
|
|
||||||
return m.tableSelect.View()
|
|
||||||
|
|
||||||
/*
|
|
||||||
if !m.ready {
|
|
||||||
return "Initializing"
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.pendingInput != nil {
|
|
||||||
return lipgloss.JoinVertical(lipgloss.Top,
|
|
||||||
m.headerView(),
|
|
||||||
m.table.View(),
|
|
||||||
m.splitterView(),
|
|
||||||
m.viewport.View(),
|
|
||||||
m.textInput.View(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return lipgloss.JoinVertical(lipgloss.Top,
|
|
||||||
m.headerView(),
|
|
||||||
m.table.View(),
|
|
||||||
m.splitterView(),
|
|
||||||
m.viewport.View(),
|
|
||||||
m.footerView(),
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m uiModel) headerView() string {
|
|
||||||
var titleText string
|
|
||||||
if m.state.ResultSet != nil {
|
|
||||||
titleText = "Table: " + m.state.ResultSet.TableInfo.Name
|
|
||||||
} else {
|
|
||||||
titleText = "No table"
|
|
||||||
}
|
|
||||||
|
|
||||||
title := activeHeaderStyle.Render(titleText)
|
|
||||||
line := activeHeaderStyle.Render(strings.Repeat(" ", max(0, m.viewport.Width-lipgloss.Width(title))))
|
|
||||||
return lipgloss.JoinHorizontal(lipgloss.Left, title, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m uiModel) splitterView() string {
|
|
||||||
title := inactiveHeaderStyle.Render("Item")
|
|
||||||
line := inactiveHeaderStyle.Render(strings.Repeat(" ", max(0, m.viewport.Width-lipgloss.Width(title))))
|
|
||||||
return lipgloss.JoinHorizontal(lipgloss.Left, title, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m uiModel) footerView() string {
|
|
||||||
title := m.message
|
|
||||||
line := strings.Repeat(" ", max(0, m.viewport.Width-lipgloss.Width(title)))
|
|
||||||
return lipgloss.JoinHorizontal(lipgloss.Left, title, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
func max(a, b int) int {
|
|
||||||
if a > b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
package ui
|
|
||||||
|
|
||||||
import (
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// sizeWaitModel is a model which waits until the first screen size message comes through. It then creates the
|
|
||||||
// submodel and delegates calls to that model
|
|
||||||
type sizeWaitModel struct {
|
|
||||||
constr func(width, height int) tea.Model
|
|
||||||
model tea.Model
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSizeWaitModel(constr func(width, height int) tea.Model) tea.Model {
|
|
||||||
return sizeWaitModel{constr: constr}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s sizeWaitModel) Init() tea.Cmd {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s sizeWaitModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
||||||
switch m := msg.(type) {
|
|
||||||
case tea.WindowSizeMsg:
|
|
||||||
log.Println("got window size message")
|
|
||||||
if s.model == nil {
|
|
||||||
log.Println("creating model")
|
|
||||||
s.model = s.constr(m.Width, m.Height)
|
|
||||||
s.model.Init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var submodelCmds tea.Cmd
|
|
||||||
if s.model != nil {
|
|
||||||
log.Println("starting update")
|
|
||||||
s.model, submodelCmds = s.model.Update(msg)
|
|
||||||
log.Println("ending update")
|
|
||||||
}
|
|
||||||
return s, submodelCmds
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s sizeWaitModel) View() string {
|
|
||||||
if s.model == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return s.model.View()
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
package ui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/charmbracelet/bubbles/list"
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
|
||||||
"github.com/charmbracelet/lipgloss"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
titleStyle = lipgloss.NewStyle().MarginLeft(2)
|
|
||||||
itemStyle = lipgloss.NewStyle().PaddingLeft(4)
|
|
||||||
selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170"))
|
|
||||||
paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4)
|
|
||||||
helpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1)
|
|
||||||
quitTextStyle = lipgloss.NewStyle().Margin(1, 0, 2, 4)
|
|
||||||
)
|
|
||||||
|
|
||||||
type tableSelectModel struct {
|
|
||||||
list list.Model
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t tableSelectModel) Init() tea.Cmd {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t tableSelectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case tea.WindowSizeMsg:
|
|
||||||
t.list.SetHeight(msg.Height)
|
|
||||||
t.list.SetWidth(msg.Width)
|
|
||||||
return t, nil
|
|
||||||
|
|
||||||
case tea.KeyMsg:
|
|
||||||
switch keypress := msg.String(); keypress {
|
|
||||||
case "ctrl+c":
|
|
||||||
return t, tea.Quit
|
|
||||||
|
|
||||||
case "enter":
|
|
||||||
//i, ok := m.list.SelectedItem().(item)
|
|
||||||
//if ok {
|
|
||||||
// m.choice = string(i)
|
|
||||||
//}
|
|
||||||
return t, tea.Quit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmd tea.Cmd
|
|
||||||
t.list, cmd = t.list.Update(msg)
|
|
||||||
return t, cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t tableSelectModel) View() string {
|
|
||||||
return t.list.View()
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTableSelectModel(w, h int) tableSelectModel {
|
|
||||||
tableItems := []tableItem{
|
|
||||||
{name: "alpha"},
|
|
||||||
{name: "beta"},
|
|
||||||
{name: "gamma"},
|
|
||||||
}
|
|
||||||
|
|
||||||
items := toListItems(tableItems)
|
|
||||||
|
|
||||||
delegate := list.NewDefaultDelegate()
|
|
||||||
delegate.ShowDescription = false
|
|
||||||
|
|
||||||
return tableSelectModel{
|
|
||||||
list: list.New(items, delegate, w, h),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type tableItem struct {
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ti tableItem) FilterValue() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ti tableItem) Title() string {
|
|
||||||
return ti.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ti tableItem) Description() string {
|
|
||||||
return "abc"
|
|
||||||
}
|
|
||||||
|
|
||||||
func toListItems[T list.Item](xs []T) []list.Item {
|
|
||||||
ls := make([]list.Item, len(xs))
|
|
||||||
for i, x := range xs {
|
|
||||||
ls[i] = x
|
|
||||||
}
|
|
||||||
return ls
|
|
||||||
}
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
table "github.com/calyptia/go-bubble-table"
|
table "github.com/calyptia/go-bubble-table"
|
||||||
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/commandctrl"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
|
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/dynamoitemview"
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/dynamoitemview"
|
||||||
|
@ -13,15 +14,17 @@ import (
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
tableReadControllers *controllers.TableReadController
|
tableReadControllers *controllers.TableReadController
|
||||||
frameTitle frame.FrameTitle
|
commandCtrl *commandctrl.CommandController
|
||||||
table table.Model
|
|
||||||
w, h int
|
frameTitle frame.FrameTitle
|
||||||
|
table table.Model
|
||||||
|
w, h int
|
||||||
|
|
||||||
// model state
|
// model state
|
||||||
resultSet *models.ResultSet
|
resultSet *models.ResultSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(tableReadControllers *controllers.TableReadController) Model {
|
func New(tableReadControllers *controllers.TableReadController, commandCtrl *commandctrl.CommandController) Model {
|
||||||
tbl := table.New([]string{"pk", "sk"}, 100, 100)
|
tbl := table.New([]string{"pk", "sk"}, 100, 100)
|
||||||
rows := make([]table.Row, 0)
|
rows := make([]table.Row, 0)
|
||||||
tbl.SetRows(rows)
|
tbl.SetRows(rows)
|
||||||
|
@ -30,6 +33,7 @@ func New(tableReadControllers *controllers.TableReadController) Model {
|
||||||
|
|
||||||
return Model{
|
return Model{
|
||||||
tableReadControllers: tableReadControllers,
|
tableReadControllers: tableReadControllers,
|
||||||
|
commandCtrl: commandCtrl,
|
||||||
frameTitle: frameTitle,
|
frameTitle: frameTitle,
|
||||||
table: tbl,
|
table: tbl,
|
||||||
}
|
}
|
||||||
|
@ -58,6 +62,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
// TEMP
|
// TEMP
|
||||||
case "s":
|
case "s":
|
||||||
return m, m.tableReadControllers.Rescan(m.resultSet)
|
return m, m.tableReadControllers.Rescan(m.resultSet)
|
||||||
|
case ":":
|
||||||
|
return m, m.commandCtrl.Prompt()
|
||||||
|
// END TEMP
|
||||||
case "ctrl+c", "esc":
|
case "ctrl+c", "esc":
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
package teamodels
|
|
||||||
|
|
||||||
import tea "github.com/charmbracelet/bubbletea"
|
|
||||||
|
|
||||||
// NewModePushed pushes a new mode on the modal stack
|
|
||||||
type NewModePushed tea.Model
|
|
||||||
|
|
||||||
// ModePopped pops a mode from the modal stack
|
|
||||||
type ModePopped struct{}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package teamodels
|
|
||||||
|
|
||||||
import (
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestModel is a model used for testing
|
|
||||||
type TestModel struct {
|
|
||||||
Message string
|
|
||||||
OnKeyPressed func(k string) tea.Cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t TestModel) Init() tea.Cmd {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t TestModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case tea.KeyMsg:
|
|
||||||
switch msg.String() {
|
|
||||||
case "ctrl+c", "esc":
|
|
||||||
return t, tea.Quit
|
|
||||||
default:
|
|
||||||
return t, t.OnKeyPressed(msg.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t TestModel) View() string {
|
|
||||||
return t.Message
|
|
||||||
}
|
|
Loading…
Reference in a new issue