Skip to content

Commit

Permalink
feat: allow prefix selection during task creation/update
Browse files Browse the repository at this point in the history
  • Loading branch information
dhth committed Jul 25, 2024
1 parent bc53dfe commit bf8f956
Show file tree
Hide file tree
Showing 9 changed files with 266 additions and 53 deletions.
8 changes: 8 additions & 0 deletions cmd/assets/guide/actions-choosing-a-prefix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Once you've added a few tasks, you can use the **Prefix Selection List** to
quickly choose a prefix for your next task. This is intended to make creation of
new tasks easier.

Go ahead, try it out.

Press `a/o/O/I/A` to start adding a new task, and then press `<ctrl+p>`. Choose
a prefix from the list and press ``.
1 change: 1 addition & 0 deletions cmd/guide.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var (
{summary: "visuals: list density"},
{summary: "visuals: toggling context pane"},
{summary: "actions: adding tasks"},
{summary: "actions: choosing a prefix"},
{summary: "cli: adding a task via the CLI"},
{summary: "cli: importing several tasks via the CLI"},
{summary: "actions: adding context"},
Expand Down
13 changes: 9 additions & 4 deletions internal/ui/assets/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ omm has 6 components:

- Active Tasks List
- Archived Tasks List
- Task creation/update Pane
- Task Creation/Update Pane
- Task Details Pane
- Task Bookmarks List
- Quick Filter List
- Prefix Selection List

## Keymaps

Expand All @@ -38,7 +38,7 @@ omm has 6 components:
ctrl+x delete task
ctrl+r reload task lists
/ filter list by task prefix
ctrl+p open the "Quick Filter List"
ctrl+p filter by prefix via the prefix selection list
y copy selected task's context to system clipboard
v toggle between compact and spacious view

Expand All @@ -58,6 +58,11 @@ omm has 6 components:
**Note**: Moving tasks around is not allowed when the active tasks list is in a
filtered state, however, you can still use `` to move a task to the top.

### Task Creation/Update Pane

⏎ submit task summary
ctrl+p choose/change prefix via the prefix selection list

### Task Details Pane

h/←/→/l move backwards/forwards when in the task details view
Expand All @@ -68,6 +73,6 @@ filtered state, however, you can still use `⏎` to move a task to the top.

⏎ open URL in browser

### Quick Filter List
### Prefix Selection List

⏎ pre-populate task list's search prompt with chosen prefix
5 changes: 0 additions & 5 deletions internal/ui/initial.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,6 @@ func InitialModel(db *sql.DB, config Config) model {
Background(lipgloss.Color(prefixSearchColor)).
Bold(true)

activeTasksPrefixes := make(map[types.TaskPrefix]struct{})
archivedTasksPrefixes := make(map[types.TaskPrefix]struct{})

tr, _ := getMarkDownRenderer(taskDetailsWordWrap)

m := model{
Expand All @@ -129,8 +126,6 @@ func InitialModel(db *sql.DB, config Config) model {
archivedTaskList: archivedTaskList,
taskBMList: contextBMList,
prefixSearchList: prefixSearchList,
activeTasksPrefixes: activeTasksPrefixes,
archivedTasksPrefixes: archivedTasksPrefixes,
taskInput: taskInput,
showHelpIndicator: true,
tlTitleStyle: taskListTitleStyle,
Expand Down
13 changes: 9 additions & 4 deletions internal/ui/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/charmbracelet/glamour"
"github.com/charmbracelet/lipgloss"
pers "github.com/dhth/omm/internal/persistence"
"github.com/dhth/omm/internal/types"
)

const (
Expand Down Expand Up @@ -46,7 +45,7 @@ const (
taskEntryView
taskDetailsView
contextBookmarksView
prefixSearchView
prefixSelectionView
helpView
)

Expand All @@ -57,15 +56,20 @@ const (
archivedTasks
)

type prefixUse uint

const (
prefixFilter prefixUse = iota
prefixChoose
)

type model struct {
db *sql.DB
cfg Config
taskList list.Model
archivedTaskList list.Model
taskBMList list.Model
prefixSearchList list.Model
activeTasksPrefixes map[types.TaskPrefix]struct{}
archivedTasksPrefixes map[types.TaskPrefix]struct{}
tlIndexMap map[uint64]int
taskIndex int
taskId uint64
Expand Down Expand Up @@ -96,4 +100,5 @@ type model struct {
shortenedListHt int
contextMdRenderer *glamour.TermRenderer
taskDetailsMdRenderer *glamour.TermRenderer
prefixSearchUse prefixUse
}
163 changes: 124 additions & 39 deletions internal/ui/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,73 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.activeView = taskListView
m.activeTaskList = activeTasks
}

case "ctrl+p":
if len(m.taskList.Items()) == 0 {
m.errorMsg = "No items in task list"
break
}

tasksPrefixes := make(map[types.TaskPrefix]struct{})

summary := m.taskInput.Value()

currentPrefix, prefixPresent := getPrefix(summary)

for _, li := range m.taskList.Items() {
t, ok := li.(types.Task)
if !ok {
continue
}

prefix, pOk := t.Prefix()
if !pOk {
continue
}

if prefixPresent && prefix.FilterValue() == currentPrefix {
continue
}

tasksPrefixes[prefix] = struct{}{}
}

var prefixes []types.TaskPrefix
for k := range tasksPrefixes {
prefixes = append(prefixes, k)
}

if len(prefixes) == 0 {
m.errorMsg = "No prefixes in task list"
break
}

if len(prefixes) == 1 {
m.errorMsg = "Only 1 unique prefix in task list"
break
}

sort.Slice(prefixes, func(i, j int) bool {
return prefixes[i] < prefixes[j]
})

pi := make([]list.Item, len(prefixes))
for i, p := range prefixes {
pi[i] = list.Item(p)
}

m.prefixSearchList.SetItems(pi)
m.lastActiveView = m.activeView
m.activeView = prefixSelectionView
switch prefixPresent {
case true:
m.prefixSearchList.Title = "change prefix"
case false:
m.prefixSearchList.Title = "choose prefix"
}
m.prefixSearchUse = prefixChoose

return m, tea.Batch(cmds...)
}
}

Expand Down Expand Up @@ -201,7 +268,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
break
}

if m.activeView == taskDetailsView || m.activeView == contextBookmarksView || m.activeView == prefixSearchView || m.activeView == helpView {
if m.activeView == taskDetailsView || m.activeView == contextBookmarksView || m.activeView == helpView {
m.activeView = m.lastActiveView
switch m.activeView {
case taskListView:
Expand All @@ -212,14 +279,22 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
break
}

if m.activeView == prefixSelectionView {
m.activeView = m.lastActiveView
if m.prefixSearchUse == prefixChoose {
return m, tea.Batch(cmds...)
}
break
}

m.quitting = true
if m.cfg.Guide {
_ = os.Remove(m.cfg.DBPath)
}
return m, tea.Quit

case "?":
if m.activeView == taskDetailsView || m.activeView == contextBookmarksView || m.activeView == prefixSearchView {
if m.activeView == taskDetailsView || m.activeView == contextBookmarksView || m.activeView == prefixSelectionView {
break
}

Expand Down Expand Up @@ -565,15 +640,13 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

var taskList list.Model
var taskPrefixes map[types.TaskPrefix]struct{}
taskPrefixes := make(map[types.TaskPrefix]struct{})

switch m.activeView {
case taskListView:
taskList = m.taskList
taskPrefixes = m.activeTasksPrefixes
case archivedTaskListView:
taskList = m.archivedTaskList
taskPrefixes = m.archivedTasksPrefixes
}

if len(taskList.Items()) == 0 {
Expand Down Expand Up @@ -615,10 +688,13 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

m.prefixSearchList.SetItems(pi)
m.activeView = prefixSearchView
m.lastActiveView = m.activeView
m.activeView = prefixSelectionView
m.prefixSearchList.Title = "filter by prefix"
m.prefixSearchUse = prefixFilter

case "enter":
if m.activeView != taskListView && m.activeView != contextBookmarksView && m.activeView != prefixSearchView {
if m.activeView != taskListView && m.activeView != contextBookmarksView && m.activeView != prefixSelectionView {
break
}
switch m.activeView {
Expand Down Expand Up @@ -663,48 +739,57 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case contextBookmarksView:
url := m.taskBMList.SelectedItem().FilterValue()
cmds = append(cmds, openURL(url))
case prefixSearchView:
case prefixSelectionView:
prefix := m.prefixSearchList.SelectedItem().FilterValue()
var taskList list.Model

switch m.activeTaskList {
case activeTasks:
taskList = m.taskList
case archivedTasks:
taskList = m.archivedTaskList
}
switch m.prefixSearchUse {
case prefixFilter:
var taskList list.Model

taskList.ResetFilter()
var tlCmd tea.Cmd
switch m.activeTaskList {
case activeTasks:
taskList = m.taskList
case archivedTasks:
taskList = m.archivedTaskList
}

runes := []rune(prefix)
taskList.ResetFilter()
var tlCmd tea.Cmd

if len(runes) > 1 {
taskList.FilterInput.SetValue(string(runes[:len(runes)-1]))
}
runes := []rune(prefix)

taskList, tlCmd = taskList.Update(tea.KeyMsg{Type: -1, Runes: []int32{47}, Alt: false, Paste: false})
cmds = append(cmds, tlCmd)
if len(runes) > 1 {
taskList.FilterInput.SetValue(string(runes[:len(runes)-1]))
}

taskList, tlCmd = taskList.Update(tea.KeyMsg{Type: -1, Runes: []rune{runes[len(runes)-1]}, Alt: false, Paste: false})
cmds = append(cmds, tlCmd)
taskList, tlCmd = taskList.Update(tea.KeyMsg{Type: -1, Runes: []int32{47}, Alt: false, Paste: false})
cmds = append(cmds, tlCmd)

taskList, tlCmd = taskList.Update(tea.KeyMsg{Type: -1, Runes: []rune{runes[len(runes)-1]}, Alt: false, Paste: false})
cmds = append(cmds, tlCmd)

// TODO: Try sending ENTER programmatically too
// taskList, tlCmd = taskList.Update(tea.KeyMsg{Type: 13, Runes: []int32(nil), Alt: false, Paste: false})
// or
// taskList, tlCmd = taskList.Update(tea.KeyEnter)
// this results in the list's paginator being broken, so requires another manual ENTER keypress

switch m.activeTaskList {
case activeTasks:
m.taskList = taskList
m.activeView = taskListView
case archivedTasks:
m.archivedTaskList = taskList
m.activeView = archivedTaskListView
}

// TODO: Try sending ENTER programmatically too
// taskList, tlCmd = taskList.Update(tea.KeyMsg{Type: 13, Runes: []int32(nil), Alt: false, Paste: false})
// or
// taskList, tlCmd = taskList.Update(tea.KeyEnter)
// this results in the list's paginator being broken, so requires another manual ENTER keypress
return m, tea.Sequence(cmds...)

switch m.activeTaskList {
case activeTasks:
m.taskList = taskList
m.activeView = taskListView
case archivedTasks:
m.archivedTaskList = taskList
m.activeView = archivedTaskListView
case prefixChoose:
m.taskInput.SetValue(getSummaryWithNewPrefix(m.taskInput.Value(), prefix))
m.activeView = taskEntryView
}

return m, tea.Sequence(cmds...)
}

case "E":
Expand Down Expand Up @@ -1340,7 +1425,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case contextBookmarksView:
m.taskBMList, viewUpdateCmd = m.taskBMList.Update(msg)

case prefixSearchView:
case prefixSelectionView:
m.prefixSearchList, viewUpdateCmd = m.prefixSearchList.Update(msg)

case helpView:
Expand Down
Loading

0 comments on commit bf8f956

Please sign in to comment.