|
@@ -7,6 +7,7 @@ package issues
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
"fmt"
|
|
"fmt"
|
|
|
|
|
+ "os"
|
|
|
"path/filepath"
|
|
"path/filepath"
|
|
|
"strings"
|
|
"strings"
|
|
|
|
|
|
|
@@ -70,6 +71,13 @@ type Model struct {
|
|
|
width int
|
|
width int
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// update signal types
|
|
|
|
|
+type (
|
|
|
|
|
+ validateMsg bool
|
|
|
|
|
+ deleteResult string
|
|
|
|
|
+ deletePath string
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
// The bubbletea init function
|
|
// The bubbletea init function
|
|
|
func (m Model) Init() tea.Cmd { return m.load }
|
|
func (m Model) Init() tea.Cmd { return m.load }
|
|
|
|
|
|
|
@@ -132,6 +140,15 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
|
i.Width = 30
|
|
i.Width = 30
|
|
|
wg.input = i
|
|
wg.input = i
|
|
|
cmds = append(cmds, func() tea.Msg { return wg })
|
|
cmds = append(cmds, func() tea.Msg { return wg })
|
|
|
|
|
+ case deletePath:
|
|
|
|
|
+ wg := confirmDelete{Path: string(msg), prompt: "Delete", validateString: "yes"}
|
|
|
|
|
+ i := textinput.New()
|
|
|
|
|
+ i.Placeholder = "yes"
|
|
|
|
|
+ i.Focus()
|
|
|
|
|
+ i.CharLimit = 80
|
|
|
|
|
+ i.Width = 30
|
|
|
|
|
+ wg.input = i
|
|
|
|
|
+ cmds = append(cmds, func() tea.Msg { return wg })
|
|
|
case string:
|
|
case string:
|
|
|
m.content = msg
|
|
m.content = msg
|
|
|
m.viewport.SetContent(m.content)
|
|
m.viewport.SetContent(m.content)
|
|
@@ -153,6 +170,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
|
case setTitle:
|
|
case setTitle:
|
|
|
m.widget, cmd = w.update(msg)
|
|
m.widget, cmd = w.update(msg)
|
|
|
cmds = append(cmds, cmd)
|
|
cmds = append(cmds, cmd)
|
|
|
|
|
+ case confirmDelete:
|
|
|
|
|
+ m.widget, cmd = w.update(msg)
|
|
|
|
|
+ cmds = append(cmds, cmd)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return m, tea.Batch(cmds...)
|
|
return m, tea.Batch(cmds...)
|
|
@@ -717,10 +737,13 @@ func (ic IssueCollection) update(msg tea.Msg) (widget, tea.Cmd) {
|
|
|
ic.Path = ic.Collection[ic.selection].Path
|
|
ic.Path = ic.Collection[ic.selection].Path
|
|
|
return ic, ic.sendLoad
|
|
return ic, ic.sendLoad
|
|
|
case "c":
|
|
case "c":
|
|
|
- return ic, ic.newIssueInCollection
|
|
|
|
|
|
|
+ return ic, func() tea.Msg { return setTitleMsg(ic.Path) }
|
|
|
case "e":
|
|
case "e":
|
|
|
ic.Path = ic.Collection[ic.selection].Path
|
|
ic.Path = ic.Collection[ic.selection].Path
|
|
|
return ic, ic.edit
|
|
return ic, ic.edit
|
|
|
|
|
+ case "d":
|
|
|
|
|
+ ic.Path = ic.Collection[ic.selection].Path
|
|
|
|
|
+ return ic, func() tea.Msg { return deletePath(ic.Path) }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -729,8 +752,6 @@ func (ic IssueCollection) update(msg tea.Msg) (widget, tea.Cmd) {
|
|
|
|
|
|
|
|
func (ic IssueCollection) sendLoad() tea.Msg { return loadPath(ic.Path) }
|
|
func (ic IssueCollection) sendLoad() tea.Msg { return loadPath(ic.Path) }
|
|
|
|
|
|
|
|
-func (ic IssueCollection) newIssueInCollection() tea.Msg { return setTitleMsg(ic.Path) }
|
|
|
|
|
-
|
|
|
|
|
func (ic IssueCollection) edit() tea.Msg { return newEditWidget(ic.Path) }
|
|
func (ic IssueCollection) edit() tea.Msg { return newEditWidget(ic.Path) }
|
|
|
|
|
|
|
|
// render cmd for IssueCollection widget
|
|
// render cmd for IssueCollection widget
|
|
@@ -763,7 +784,7 @@ func (ic IssueCollection) render() tea.Msg {
|
|
|
// keyhelp cmd for IssueCollection widget
|
|
// keyhelp cmd for IssueCollection widget
|
|
|
func (ic IssueCollection) keyhelp() string {
|
|
func (ic IssueCollection) keyhelp() string {
|
|
|
var output string
|
|
var output string
|
|
|
- output = output + "tab/shift+tab: select\t\tenter: view issue\n\nc: create new issue\t\te: edit selected issue"
|
|
|
|
|
|
|
+ output = output + "tab/shift+tab: select\t\tenter: view issue\nc: create new issue\t\td: delete selected issue\ne: edit selected issue"
|
|
|
return output
|
|
return output
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -810,3 +831,93 @@ func (w setTitle) render() tea.Msg {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (w setTitle) keyhelp() string { return "enter: submit" }
|
|
func (w setTitle) keyhelp() string { return "enter: submit" }
|
|
|
|
|
+
|
|
|
|
|
+type confirmDelete struct {
|
|
|
|
|
+ Path string
|
|
|
|
|
+ input textinput.Model
|
|
|
|
|
+ prompt string
|
|
|
|
|
+ validateString string
|
|
|
|
|
+ err string
|
|
|
|
|
+ success bool
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (w confirmDelete) update(msg tea.Msg) (widget, tea.Cmd) {
|
|
|
|
|
+ var cmds []tea.Cmd
|
|
|
|
|
+ var cmd tea.Cmd
|
|
|
|
|
+ w.input, cmd = w.input.Update(msg)
|
|
|
|
|
+ cmds = append(cmds, cmd)
|
|
|
|
|
+
|
|
|
|
|
+ switch msg := msg.(type) {
|
|
|
|
|
+ case tea.KeyMsg:
|
|
|
|
|
+ switch msg.String() {
|
|
|
|
|
+ case "enter":
|
|
|
|
|
+ if w.success {
|
|
|
|
|
+ cmds = append(cmds, w.back)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ cmds = append(cmds, w.validate)
|
|
|
|
|
+ }
|
|
|
|
|
+ case "esc":
|
|
|
|
|
+ cmds = append(cmds, w.back)
|
|
|
|
|
+ }
|
|
|
|
|
+ cmds = append(cmds, w.render)
|
|
|
|
|
+ case validateMsg:
|
|
|
|
|
+ switch msg {
|
|
|
|
|
+ case true:
|
|
|
|
|
+ cmds = append(cmds, w.deletePath)
|
|
|
|
|
+ case false:
|
|
|
|
|
+ cmds = append(cmds, w.back)
|
|
|
|
|
+ }
|
|
|
|
|
+ case deleteResult:
|
|
|
|
|
+ if len(msg) > 0 {
|
|
|
|
|
+ w.success = true
|
|
|
|
|
+ cmds = append(cmds, w.render)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ w.err = string(msg)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return w, tea.Batch(cmds...)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (w confirmDelete) render() tea.Msg {
|
|
|
|
|
+ if len(w.err) == 0 {
|
|
|
|
|
+ if w.success {
|
|
|
|
|
+ return fmt.Sprintf("Successfully deleted %s", w.Path)
|
|
|
|
|
+ }
|
|
|
|
|
+ prompt := fmt.Sprintf("%s (%s)...\n", w.prompt, w.Path)
|
|
|
|
|
+ return fmt.Sprintf("%s%s", prompt, w.input.View())
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // wrap in string so its caught by the content update signal
|
|
|
|
|
+ return w.err
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (w confirmDelete) keyhelp() string {
|
|
|
|
|
+ if w.success {
|
|
|
|
|
+ return "enter: continue\t\tesc: continue"
|
|
|
|
|
+ }
|
|
|
|
|
+ return "enter: submit\t\tesc: cancel"
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (w confirmDelete) validate() tea.Msg {
|
|
|
|
|
+ if w.input.Value() == w.validateString {
|
|
|
|
|
+ return validateMsg(true)
|
|
|
|
|
+ }
|
|
|
|
|
+ return validateMsg(false)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (w confirmDelete) deletePath() tea.Msg {
|
|
|
|
|
+ if err := os.RemoveAll(w.Path); err != nil {
|
|
|
|
|
+ return deleteResult(err.Error())
|
|
|
|
|
+ }
|
|
|
|
|
+ return deleteResult("")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (w confirmDelete) back() tea.Msg {
|
|
|
|
|
+ remainder, _ := filepath.Split(w.Path)
|
|
|
|
|
+ remainder = strings.TrimRight(remainder, "/")
|
|
|
|
|
+ if len(remainder) == 0 {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ return loadPath(remainder)
|
|
|
|
|
+}
|