From 56cf60e071f32c5a2e559518acbfd08736b977e7 Mon Sep 17 00:00:00 2001 From: Slug-Boi Date: Fri, 8 Nov 2024 16:19:02 +0100 Subject: [PATCH] feat: add groups and start refactor of tree model for tui elements --- src/cmd/tui/tui_author.go | 75 ++++++++++++++++--- src/cmd/tui/tui_groups.go | 148 +++++++++++++++++++++++++++++++++++++ src/cmd/tui/tui_list.go | 38 ++++++++-- src/cmd/utils/user_util.go | 4 +- 4 files changed, 246 insertions(+), 19 deletions(-) create mode 100644 src/cmd/tui/tui_groups.go diff --git a/src/cmd/tui/tui_author.go b/src/cmd/tui/tui_author.go index 8434e0f..858669a 100644 --- a/src/cmd/tui/tui_author.go +++ b/src/cmd/tui/tui_author.go @@ -37,7 +37,11 @@ type model_ca struct { exclude bool } -func createAuthorModel() model_ca { +var parent_m *model + +func createAuthorModel(old_m *model) model_ca { + parent_m = old_m + m := model_ca{ inputs: make([]textinput.Model, 5), } @@ -100,11 +104,13 @@ func tempAuthorModel() model_ca { } func initialModel(model string) model_ca { - if model == "author" { - return createAuthorModel() - } else { - return tempAuthorModel() - } + // if model == "author" { + // return createAuthorModel() + // } else { + // return tempAuthorModel() + // } + + return tempAuthorModel() } @@ -118,18 +124,18 @@ func (m model_ca) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg.String() { case "ctrl+c", "esc": m.inputs = nil - return m, tea.Quit + return nil, nil // Set focus to next input case "tab", "shift+tab", "enter", "up", "down": s := msg.String() - // Did the user press enter while the submit button was focused? // If so, exit. if !removeButton { if s == "enter" && m.focusIndex == len(m.inputs)+1 { m.quitting = true - return m, tea.Quit + m.AddAuthor() + return model{list: parent_m.list}, tea.ClearScreen } else if s == "enter" && m.focusIndex == len(m.inputs) { // toggle exclude m.exclude = !m.exclude @@ -138,7 +144,7 @@ func (m model_ca) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } else { if s == "enter" && m.focusIndex == len(m.inputs) { m.quitting = true - return m, tea.Quit + return nil, nil } } @@ -238,6 +244,53 @@ func (m model_ca) View() string { return b.String() } +func (m *model_ca) AddAuthor() { + if len(m.inputs) > 0 && + m.inputs[0].Value() != "" && + m.inputs[1].Value() != "" && + m.inputs[2].Value() != "" && + m.inputs[3].Value() != "" { + author_file := utils.Find_authorfile() + f, err := os.OpenFile(author_file, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) + if err != nil { + panic(err) + } + + defer f.Close() + + sb := strings.Builder{} + sb.WriteRune('\n') + + sb.WriteString(fmt.Sprintf("%s|%s|%s|%s", + m.inputs[0].Value(), + m.inputs[1].Value(), + m.inputs[2].Value(), + m.inputs[3].Value())) + + if m.exclude { + sb.WriteString(fmt.Sprintf("|%s", "ex")) + } + + if m.inputs[4].Value() != "" { + sb.WriteString(fmt.Sprintf(";;%s", m.inputs[4].Value())) + } + + //sb.WriteRune('\n') + + if _, err = f.WriteString(sb.String()); err != nil { + panic(err) + } + utils.Define_users(utils.Find_authorfile()) + + author := m.inputs[0].Value() + + item_str := utils.Users[author].Username + " - " + utils.Users[author].Email + dupProtect[item_str] = author + parent_m.list.InsertItem(len(parent_m.list.Items())+1, item(item_str)) + + } +} + func Entry_CA() string { m, err := tea.NewProgram(initialModel("author")).Run() if err != nil { @@ -287,6 +340,8 @@ func Entry_CA() string { } func Entry_TA() string { + //old_m = old_m + m, err := tea.NewProgram(initialModel("temp")).Run() if err != nil { fmt.Printf("could not start program: %s\n", err) diff --git a/src/cmd/tui/tui_groups.go b/src/cmd/tui/tui_groups.go new file mode 100644 index 0000000..a44ad7a --- /dev/null +++ b/src/cmd/tui/tui_groups.go @@ -0,0 +1,148 @@ +package tui + +import ( + "log" + "slices" + "strings" + + "github.com/Slug-Boi/cocommit/src/cmd/utils" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +// sessionState is used to track which model is focused + +var ( + modelStyle = lipgloss.NewStyle(). + Width(20). + Height(8). + Align(lipgloss.Center, lipgloss.Center). + BorderStyle(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("241")) + focusedModelStyle = lipgloss.NewStyle(). + Width(20). + Height(8). + Align(lipgloss.Center, lipgloss.Center). + BorderStyle(lipgloss.DoubleBorder()). + BorderForeground(lipgloss.Color("170")) +) + +type mainModel struct { + content []string + index int +} + +func newModel() mainModel { + groups := utils.Groups + + content := []string{} + + for name, users := range groups { + newUser := strings.Builder{} + newUser.WriteString(name + ":\n") + for _, user := range users { + newUser.WriteString(user.Username + "\n") + } + content = append(content, newUser.String()) + } + + slices.Sort(content) + + m := mainModel{content: content} + return m +} + +func (m mainModel) Init() tea.Cmd { + // start the timer and spinner on program start + return nil +} + +func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + //var cmd tea.Cmd + var cmds []tea.Cmd + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "ctrl+c", "q", "esc": + m.content = nil + return nil, nil + case "enter": + var group string + if m.currentFocusedModel() != "" { + group = strings.Split(m.currentFocusedModel(), ":")[0] + } + if group != "" { + for _, sel := range selected { + delete(selected, string(sel)) + } + users := utils.Groups[group] + for k, v := range dupProtect { + if _, ok := selected[k]; !ok { + for _, user := range users { + if strings.Contains(user.Names, v) { + selected[k] = item(k) + } + } + } + } + } + return nil, nil + case "tab", "right": + m.Next() + case "left": + if m.index == 0 { + m.index = len(m.content) - 1 + } else { + m.index-- + } + } + } + return m, tea.Batch(cmds...) +} + +func (m mainModel) View() string { + var s string + var squares []string + for i, c := range m.content { + // uses joinhorizontal to create a grid of squares + if i == m.index { + squares = append(squares, focusedModelStyle.Render(c)) + } else { + squares = append(squares, modelStyle.Render(c)) + } + } + s += lipgloss.JoinHorizontal(lipgloss.Top, squares...) + + s += helpStyle.Render("\ntab/right: focus next • left: focus previous • enter: select group • q/esq: exit\n") + return s +} + +func (m mainModel) currentFocusedModel() string { + if m.index < len(m.content) { + return m.content[m.index] + } + return "" +} + +func (m *mainModel) Next() { + if m.index == len(m.content)-1 { + m.index = 0 + } else { + m.index++ + } +} + +func Entry_GR() string { + p := tea.NewProgram(newModel()) + + m, err := p.Run() + if err != nil { + log.Fatal(err) + } + + if m.(mainModel).currentFocusedModel() != "" { + // returns the group name + return strings.Split(m.(mainModel).currentFocusedModel(), ":")[0] + } + return "" +} diff --git a/src/cmd/tui/tui_list.go b/src/cmd/tui/tui_list.go index 4499d1a..ad27521 100644 --- a/src/cmd/tui/tui_list.go +++ b/src/cmd/tui/tui_list.go @@ -37,6 +37,8 @@ var negation = false var dupProtect = map[string]string{} +var sub_model tea.Model + type listKeyMap struct { selectAll key.Binding negation key.Binding @@ -147,6 +149,17 @@ func toggleNegation() { var deletion bool func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + if sub_model != nil { + var cmd tea.Cmd + sub_model, cmd = sub_model.Update(msg) + if sub_model_mod, ok := sub_model.(model); ok { + m.list = sub_model_mod.list + sub_model = nil + return m, nil + } + return m, cmd + } + switch msg := msg.(type) { case tea.WindowSizeMsg: m.list.SetWidth(msg.Width) @@ -183,7 +196,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case key.Matches(msg, m.keys.groupSelect): - // group code goes here + // TODO: Look into how to select multiple groups + sub_model = newModel() + //group := Entry_GR() + return m, tea.ClearScreen case key.Matches(msg, m.keys.tempAdd): screen.Clear() @@ -202,12 +218,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case key.Matches(msg, m.keys.createAuthor): screen.Clear() screen.MoveTopLeft() - author := Entry_CA() - if author != "" { - item_str := utils.Users[author].Username + " - " + utils.Users[author].Email - dupProtect[item_str] = author - m.list.InsertItem(len(m.list.Items())+1, item(item_str)) - } + sub_model = createAuthorModel(&m) return m, tea.ClearScreen case key.Matches(msg, m.keys.deleteAuthor): if deletion { @@ -224,11 +235,21 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // extra key options switch keypress := msg.String(); keypress { case "q", "ctrl+c", "esc": + if sub_model != nil { + var cmd tea.Cmd + sub_model, cmd = sub_model.Update(msg) + return m, cmd + } m.quitting = true selected = nil return m, tea.Quit case "enter": + if sub_model != nil { + var cmd tea.Cmd + sub_model, cmd = sub_model.Update(msg) + return m, cmd + } m.quitting = true return m, tea.Quit } @@ -241,6 +262,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (m model) View() string { + if sub_model != nil { + return sub_model.View() + } if m.quitting { return "" //quitTextStyle.Render(strings.Join(m.choice, " ")) } diff --git a/src/cmd/utils/user_util.go b/src/cmd/utils/user_util.go index cdfd5af..e5ded0e 100644 --- a/src/cmd/utils/user_util.go +++ b/src/cmd/utils/user_util.go @@ -57,7 +57,8 @@ func Define_users(author_file string) { if info[4] == "ex" { DefExclude = append(DefExclude, info[2]) } - } else if len(group_info) > 0 { + } + if len(group_info) > 0 { // Group assignment for _, group := range group_info { if Groups[group] == nil { @@ -89,4 +90,3 @@ func TempAddUser(username, email string) { Users[username] = usr } -