Skip to content

Commit

Permalink
feat: add new input model, use in projects step (#30)
Browse files Browse the repository at this point in the history
* add new text input mmodel

* add new input step to project model, update wizard step to handle that update

* add note about error handling;

* remove debugging stmt

* store projects in memory so we can mimic adding new ones

* refetch projects if we go back to that step from environment step

* make fetching resources more generic

* add/update comments
  • Loading branch information
k3llymariee authored Mar 18, 2024
1 parent c4dc7d0 commit fbc9d0f
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 35 deletions.
81 changes: 60 additions & 21 deletions internal/setup/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ type project struct {
func (p project) FilterValue() string { return "" }

type projectModel struct {
choice string
err error
list list.Model
choice string
err error
list list.Model
showInput bool
textInput tea.Model
}

func NewProject() tea.Model {
Expand All @@ -48,8 +50,31 @@ func (p projectModel) Init() tea.Cmd {

func (m projectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
// if we've selected the option to create a new project, delegate to the textInput model
if m.showInput {
m.textInput, cmd = m.textInput.Update(msg)

// catch the enter key here to update the projectModel when a final value is provided
switch msg := msg.(type) {
case tea.KeyMsg:
switch {
case key.Matches(msg, keys.Enter):
iModel, ok := m.textInput.(inputModel)
if ok {
m.choice = iModel.textInput.Value()
m.showInput = false
}

// TODO: send request to create project, hardcoding for now
projects = append(projects, project{Key: m.choice, Name: m.choice})
}
default:

}
return m, cmd
}
switch msg := msg.(type) {
case fetchProjects:
case fetchResources:
projects, err := getProjects()
if err != nil {
m.err = err
Expand All @@ -59,9 +84,16 @@ func (m projectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyMsg:
switch {
case key.Matches(msg, keys.Enter):

i, ok := m.list.SelectedItem().(project)
if ok {
m.choice = i.Key
if i.Key == "create-new-project" {
iModel := newTextInputModel("desired-proj-key", "Enter project name")
m.textInput = iModel
m.showInput = true
} else {
m.choice = i.Key
}
}
case key.Matches(msg, keys.Quit):
return m, tea.Quit
Expand All @@ -74,6 +106,10 @@ func (m projectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

func (m projectModel) View() string {
if m.showInput {
return m.textInput.View()
}

return "\n" + m.list.View()
}

Expand Down Expand Up @@ -110,27 +146,30 @@ func projectsToItems(projects []project) []list.Item {
return items
}

type fetchProjects struct{}

// type projectsResponse struct {
// Items []project `json:"items"`
// }

var projects = []project{
{
Key: "proj1",
Name: "project 1",
},
{
Key: "proj2",
Name: "project 2",
},
{
Key: "proj3",
Name: "project 3",
},
}

func getProjects() ([]project, error) {
return []project{
{
Key: "proj1",
Name: "project 1",
},
{
Key: "proj2",
Name: "project 2",
},
{
Key: "proj3",
Name: "project 3",
},
}, nil
projectList := projects
createNewOption := project{Key: "create-new-project", Name: "Create a new project"}
projectList = append(projectList, createNewOption)
return projectList, nil

// uncomment out below to fetch projects locally after adding an access token to the
// Authorization header
Expand Down
62 changes: 62 additions & 0 deletions internal/setup/text_input.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package setup

import (
"fmt"

"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
)

type inputModel struct {
textInput textinput.Model
done bool
title string
err error
}

func newTextInputModel(placeholder, title string) inputModel {
ti := textinput.New()
ti.Placeholder = placeholder
ti.Focus()
ti.CharLimit = 156
ti.Width = 20

return inputModel{
title: title,
textInput: ti,
err: nil,
}
}

func (m inputModel) Init() tea.Cmd {
return textinput.Blink
}

func (m inputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd

switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.Type {
case tea.KeyEnter:
m.done = true
return m, nil
case tea.KeyCtrlC, tea.KeyEsc:
return m, tea.Quit
}

// TODO: Handle errors
}

m.textInput, cmd = m.textInput.Update(msg)
return m, cmd
}

func (m inputModel) View() string {
return fmt.Sprintf(
"%s\n\n%s\n\n%s",
m.title,
m.textInput.View(),
"(esc to quit)",
) + "\n"
}
29 changes: 15 additions & 14 deletions internal/setup/wizard.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import (
// TODO: we may want to rename this for clarity
type sessionState int

// generic message type to pass into each models' Update method when we want to perform a new GET request
type fetchResources struct{}

// list of steps in the wizard
const (
autoCreateStep sessionState = iota
Expand Down Expand Up @@ -82,28 +85,24 @@ func (m WizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.currFlagKey = "setup-wizard-flag"
m.currStep = flagsStep + 1
} else {
projModel, _ := m.steps[projectsStep].Update(fetchProjects{})
// we need to cast this to get the data out of it, but maybe we can create our own interface with
// common values such as Choice() and Err() so we don't have to cast
p, ok := projModel.(projectModel)
if ok {
if p.err != nil {
m.err = p.err
return m, nil
}
}
// update projModel with the fetched projects
m.steps[projectsStep] = projModel
// go to the next step
// pre-load projects
m.steps[projectsStep], _ = m.steps[projectsStep].Update(fetchResources{})
m.currStep += 1
}
}
case projectsStep:
projModel, _ := m.steps[projectsStep].Update(msg)
// we need to cast this to get the data out of it, but maybe we can create our own interface with
// common values such as Choice() and Err() so we don't have to cast
p, ok := projModel.(projectModel)
if ok {
m.currProjectKey = p.choice
m.currStep += 1
// update projModel with new input model
m.steps[projectsStep] = p
// only progress if we don't want to show input
if !p.showInput {
m.currStep += 1
}
}
case environmentsStep:
envModel, _ := m.steps[environmentsStep].Update(msg)
Expand Down Expand Up @@ -137,6 +136,8 @@ func (m WizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
// only go back if not on the first step
if m.currStep > autoCreateStep {
// fetch resources for the previous step again in case we created new ones
m.steps[m.currStep-1], _ = m.steps[m.currStep-1].Update(fetchResources{})
m.currStep -= 1
}
case key.Matches(msg, keys.Quit):
Expand Down

0 comments on commit fbc9d0f

Please sign in to comment.