Add profile management
This commit is contained in:
parent
b27e6fc9f1
commit
30c8dcf3cd
21 changed files with 552 additions and 78 deletions
|
@ -22,7 +22,8 @@ func TestAddInstallation(t *testing.T) {
|
|||
testza.AssertNoError(t, err)
|
||||
|
||||
profileName := "InstallationTest"
|
||||
profile := ctx.Profiles.AddProfile(profileName)
|
||||
profile, err := ctx.Profiles.AddProfile(profileName)
|
||||
testza.AssertNoError(t, err)
|
||||
testza.AssertNoError(t, profile.AddMod("AreaActions", ">=1.6.5"))
|
||||
testza.AssertNoError(t, profile.AddMod("ArmorModules__Modpack_All", ">=1.4.1"))
|
||||
|
||||
|
|
|
@ -12,10 +12,10 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const defaultProfileName = "Default"
|
||||
const DefaultProfileName = "Default"
|
||||
|
||||
var defaultProfile = Profile{
|
||||
Name: defaultProfileName,
|
||||
Name: DefaultProfileName,
|
||||
}
|
||||
|
||||
type ProfilesVersion int
|
||||
|
@ -28,9 +28,9 @@ const (
|
|||
)
|
||||
|
||||
type Profiles struct {
|
||||
Version ProfilesVersion `json:"version"`
|
||||
Profiles []*Profile `json:"profiles"`
|
||||
SelectedProfile string `json:"selected_profile"`
|
||||
Version ProfilesVersion `json:"version"`
|
||||
Profiles map[string]*Profile `json:"profiles"`
|
||||
SelectedProfile string `json:"selected_profile"`
|
||||
}
|
||||
|
||||
type Profile struct {
|
||||
|
@ -66,8 +66,10 @@ func InitProfiles() (*Profiles, error) {
|
|||
}
|
||||
|
||||
emptyProfiles := Profiles{
|
||||
Version: nextProfilesVersion - 1,
|
||||
Profiles: []*Profile{&defaultProfile},
|
||||
Version: nextProfilesVersion - 1,
|
||||
Profiles: map[string]*Profile{
|
||||
DefaultProfileName: &defaultProfile,
|
||||
},
|
||||
}
|
||||
|
||||
if err := emptyProfiles.Save(); err != nil {
|
||||
|
@ -90,12 +92,14 @@ func InitProfiles() (*Profiles, error) {
|
|||
}
|
||||
|
||||
if len(profiles.Profiles) == 0 {
|
||||
profiles.Profiles = []*Profile{&defaultProfile}
|
||||
profiles.SelectedProfile = defaultProfileName
|
||||
profiles.Profiles = map[string]*Profile{
|
||||
DefaultProfileName: &defaultProfile,
|
||||
}
|
||||
profiles.SelectedProfile = DefaultProfileName
|
||||
}
|
||||
|
||||
if profiles.SelectedProfile == "" {
|
||||
profiles.SelectedProfile = profiles.Profiles[0].Name
|
||||
if profiles.SelectedProfile == "" || profiles.Profiles[profiles.SelectedProfile] == nil {
|
||||
profiles.SelectedProfile = DefaultProfileName
|
||||
}
|
||||
|
||||
return &profiles, nil
|
||||
|
@ -125,38 +129,53 @@ func (p *Profiles) Save() error {
|
|||
}
|
||||
|
||||
// AddProfile adds a new profile with the given name to the profiles list.
|
||||
func (p *Profiles) AddProfile(name string) *Profile {
|
||||
profile := &Profile{
|
||||
func (p *Profiles) AddProfile(name string) (*Profile, error) {
|
||||
if _, ok := p.Profiles[name]; ok {
|
||||
return nil, fmt.Errorf("profile with name %s already exists", name)
|
||||
}
|
||||
|
||||
p.Profiles[name] = &Profile{
|
||||
Name: name,
|
||||
}
|
||||
|
||||
p.Profiles = append(p.Profiles, profile)
|
||||
|
||||
return profile
|
||||
return p.Profiles[name], nil
|
||||
}
|
||||
|
||||
// DeleteProfile deletes the profile with the given name.
|
||||
func (p *Profiles) DeleteProfile(name string) {
|
||||
i := 0
|
||||
for _, profile := range p.Profiles {
|
||||
if profile.Name == name {
|
||||
break
|
||||
func (p *Profiles) DeleteProfile(name string) error {
|
||||
if _, ok := p.Profiles[name]; ok {
|
||||
delete(p.Profiles, name)
|
||||
|
||||
if p.SelectedProfile == name {
|
||||
p.SelectedProfile = DefaultProfileName
|
||||
}
|
||||
|
||||
i++
|
||||
return nil
|
||||
}
|
||||
|
||||
if i < len(p.Profiles) {
|
||||
p.Profiles = append(p.Profiles[:i], p.Profiles[i+1:]...)
|
||||
}
|
||||
return fmt.Errorf("profile with name %s does not exist", name)
|
||||
}
|
||||
|
||||
// GetProfile returns the profile with the given name or nil if it doesn't exist.
|
||||
func (p *Profiles) GetProfile(name string) *Profile {
|
||||
for _, profile := range p.Profiles {
|
||||
if profile.Name == name {
|
||||
return profile
|
||||
}
|
||||
return p.Profiles[name]
|
||||
}
|
||||
|
||||
func (p *Profiles) RenameProfile(oldName string, newName string) error {
|
||||
if _, ok := p.Profiles[newName]; ok {
|
||||
return fmt.Errorf("profile with name %s already exists", newName)
|
||||
}
|
||||
|
||||
if _, ok := p.Profiles[oldName]; !ok {
|
||||
return fmt.Errorf("profile with name %s does not exist", oldName)
|
||||
}
|
||||
|
||||
p.Profiles[oldName].Name = newName
|
||||
p.Profiles[newName] = p.Profiles[oldName]
|
||||
delete(p.Profiles, oldName)
|
||||
|
||||
if p.SelectedProfile == oldName {
|
||||
p.SelectedProfile = newName
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -67,6 +67,9 @@ func Execute() {
|
|||
// Execute tea as default
|
||||
cmd, _, err := rootCmd.Find(os.Args[1:])
|
||||
|
||||
// Allow opening via explorer
|
||||
cobra.MousetrapHelpText = ""
|
||||
|
||||
cli := len(os.Args) >= 2 && os.Args[1] == "cli"
|
||||
if (len(os.Args) <= 1 || os.Args[1] != "help") && (err != nil || cmd == rootCmd) {
|
||||
args := append([]string{"cli"}, os.Args[1:]...)
|
||||
|
|
24
tea/root.go
24
tea/root.go
|
@ -12,20 +12,16 @@ import (
|
|||
)
|
||||
|
||||
type rootModel struct {
|
||||
currentProfile *cli.Profile
|
||||
currentInstallation *cli.Installation
|
||||
global *cli.GlobalContext
|
||||
apiClient graphql.Client
|
||||
currentSize tea.WindowSizeMsg
|
||||
headerComponent tea.Model
|
||||
global *cli.GlobalContext
|
||||
apiClient graphql.Client
|
||||
currentSize tea.WindowSizeMsg
|
||||
headerComponent tea.Model
|
||||
}
|
||||
|
||||
func newModel(global *cli.GlobalContext) *rootModel {
|
||||
m := &rootModel{
|
||||
global: global,
|
||||
currentProfile: global.Profiles.GetProfile(global.Profiles.SelectedProfile),
|
||||
currentInstallation: global.Installations.GetInstallation(global.Installations.SelectedInstallation),
|
||||
apiClient: ficsit.InitAPI(),
|
||||
global: global,
|
||||
apiClient: ficsit.InitAPI(),
|
||||
currentSize: tea.WindowSizeMsg{
|
||||
Width: 20,
|
||||
Height: 14,
|
||||
|
@ -38,21 +34,19 @@ func newModel(global *cli.GlobalContext) *rootModel {
|
|||
}
|
||||
|
||||
func (m *rootModel) GetCurrentProfile() *cli.Profile {
|
||||
return m.currentProfile
|
||||
return m.global.Profiles.GetProfile(m.global.Profiles.SelectedProfile)
|
||||
}
|
||||
|
||||
func (m *rootModel) SetCurrentProfile(profile *cli.Profile) error {
|
||||
m.currentProfile = profile
|
||||
m.global.Profiles.SelectedProfile = profile.Name
|
||||
return m.global.Save()
|
||||
}
|
||||
|
||||
func (m *rootModel) GetCurrentInstallation() *cli.Installation {
|
||||
return m.currentInstallation
|
||||
return m.global.Installations.GetInstallation(m.global.Installations.SelectedInstallation)
|
||||
}
|
||||
|
||||
func (m *rootModel) SetCurrentInstallation(installation *cli.Installation) error {
|
||||
m.currentInstallation = installation
|
||||
m.global.Installations.SelectedInstallation = installation.Path
|
||||
return m.global.Save()
|
||||
}
|
||||
|
@ -82,7 +76,7 @@ func (m *rootModel) GetGlobal() *cli.GlobalContext {
|
|||
}
|
||||
|
||||
func RunTea(global *cli.GlobalContext) error {
|
||||
if err := tea.NewProgram(scenes.NewMainMenu(newModel(global))).Start(); err != nil {
|
||||
if err := tea.NewProgram(scenes.NewMainMenu(newModel(global)), tea.WithAltScreen(), tea.WithMouseCellMotion()).Start(); err != nil {
|
||||
return errors.Wrap(err, "internal tea error")
|
||||
}
|
||||
return nil
|
||||
|
|
5
tea/scenes/errors.go
Normal file
5
tea/scenes/errors.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
package scenes
|
||||
|
||||
const (
|
||||
ErrorFailedAddMod = "failed to add mod"
|
||||
)
|
|
@ -1,10 +1,11 @@
|
|||
package scenes
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/bubbles/list"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/utils"
|
||||
|
@ -27,7 +28,11 @@ func NewExitMenu(root components.RootModel) tea.Model {
|
|||
ItemTitle: "Exit Saving Changes",
|
||||
Activate: func(msg tea.Msg, currentModel tea.Model) (tea.Model, tea.Cmd) {
|
||||
if err := root.GetGlobal().Save(); err != nil {
|
||||
panic(err) // TODO
|
||||
message := "failed to save"
|
||||
menu := currentModel.(exitMenu)
|
||||
log.Error().Err(err).Msg(message)
|
||||
cmd := menu.list.NewStatusMessage(message)
|
||||
return currentModel, cmd
|
||||
}
|
||||
return currentModel, tea.Quit
|
||||
},
|
||||
|
@ -47,6 +52,7 @@ func NewExitMenu(root components.RootModel) tea.Model {
|
|||
model.list.Styles = utils.ListStyles
|
||||
model.list.DisableQuitKeybindings()
|
||||
model.list.SetSize(model.list.Width(), model.list.Height())
|
||||
model.list.StatusMessageLifetime = time.Second * 3
|
||||
|
||||
return model
|
||||
}
|
||||
|
@ -56,7 +62,6 @@ func (m exitMenu) Init() tea.Cmd {
|
|||
}
|
||||
|
||||
func (m exitMenu) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
log.Warn().Msg(spew.Sdump(msg))
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch keypress := msg.String(); keypress {
|
||||
|
|
|
@ -3,4 +3,5 @@ package scenes
|
|||
const (
|
||||
KeyControlC = "ctrl+c"
|
||||
KeyEnter = "enter"
|
||||
KeyEscape = "esc"
|
||||
)
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package scenes
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/bubbles/list"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/utils"
|
||||
|
@ -55,7 +56,10 @@ func NewMainMenu(root components.RootModel) tea.Model {
|
|||
ItemTitle: "Save",
|
||||
Activate: func(msg tea.Msg, currentModel tea.Model) (tea.Model, tea.Cmd) {
|
||||
if err := root.GetGlobal().Save(); err != nil {
|
||||
panic(err) // TODO Handle Error
|
||||
menu := currentModel.(exitMenu)
|
||||
log.Error().Err(err).Msg(ErrorFailedAddMod)
|
||||
cmd := menu.list.NewStatusMessage(ErrorFailedAddMod)
|
||||
return currentModel, cmd
|
||||
}
|
||||
return nil, nil
|
||||
},
|
||||
|
@ -75,6 +79,8 @@ func NewMainMenu(root components.RootModel) tea.Model {
|
|||
model.list.Title = "Main Menu"
|
||||
model.list.Styles = utils.ListStyles
|
||||
model.list.SetSize(model.list.Width(), model.list.Height())
|
||||
model.list.StatusMessageLifetime = time.Second * 3
|
||||
model.list.DisableQuitKeybindings()
|
||||
|
||||
return model
|
||||
}
|
||||
|
@ -84,7 +90,6 @@ func (m mainMenu) Init() tea.Cmd {
|
|||
}
|
||||
|
||||
func (m mainMenu) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
log.Warn().Msg(spew.Sdump(msg))
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch keypress := msg.String(); keypress {
|
||||
|
@ -107,7 +112,7 @@ func (m mainMenu) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
return m, nil
|
||||
}
|
||||
}
|
||||
return m, tea.Quit
|
||||
return m, nil
|
||||
default:
|
||||
var cmd tea.Cmd
|
||||
m.list, cmd = m.list.Update(msg)
|
||||
|
|
17
tea/scenes/messages.go
Normal file
17
tea/scenes/messages.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package scenes
|
||||
|
||||
import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
type updateProfileList struct{}
|
||||
|
||||
func updateProfileListCmd() tea.Msg {
|
||||
return updateProfileList{}
|
||||
}
|
||||
|
||||
type updateProfileNames struct{}
|
||||
|
||||
func updateProfileNamesCmd() tea.Msg {
|
||||
return updateProfileNames{}
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
package scenes
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/bubbles/list"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/utils"
|
||||
|
@ -49,7 +50,10 @@ func NewModMenu(root components.RootModel, parent tea.Model, mod utils.Mod) tea.
|
|||
Activate: func(msg tea.Msg, currentModel tea.Model) (tea.Model, tea.Cmd) {
|
||||
err := root.GetCurrentProfile().AddMod(mod.Reference, ">=0.0.0")
|
||||
if err != nil {
|
||||
panic(err) // TODO Handle Error
|
||||
menu := currentModel.(exitMenu)
|
||||
log.Error().Err(err).Msg(ErrorFailedAddMod)
|
||||
cmd := menu.list.NewStatusMessage(ErrorFailedAddMod)
|
||||
return currentModel, cmd
|
||||
}
|
||||
return currentModel.(modMenu).parent, nil
|
||||
},
|
||||
|
@ -79,6 +83,8 @@ func NewModMenu(root components.RootModel, parent tea.Model, mod utils.Mod) tea.
|
|||
model.list.Styles = utils.ListStyles
|
||||
model.list.SetSize(model.list.Width(), model.list.Height())
|
||||
model.list.KeyMap.Quit.SetHelp("q", "back")
|
||||
model.list.StatusMessageLifetime = time.Second * 3
|
||||
model.list.DisableQuitKeybindings()
|
||||
|
||||
return model
|
||||
}
|
||||
|
@ -88,7 +94,6 @@ func (m modMenu) Init() tea.Cmd {
|
|||
}
|
||||
|
||||
func (m modMenu) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
log.Warn().Msg(spew.Sdump(msg))
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch keypress := msg.String(); keypress {
|
||||
|
@ -115,7 +120,7 @@ func (m modMenu) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
return m, nil
|
||||
}
|
||||
}
|
||||
return m, tea.Quit
|
||||
return m, nil
|
||||
default:
|
||||
var cmd tea.Cmd
|
||||
m.list, cmd = m.list.Update(msg)
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
|
||||
md "github.com/JohannesKaufmann/html-to-markdown"
|
||||
|
@ -185,12 +187,14 @@ func (m modInfo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
|
||||
markdownDescription, err := converter.ConvertString(mod.Full_description)
|
||||
if err != nil {
|
||||
panic(err) // TODO Handle Error
|
||||
log.Error().Err(err).Msg("failed to convert html to markdown")
|
||||
markdownDescription = mod.Full_description
|
||||
}
|
||||
|
||||
description, err := glamour.Render(markdownDescription, "dark")
|
||||
if err != nil {
|
||||
panic(err) // TODO Handle Error
|
||||
log.Error().Err(err).Msg("failed to render markdown")
|
||||
description = mod.Full_description
|
||||
}
|
||||
|
||||
bottomPart := lipgloss.JoinHorizontal(lipgloss.Top, sidebar, strings.TrimSpace(description))
|
||||
|
|
|
@ -4,8 +4,6 @@ import (
|
|||
"github.com/charmbracelet/bubbles/textinput"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/utils"
|
||||
)
|
||||
|
@ -41,15 +39,13 @@ func (m modSemver) Init() tea.Cmd {
|
|||
}
|
||||
|
||||
func (m modSemver) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
log.Warn().Msg(spew.Sdump(msg))
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch keypress := msg.String(); keypress {
|
||||
case KeyControlC:
|
||||
return m, tea.Quit
|
||||
case "q":
|
||||
newModel := NewExitMenu(m.root)
|
||||
return newModel, newModel.Init()
|
||||
case KeyEscape:
|
||||
return m.parent, nil
|
||||
case KeyEnter:
|
||||
err := m.root.GetCurrentProfile().AddMod(m.mod.Reference, m.input.Value())
|
||||
if err != nil {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package scenes
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/bubbles/list"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/utils"
|
||||
|
@ -48,7 +49,10 @@ func NewModVersion(root components.RootModel, parent tea.Model, mod utils.Mod) t
|
|||
Activate: func(msg tea.Msg, currentModel tea.Model) (tea.Model, tea.Cmd) {
|
||||
err := root.GetCurrentProfile().AddMod(mod.Reference, ">=0.0.0")
|
||||
if err != nil {
|
||||
panic(err) // TODO Handle Error
|
||||
menu := currentModel.(exitMenu)
|
||||
log.Error().Err(err).Msg(ErrorFailedAddMod)
|
||||
cmd := menu.list.NewStatusMessage(ErrorFailedAddMod)
|
||||
return currentModel, cmd
|
||||
}
|
||||
return currentModel.(modMenu).parent, nil
|
||||
},
|
||||
|
@ -63,6 +67,8 @@ func NewModVersion(root components.RootModel, parent tea.Model, mod utils.Mod) t
|
|||
model.list.Styles = utils.ListStyles
|
||||
model.list.SetSize(model.list.Width(), model.list.Height())
|
||||
model.list.KeyMap.Quit.SetHelp("q", "back")
|
||||
model.list.StatusMessageLifetime = time.Second * 3
|
||||
model.list.DisableQuitKeybindings()
|
||||
|
||||
return model
|
||||
}
|
||||
|
@ -72,7 +78,6 @@ func (m modVersionMenu) Init() tea.Cmd {
|
|||
}
|
||||
|
||||
func (m modVersionMenu) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
log.Warn().Msg(spew.Sdump(msg))
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch keypress := msg.String(); keypress {
|
||||
|
@ -99,7 +104,7 @@ func (m modVersionMenu) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
return m, nil
|
||||
}
|
||||
}
|
||||
return m, tea.Quit
|
||||
return m, nil
|
||||
default:
|
||||
var cmd tea.Cmd
|
||||
m.list, cmd = m.list.Update(msg)
|
||||
|
|
|
@ -10,8 +10,6 @@ import (
|
|||
"github.com/charmbracelet/bubbles/spinner"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/satisfactorymodding/ficsit-cli/ficsit"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/utils"
|
||||
|
@ -55,6 +53,8 @@ func NewMods(root components.RootModel, parent tea.Model) tea.Model {
|
|||
l.Styles = utils.ListStyles
|
||||
l.SetSize(l.Width(), l.Height())
|
||||
l.KeyMap.Quit.SetHelp("q", "back")
|
||||
l.DisableQuitKeybindings()
|
||||
|
||||
l.AdditionalShortHelpKeys = func() []key.Binding {
|
||||
return []key.Binding{
|
||||
key.NewBinding(key.WithHelp("s", "sort")),
|
||||
|
@ -62,6 +62,13 @@ func NewMods(root components.RootModel, parent tea.Model) tea.Model {
|
|||
}
|
||||
}
|
||||
|
||||
l.AdditionalFullHelpKeys = func() []key.Binding {
|
||||
return []key.Binding{
|
||||
key.NewBinding(key.WithHelp("s", "sort")),
|
||||
key.NewBinding(key.WithHelp("o", "order")),
|
||||
}
|
||||
}
|
||||
|
||||
sortFieldList := list.NewModel([]list.Item{
|
||||
utils.SimpleItem{
|
||||
ItemTitle: "Name",
|
||||
|
@ -101,6 +108,7 @@ func NewMods(root components.RootModel, parent tea.Model) tea.Model {
|
|||
sortFieldList.Styles = utils.ListStyles
|
||||
sortFieldList.SetSize(l.Width(), l.Height())
|
||||
sortFieldList.KeyMap.Quit.SetHelp("q", "back")
|
||||
sortFieldList.DisableQuitKeybindings()
|
||||
|
||||
sortOrderList := list.NewModel([]list.Item{
|
||||
utils.SimpleItem{
|
||||
|
@ -131,6 +139,7 @@ func NewMods(root components.RootModel, parent tea.Model) tea.Model {
|
|||
sortOrderList.Styles = utils.ListStyles
|
||||
sortOrderList.SetSize(l.Width(), l.Height())
|
||||
sortOrderList.KeyMap.Quit.SetHelp("q", "back")
|
||||
sortOrderList.DisableQuitKeybindings()
|
||||
|
||||
m := &modsList{
|
||||
root: root,
|
||||
|
@ -196,7 +205,9 @@ func (m modsList) Init() tea.Cmd {
|
|||
}
|
||||
|
||||
func (m modsList) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
log.Info().Msg(spew.Sdump(msg))
|
||||
// List enables its own keybindings when they were previously disabled
|
||||
m.list.DisableQuitKeybindings()
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
if m.list.SettingFilter() {
|
||||
|
|
67
tea/scenes/new_profile.go
Normal file
67
tea/scenes/new_profile.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package scenes
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/utils"
|
||||
)
|
||||
|
||||
var _ tea.Model = (*newProfile)(nil)
|
||||
|
||||
type newProfile struct {
|
||||
root components.RootModel
|
||||
parent tea.Model
|
||||
input textinput.Model
|
||||
title string
|
||||
}
|
||||
|
||||
func NewNewProfile(root components.RootModel, parent tea.Model) tea.Model {
|
||||
model := newProfile{
|
||||
root: root,
|
||||
parent: parent,
|
||||
input: textinput.NewModel(),
|
||||
title: utils.NonListTitleStyle.Render("New Profile"),
|
||||
}
|
||||
|
||||
model.input.Focus()
|
||||
model.input.Width = root.Size().Width
|
||||
|
||||
return model
|
||||
}
|
||||
|
||||
func (m newProfile) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m newProfile) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch keypress := msg.String(); keypress {
|
||||
case KeyControlC:
|
||||
return m, tea.Quit
|
||||
case KeyEscape:
|
||||
return m.parent, nil
|
||||
case KeyEnter:
|
||||
if _, err := m.root.GetGlobal().Profiles.AddProfile(m.input.Value()); err != nil {
|
||||
panic(err) // TODO Handle Error
|
||||
}
|
||||
|
||||
return m.parent, updateProfileListCmd
|
||||
default:
|
||||
var cmd tea.Cmd
|
||||
m.input, cmd = m.input.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
case tea.WindowSizeMsg:
|
||||
m.root.SetSize(msg)
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m newProfile) View() string {
|
||||
inputView := lipgloss.NewStyle().Padding(1, 2).Render(m.input.View())
|
||||
return lipgloss.JoinVertical(lipgloss.Left, m.root.View(), m.title, inputView)
|
||||
}
|
134
tea/scenes/profile.go
Normal file
134
tea/scenes/profile.go
Normal file
|
@ -0,0 +1,134 @@
|
|||
package scenes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/bubbles/list"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/satisfactorymodding/ficsit-cli/cli"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/utils"
|
||||
)
|
||||
|
||||
var _ tea.Model = (*profile)(nil)
|
||||
|
||||
type profile struct {
|
||||
root components.RootModel
|
||||
list list.Model
|
||||
parent tea.Model
|
||||
profile *cli.Profile
|
||||
hadRenamed bool
|
||||
}
|
||||
|
||||
func NewProfile(root components.RootModel, parent tea.Model, profileData *cli.Profile) tea.Model {
|
||||
model := profile{
|
||||
root: root,
|
||||
parent: parent,
|
||||
profile: profileData,
|
||||
}
|
||||
|
||||
items := []list.Item{
|
||||
utils.SimpleItem{
|
||||
ItemTitle: "Select",
|
||||
Activate: func(msg tea.Msg, currentModel tea.Model) (tea.Model, tea.Cmd) {
|
||||
if err := root.SetCurrentProfile(profileData); err != nil {
|
||||
panic(err) // TODO Handle Error
|
||||
}
|
||||
|
||||
return currentModel.(profile).parent, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if profileData.Name != cli.DefaultProfileName {
|
||||
items = append(items,
|
||||
utils.SimpleItem{
|
||||
ItemTitle: "Rename",
|
||||
Activate: func(msg tea.Msg, currentModel tea.Model) (tea.Model, tea.Cmd) {
|
||||
newModel := NewRenameProfile(root, currentModel, profileData)
|
||||
return newModel, newModel.Init()
|
||||
},
|
||||
},
|
||||
utils.SimpleItem{
|
||||
ItemTitle: "Delete",
|
||||
Activate: func(msg tea.Msg, currentModel tea.Model) (tea.Model, tea.Cmd) {
|
||||
if err := root.GetGlobal().Profiles.DeleteProfile(profileData.Name); err != nil {
|
||||
panic(err) // TODO Handle Error
|
||||
}
|
||||
|
||||
return currentModel.(profile).parent, updateProfileListCmd
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
model.list = list.NewModel(items, utils.NewItemDelegate(), root.Size().Width, root.Size().Height-root.Height())
|
||||
model.list.SetShowStatusBar(false)
|
||||
model.list.SetFilteringEnabled(false)
|
||||
model.list.Title = fmt.Sprintf("Profile: %s", profileData.Name)
|
||||
model.list.Styles = utils.ListStyles
|
||||
model.list.SetSize(model.list.Width(), model.list.Height())
|
||||
model.list.StatusMessageLifetime = time.Second * 3
|
||||
model.list.DisableQuitKeybindings()
|
||||
|
||||
return model
|
||||
}
|
||||
|
||||
func (m profile) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m profile) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch keypress := msg.String(); keypress {
|
||||
case KeyControlC:
|
||||
return m, tea.Quit
|
||||
case "q":
|
||||
if m.parent != nil {
|
||||
m.parent.Update(m.root.Size())
|
||||
|
||||
if m.hadRenamed {
|
||||
return m.parent, updateProfileNamesCmd
|
||||
}
|
||||
|
||||
return m.parent, nil
|
||||
}
|
||||
return m, nil
|
||||
case KeyEnter:
|
||||
i, ok := m.list.SelectedItem().(utils.SimpleItem)
|
||||
if ok {
|
||||
if i.Activate != nil {
|
||||
newModel, cmd := i.Activate(msg, m)
|
||||
if newModel != nil || cmd != nil {
|
||||
if newModel == nil {
|
||||
newModel = m
|
||||
}
|
||||
return newModel, cmd
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
default:
|
||||
var cmd tea.Cmd
|
||||
m.list, cmd = m.list.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
case tea.WindowSizeMsg:
|
||||
top, right, bottom, left := lipgloss.NewStyle().Margin(2, 2).GetMargin()
|
||||
m.list.SetSize(msg.Width-left-right, msg.Height-top-bottom)
|
||||
m.root.SetSize(msg)
|
||||
case updateProfileNames:
|
||||
m.hadRenamed = true
|
||||
m.list.Title = fmt.Sprintf("Profile: %s", m.profile.Name)
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m profile) View() string {
|
||||
return lipgloss.JoinVertical(lipgloss.Left, m.root.View(), m.list.View())
|
||||
}
|
|
@ -1,10 +1,140 @@
|
|||
package scenes
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
"github.com/charmbracelet/bubbles/list"
|
||||
"github.com/charmbracelet/bubbles/spinner"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/utils"
|
||||
)
|
||||
|
||||
var _ tea.Model = (*profiles)(nil)
|
||||
|
||||
type profiles struct {
|
||||
root components.RootModel
|
||||
list list.Model
|
||||
parent tea.Model
|
||||
}
|
||||
|
||||
func NewProfiles(root components.RootModel, parent tea.Model) tea.Model {
|
||||
l := list.NewModel(profilesToList(root), utils.NewItemDelegate(), root.Size().Width, root.Size().Height-root.Height())
|
||||
l.SetShowStatusBar(true)
|
||||
l.SetFilteringEnabled(true)
|
||||
l.SetSpinner(spinner.MiniDot)
|
||||
l.Title = "Profiles"
|
||||
l.Styles = utils.ListStyles
|
||||
l.SetSize(l.Width(), l.Height())
|
||||
l.KeyMap.Quit.SetHelp("q", "back")
|
||||
l.DisableQuitKeybindings()
|
||||
|
||||
l.AdditionalShortHelpKeys = func() []key.Binding {
|
||||
return []key.Binding{
|
||||
key.NewBinding(key.WithHelp("n", "new profile")),
|
||||
}
|
||||
}
|
||||
|
||||
l.AdditionalFullHelpKeys = func() []key.Binding {
|
||||
return []key.Binding{
|
||||
key.NewBinding(key.WithHelp("n", "new profile")),
|
||||
}
|
||||
}
|
||||
|
||||
return &profiles{
|
||||
root: root,
|
||||
list: l,
|
||||
parent: parent,
|
||||
}
|
||||
}
|
||||
|
||||
func (m profiles) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m profiles) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// List enables its own keybindings when they were previously disabled
|
||||
m.list.DisableQuitKeybindings()
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
if m.list.SettingFilter() {
|
||||
var cmd tea.Cmd
|
||||
m.list, cmd = m.list.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
switch keypress := msg.String(); keypress {
|
||||
case "n":
|
||||
newModel := NewNewProfile(m.root, m)
|
||||
return newModel, newModel.Init()
|
||||
case KeyControlC:
|
||||
return m, tea.Quit
|
||||
case "q":
|
||||
if m.parent != nil {
|
||||
m.parent.Update(m.root.Size())
|
||||
return m.parent, nil
|
||||
}
|
||||
return m, tea.Quit
|
||||
case KeyEnter:
|
||||
i, ok := m.list.SelectedItem().(utils.SimpleItem)
|
||||
if ok {
|
||||
if i.Activate != nil {
|
||||
newModel, cmd := i.Activate(msg, m)
|
||||
if newModel != nil || cmd != nil {
|
||||
if newModel == nil {
|
||||
newModel = m
|
||||
}
|
||||
return newModel, cmd
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
case tea.WindowSizeMsg:
|
||||
top, right, bottom, left := lipgloss.NewStyle().Margin(m.root.Height(), 2, 0).GetMargin()
|
||||
m.list.SetSize(msg.Width-left-right, msg.Height-top-bottom)
|
||||
m.root.SetSize(msg)
|
||||
case updateProfileList:
|
||||
m.list.ResetSelected()
|
||||
cmd := m.list.SetItems(profilesToList(m.root))
|
||||
|
||||
// Done to refresh keymap
|
||||
m.list.SetFilteringEnabled(m.list.FilteringEnabled())
|
||||
return m, cmd
|
||||
case updateProfileNames:
|
||||
cmd := m.list.SetItems(profilesToList(m.root))
|
||||
|
||||
// Done to refresh keymap
|
||||
m.list.SetFilteringEnabled(m.list.FilteringEnabled())
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
var cmd tea.Cmd
|
||||
m.list, cmd = m.list.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m profiles) View() string {
|
||||
return lipgloss.JoinVertical(lipgloss.Left, m.root.View(), m.list.View())
|
||||
}
|
||||
|
||||
func profilesToList(root components.RootModel) []list.Item {
|
||||
items := make([]list.Item, len(root.GetGlobal().Profiles.Profiles))
|
||||
|
||||
i := 0
|
||||
for _, profile := range root.GetGlobal().Profiles.Profiles {
|
||||
temp := profile
|
||||
items[i] = utils.SimpleItem{
|
||||
ItemTitle: temp.Name,
|
||||
Activate: func(msg tea.Msg, currentModel tea.Model) (tea.Model, tea.Cmd) {
|
||||
newModel := NewProfile(root, currentModel, temp)
|
||||
return newModel, newModel.Init()
|
||||
},
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
|
73
tea/scenes/rename_profile.go
Normal file
73
tea/scenes/rename_profile.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
package scenes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/satisfactorymodding/ficsit-cli/cli"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/utils"
|
||||
)
|
||||
|
||||
var _ tea.Model = (*renameProfile)(nil)
|
||||
|
||||
type renameProfile struct {
|
||||
root components.RootModel
|
||||
parent tea.Model
|
||||
input textinput.Model
|
||||
title string
|
||||
oldName string
|
||||
}
|
||||
|
||||
func NewRenameProfile(root components.RootModel, parent tea.Model, profileData *cli.Profile) tea.Model {
|
||||
model := renameProfile{
|
||||
root: root,
|
||||
parent: parent,
|
||||
input: textinput.NewModel(),
|
||||
title: utils.NonListTitleStyle.Render(fmt.Sprintf("Rename Profile: %s", profileData.Name)),
|
||||
oldName: profileData.Name,
|
||||
}
|
||||
|
||||
model.input.SetValue(profileData.Name)
|
||||
model.input.Focus()
|
||||
model.input.Width = root.Size().Width
|
||||
|
||||
return model
|
||||
}
|
||||
|
||||
func (m renameProfile) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m renameProfile) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch keypress := msg.String(); keypress {
|
||||
case KeyControlC:
|
||||
return m, tea.Quit
|
||||
case KeyEscape:
|
||||
return m.parent, nil
|
||||
case KeyEnter:
|
||||
if err := m.root.GetGlobal().Profiles.RenameProfile(m.oldName, m.input.Value()); err != nil {
|
||||
panic(err) // TODO Handle Error
|
||||
}
|
||||
|
||||
return m.parent, updateProfileNamesCmd
|
||||
default:
|
||||
var cmd tea.Cmd
|
||||
m.input, cmd = m.input.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
case tea.WindowSizeMsg:
|
||||
m.root.SetSize(msg)
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m renameProfile) View() string {
|
||||
inputView := lipgloss.NewStyle().Padding(1, 2).Render(m.input.View())
|
||||
return lipgloss.JoinVertical(lipgloss.Left, m.root.View(), m.title, inputView)
|
||||
}
|
|
@ -8,8 +8,6 @@ import (
|
|||
"github.com/charmbracelet/bubbles/spinner"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/satisfactorymodding/ficsit-cli/ficsit"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||
"github.com/satisfactorymodding/ficsit-cli/tea/utils"
|
||||
|
@ -33,6 +31,7 @@ func NewModVersionList(root components.RootModel, parent tea.Model, mod utils.Mo
|
|||
l.Styles = utils.ListStyles
|
||||
l.SetSize(l.Width(), l.Height())
|
||||
l.KeyMap.Quit.SetHelp("q", "back")
|
||||
l.DisableQuitKeybindings()
|
||||
|
||||
m := &selectModVersionList{
|
||||
root: root,
|
||||
|
@ -93,7 +92,6 @@ func (m selectModVersionList) Init() tea.Cmd {
|
|||
}
|
||||
|
||||
func (m selectModVersionList) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
log.Info().Msg(spew.Sdump(msg))
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch keypress := msg.String(); keypress {
|
||||
|
@ -119,7 +117,7 @@ func (m selectModVersionList) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
return m, nil
|
||||
}
|
||||
}
|
||||
return m, tea.Quit
|
||||
return m, nil
|
||||
default:
|
||||
var cmd tea.Cmd
|
||||
m.list, cmd = m.list.Update(msg)
|
||||
|
|
|
@ -6,9 +6,10 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
ListStyles list.Styles
|
||||
LabelStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("202"))
|
||||
TitleStyle = list.DefaultStyles().Title.Background(lipgloss.Color("22"))
|
||||
ListStyles list.Styles
|
||||
LabelStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("202"))
|
||||
TitleStyle = list.DefaultStyles().Title.Background(lipgloss.Color("22"))
|
||||
NonListTitleStyle = TitleStyle.Copy().MarginLeft(2).Background(lipgloss.Color("22"))
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
Loading…
Reference in a new issue