214 lines
4.2 KiB
Go
214 lines
4.2 KiB
Go
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"regexp"
|
||
"time"
|
||
|
||
tea "github.com/charmbracelet/bubbletea"
|
||
"github.com/charmbracelet/lipgloss"
|
||
)
|
||
|
||
func (m model) Init() tea.Cmd {
|
||
// Just return `nil`, which means "no I/O right now, please."
|
||
return nil
|
||
}
|
||
|
||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||
var cmd tea.Cmd
|
||
|
||
if m.addTask {
|
||
m.textinput, cmd = m.textinput.Update(msg)
|
||
}
|
||
|
||
switch msg := msg.(type) {
|
||
|
||
// Is it a key press?
|
||
case tea.KeyMsg:
|
||
|
||
// Cool, what was the actual key pressed?
|
||
switch msg.String() {
|
||
|
||
// These keys should exit the program.
|
||
case "ctrl+c", "q":
|
||
return m, tea.Quit
|
||
|
||
// The "up" and "k" keys move the cursor up
|
||
case "up", "k":
|
||
if m.cursor > 0 && !m.addTask {
|
||
m.cursor--
|
||
}
|
||
|
||
// The "down" and "j" keys move the cursor down
|
||
case "down", "j":
|
||
if m.cursor < len(m.todos)-1 && !m.addTask {
|
||
m.cursor++
|
||
}
|
||
|
||
case "left", "h":
|
||
if m.tab > 0 && !m.addTask {
|
||
m.tab--
|
||
}
|
||
|
||
case "right", "l":
|
||
if m.tab < 4 && !m.addTask {
|
||
m.tab++
|
||
}
|
||
|
||
// new task
|
||
case "n":
|
||
if !m.addTask {
|
||
m.addTask = true
|
||
m.textinput.Focus()
|
||
m.textinput.Reset()
|
||
}
|
||
|
||
case "esc":
|
||
if m.addTask {
|
||
m.addTask = false
|
||
m.textinput.Reset()
|
||
}
|
||
|
||
case "enter":
|
||
if m.addTask {
|
||
AddNewTask(&m)
|
||
m.addTask = false
|
||
m.textinput.Reset()
|
||
}
|
||
|
||
// The "enter" key and the spacebar (a literal space) toggle
|
||
// the selected state for the item that the cursor is pointing at.
|
||
case " ":
|
||
|
||
if !m.addTask {
|
||
if m.todos[m.cursor].done {
|
||
m.todos[m.cursor].done = false
|
||
} else {
|
||
m.todos[m.cursor].done = true
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
// Return the updated model to the Bubble Tea runtime for processing.
|
||
// Note that we're not returning a command.
|
||
return m, cmd
|
||
}
|
||
|
||
func AddNewTask(m *model) {
|
||
taskTitle := m.textinput.Value()
|
||
|
||
// parse date in title
|
||
var doDate int64 = -1
|
||
var deadline int64 = -1
|
||
|
||
reDate := regexp.MustCompile("\\d{1,2}/\\d{1,2}")
|
||
date := reDate.FindStringSubmatch(taskTitle)
|
||
|
||
if len(date) > 0 {
|
||
// what the fuck Go
|
||
date, _ := time.Parse("02/01/2006", "10/03/2025")
|
||
doDate = date.Unix()
|
||
|
||
}
|
||
|
||
t := todo{
|
||
name: taskTitle,
|
||
done: false,
|
||
startdate: doDate,
|
||
deadline: deadline,
|
||
priority: 1,
|
||
isInbox: true,
|
||
}
|
||
m.todos = append(m.todos, t)
|
||
}
|
||
|
||
func (m model) View() string {
|
||
// The header
|
||
s := ""
|
||
currentList := []todo{}
|
||
|
||
s += "GOTD\n\n"
|
||
|
||
switch m.tab {
|
||
case 0:
|
||
// s += "Inbox"
|
||
taskFilter := func(t todo) bool { return t.isInbox }
|
||
currentList = filter(m.todos, taskFilter)
|
||
case 1:
|
||
// s += "Today"
|
||
taskFilter := func(t todo) bool { return t.startdate == midnightToUnix() || t.deadline == midnightToUnix() }
|
||
currentList = filter(m.todos, taskFilter)
|
||
case 2:
|
||
// s += "Tomorrow"
|
||
// 86400 seconds in 24h, add it on to today's midnight for tomorrow's midnight
|
||
taskFilter := func(t todo) bool { return t.startdate == midnightToUnix() + 86400 || t.deadline == midnightToUnix() + 86400}
|
||
currentList = filter(m.todos, taskFilter)
|
||
case 3:
|
||
// s += "Scheduled"
|
||
taskFilter := func(t todo) bool { return t.startdate != -1 }
|
||
currentList = filter(m.todos, taskFilter)
|
||
case 4:
|
||
// s += "Anytime"
|
||
taskFilter := func(t todo) bool { return t.startdate == -1 }
|
||
currentList = filter(m.todos, taskFilter)
|
||
}
|
||
|
||
|
||
// Iterate over our choices
|
||
for i, value := range currentList {
|
||
|
||
// Is the cursor pointing at this choice?
|
||
cursor := " " // no cursor
|
||
if m.cursor == i {
|
||
cursor = ">" // cursor!
|
||
}
|
||
|
||
// Is this choice selected?
|
||
checked := " " // not selected
|
||
// if _, ok := m.selected[i]; ok {
|
||
// checked = "x" // selected!
|
||
// }
|
||
if value.done {
|
||
checked = "x"
|
||
}
|
||
|
||
renderedTime := time.Unix(value.startdate, 0)
|
||
|
||
// Render the row
|
||
s += fmt.Sprintf("%s [%s] %s – %s\n", cursor, checked, value.name, renderedTime)
|
||
}
|
||
|
||
s += "\n\n"
|
||
|
||
// render tab bar
|
||
for i, v := range []string{"Inbox", "Today", "Tomorrow", "Scheduled", "Anytime"} {
|
||
if i == m.tab {
|
||
s += lipgloss.NewStyle().
|
||
Bold(true).
|
||
Underline(true).
|
||
PaddingRight(3).
|
||
Render(v)
|
||
} else {
|
||
s += lipgloss.NewStyle().
|
||
PaddingRight(3).
|
||
Render(v)
|
||
}
|
||
}
|
||
|
||
// The footer
|
||
s += "\nn: new"
|
||
|
||
if m.addTask {
|
||
s += ": "
|
||
s += m.textinput.View()
|
||
}
|
||
|
||
s += "\nq: quit.\n"
|
||
|
||
// Send the UI for rendering
|
||
return s
|
||
}
|