Better Resource Management
This commit is contained in:
parent
427468c6a8
commit
dbf94a42a2
11
app_state.go
11
app_state.go
@ -20,10 +20,17 @@ type AppState struct {
|
||||
ValidOperations map[string][]string
|
||||
OpFuncs map[string]func([]string) int
|
||||
|
||||
mode ResourceId
|
||||
|
||||
TaskList *todotxt.TaskList
|
||||
DoneList *todotxt.TaskList
|
||||
|
||||
taskListLoaded bool
|
||||
doneListLoaded bool
|
||||
|
||||
screens []Screen
|
||||
|
||||
lang *Translator
|
||||
}
|
||||
|
||||
func NewApp() *AppState {
|
||||
@ -41,8 +48,10 @@ func NewApp() *AppState {
|
||||
func (a *AppState) run(parms []string) int {
|
||||
if len(parms) == 0 || parms[0] == "ui" {
|
||||
// UI Mode
|
||||
a.mode = ResModeUI
|
||||
return uiLoop()
|
||||
}
|
||||
a.mode = ResModeCLI
|
||||
if fn, ok := a.OpFuncs[parms[0]]; ok {
|
||||
return fn(parms[1:])
|
||||
}
|
||||
@ -117,6 +126,8 @@ func (a *AppState) migrate(from, to int) int {
|
||||
}
|
||||
|
||||
func (a *AppState) initialize() {
|
||||
a.initLanguage()
|
||||
|
||||
var err error
|
||||
a.config, err = userConfig.NewConfig(a.Name)
|
||||
if err != nil {
|
||||
|
48
model.go
48
model.go
@ -7,7 +7,36 @@ import (
|
||||
todotxt "github.com/br0xen/go-todotxt"
|
||||
)
|
||||
|
||||
// diskListChanged returns true if the task list in todo.txt
|
||||
// is different than what we have previously loaded.
|
||||
func (a *AppState) diskListChanged() bool {
|
||||
if !a.taskListLoaded {
|
||||
return false
|
||||
}
|
||||
curr, err := todotxt.LoadFromFilename(a.getTodoFile())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return curr.String() != a.TaskList.String()
|
||||
}
|
||||
|
||||
// diskDoneListChanged returns true if the task list in done.txt
|
||||
// is different than what we have previously loaded.
|
||||
func (a *AppState) diskDoneListChanged() bool {
|
||||
if !a.doneListLoaded {
|
||||
return false
|
||||
}
|
||||
curr, err := todotxt.LoadFromFilename(a.getDoneFile())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return curr.String() != a.DoneList.String()
|
||||
}
|
||||
|
||||
func (a *AppState) addTask(taskString string) error {
|
||||
if a.diskListChanged() {
|
||||
return a.e(ResStrListChanged)
|
||||
}
|
||||
t, err := todotxt.ParseTask(taskString)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -20,6 +49,9 @@ func (a *AppState) addTask(taskString string) error {
|
||||
}
|
||||
|
||||
func (a *AppState) toggleTaskComplete(id int) error {
|
||||
if a.diskListChanged() {
|
||||
return a.e(ResStrListChanged)
|
||||
}
|
||||
var task *todotxt.Task
|
||||
var err error
|
||||
if task, err = a.TaskList.GetTask(id); err != nil {
|
||||
@ -34,12 +66,15 @@ func (a *AppState) toggleTaskComplete(id int) error {
|
||||
}
|
||||
|
||||
func (a *AppState) archiveTask(id int) error {
|
||||
if a.diskListChanged() {
|
||||
return a.e(ResStrListChanged)
|
||||
}
|
||||
var err error
|
||||
var task *todotxt.Task
|
||||
if task, err = a.TaskList.GetTask(id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.TaskList.ArchiveTaskToFile(*task, app.getDoneFile()); err != nil {
|
||||
if err := a.TaskList.ArchiveTaskToFile(*task, a.getDoneFile()); err != nil {
|
||||
return err
|
||||
}
|
||||
a.TaskList.RemoveTask(*task)
|
||||
@ -47,6 +82,9 @@ func (a *AppState) archiveTask(id int) error {
|
||||
}
|
||||
|
||||
func (a *AppState) unarchiveTask(id int) error {
|
||||
if a.diskListChanged() {
|
||||
return a.e(ResStrListChanged)
|
||||
}
|
||||
var err error
|
||||
var task *todotxt.Task
|
||||
if task, err = a.DoneList.GetTask(id); err != nil {
|
||||
@ -112,6 +150,7 @@ func (a *AppState) LoadTaskList() error {
|
||||
var tl todotxt.TaskList
|
||||
tl, err = todotxt.LoadFromFilename(a.getTodoFile())
|
||||
a.TaskList = &tl
|
||||
a.taskListLoaded = true
|
||||
return err
|
||||
}
|
||||
|
||||
@ -120,13 +159,20 @@ func (a *AppState) LoadDoneList() error {
|
||||
var tl todotxt.TaskList
|
||||
tl, err = todotxt.LoadFromFilename(a.getDoneFile())
|
||||
a.DoneList = &tl
|
||||
a.doneListLoaded = true
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *AppState) WriteList() error {
|
||||
if !a.taskListLoaded {
|
||||
return a.e(ResStrTaskListNotLoaded)
|
||||
}
|
||||
return a.TaskList.WriteToFilename(a.getTodoFile())
|
||||
}
|
||||
|
||||
func (a *AppState) WriteDoneList() error {
|
||||
if !a.doneListLoaded {
|
||||
return a.e(ResStrDoneListNotLoaded)
|
||||
}
|
||||
return a.DoneList.WriteToFilename(a.getDoneFile())
|
||||
}
|
||||
|
79
resources.go
Normal file
79
resources.go
Normal file
@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
import "errors"
|
||||
|
||||
type ResourceId uint16
|
||||
|
||||
type Translator struct {
|
||||
values map[ResourceId]string
|
||||
}
|
||||
|
||||
func NewTranslator() *Translator {
|
||||
return &Translator{
|
||||
values: make(map[ResourceId]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Translator) addString(res ResourceId, val string) {
|
||||
t.values[res] = val
|
||||
if _, ok := t.values[(res & 255)]; !ok {
|
||||
t.values[(res & 255)] = val
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Translator) getString(res ResourceId) (string, error) {
|
||||
if v, ok := t.values[res]; ok {
|
||||
return v, nil
|
||||
}
|
||||
// Couldn't pull the exact resource, try the basic one
|
||||
if v, ok := t.values[(res & 255)]; ok {
|
||||
return v, nil
|
||||
}
|
||||
return "", errors.New("String Resource Not Found")
|
||||
}
|
||||
|
||||
const (
|
||||
ResStrListChanged ResourceId = iota
|
||||
ResStrTaskListNotLoaded
|
||||
ResStrDoneListNotLoaded
|
||||
ResStrInvalidRefreshRequest
|
||||
|
||||
ResModeCLI ResourceId = 1 << (iota + 9)
|
||||
ResModeUI
|
||||
)
|
||||
|
||||
func (a *AppState) initLanguage() {
|
||||
a.lang = NewTranslator()
|
||||
|
||||
// Strings that are the same regardless of mode
|
||||
a.lang.addString((ResStrTaskListNotLoaded), "Task list hasn't been loaded")
|
||||
a.lang.addString((ResStrDoneListNotLoaded), "Done list hasn't been loaded")
|
||||
|
||||
// CLI Strings
|
||||
a.lang.addString((ResStrListChanged | ResModeCLI), "List changed somewhere else")
|
||||
|
||||
// UI Strings
|
||||
a.lang.addString((ResStrListChanged | ResModeUI), "List changed somewhere else, reload and try again (Ctrl+R)")
|
||||
}
|
||||
|
||||
// s returns a string with the given stringId
|
||||
// (per the constants above)
|
||||
func (a *AppState) s(stringId ResourceId) string {
|
||||
if stringId&255 != stringId {
|
||||
// the attribute already has extended data in it.
|
||||
if v, err := a.lang.getString(stringId); err == nil {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
if v, err := a.lang.getString((stringId | a.mode)); err == nil {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// e is basically the same as 's', but returns
|
||||
// the string in an error object
|
||||
func (a *AppState) e(stringId ResourceId) error {
|
||||
return errors.New(a.s(stringId))
|
||||
}
|
19
screen.go
19
screen.go
@ -1,6 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
termbox "github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
@ -49,15 +51,16 @@ func readUserInput(e chan termbox.Event) {
|
||||
}
|
||||
}
|
||||
|
||||
func refreshList(e chan termbox.Event) {
|
||||
/*
|
||||
for {
|
||||
time.Sleep(5 * time.Minute)
|
||||
app.LoadTasklist()
|
||||
app.LoadDoneList()
|
||||
e <- termbox.Event{Type: termbox.EventNone}
|
||||
func checkForUpdate(e chan termbox.Event) {
|
||||
for {
|
||||
time.Sleep(time.Minute)
|
||||
if app.diskListChanged() {
|
||||
e <- termbox.Event{
|
||||
Type: termbox.EventError,
|
||||
Err: app.e(ResStrListChanged),
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@ -16,6 +17,8 @@ type MainScreen struct {
|
||||
message string
|
||||
messageTimeout time.Duration
|
||||
messageTime time.Time
|
||||
messageColorBg termbox.Attribute
|
||||
messageColorFg termbox.Attribute
|
||||
mode int
|
||||
cursor map[string]int
|
||||
|
||||
@ -64,6 +67,17 @@ func (screen *MainScreen) initialize(bundle Bundle) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (screen *MainScreen) refreshList(bundle Bundle) error {
|
||||
whichList := bundle.getString(MainBundleListKey, MainBundleListTodo)
|
||||
switch whichList {
|
||||
case MainBundleListTodo:
|
||||
return app.LoadTaskList()
|
||||
case MainBundleListDone:
|
||||
return app.LoadDoneList()
|
||||
}
|
||||
return errors.New("Invalid refresh request.")
|
||||
}
|
||||
|
||||
func (screen *MainScreen) reloadList(bundle Bundle) error {
|
||||
// We add tasks to the display list using append because we want to persist task Ids
|
||||
screen.displayList = todotxt.NewTaskList()
|
||||
@ -121,7 +135,7 @@ func (screen *MainScreen) handleKeyEvent(event termbox.Event) int {
|
||||
// Go to About Screen
|
||||
b := Bundle{}
|
||||
if err := app.screens[ScreenAbout].initialize(b); err != nil {
|
||||
screen.setMessage(err.Error())
|
||||
screen.setErrorMessage(err.Error())
|
||||
}
|
||||
return ScreenAbout
|
||||
} else if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 {
|
||||
@ -149,7 +163,9 @@ func (screen *MainScreen) handleKeyEvent(event termbox.Event) int {
|
||||
screen.cursor[screen.currentList] = len(*screen.displayList) - 1
|
||||
|
||||
} else if event.Key == termbox.KeyCtrlR {
|
||||
screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter))
|
||||
b := screen.buildBundle(screen.currentList, screen.currentFilter)
|
||||
screen.refreshList(b)
|
||||
screen.reloadList(b)
|
||||
|
||||
} else if event.Key == termbox.KeyCtrlF {
|
||||
// Jump forward half a screen
|
||||
@ -217,7 +233,7 @@ func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int {
|
||||
// Create the new item
|
||||
err := app.addTask(screen.inputField.GetValue())
|
||||
if err != nil {
|
||||
screen.setMessage(err.Error())
|
||||
screen.setErrorMessage(err.Error())
|
||||
}
|
||||
screen.inputField.SetID("")
|
||||
screen.inputField.SetValue("")
|
||||
@ -330,7 +346,7 @@ func (screen *MainScreen) drawFooter() {
|
||||
screen.inputField.Draw()
|
||||
}
|
||||
// And the 'message'
|
||||
termboxUtil.DrawStringAtPoint(screen.message, 0, height-1, DefaultFg, DefaultBg)
|
||||
termboxUtil.DrawStringAtPoint(screen.message, 0, height-1, screen.messageColorFg, screen.messageColorBg)
|
||||
}
|
||||
|
||||
func (screen *MainScreen) confirmArchiveItem() int {
|
||||
@ -353,20 +369,20 @@ func (screen *MainScreen) confirmArchiveItem() int {
|
||||
|
||||
func (screen *MainScreen) archiveCurrentItem() int {
|
||||
if screen.currentList != MainBundleListTodo {
|
||||
screen.setMessage("Task is already archived")
|
||||
screen.setErrorMessage("Task is already archived")
|
||||
return ScreenMain
|
||||
}
|
||||
// Find the task under the cursor
|
||||
if len(*screen.displayList) > screen.cursor[screen.currentList] {
|
||||
t := (*screen.displayList)[screen.cursor[screen.currentList]]
|
||||
if err := app.archiveTask(t.Id); err != nil {
|
||||
screen.setMessage(err.Error())
|
||||
screen.setErrorMessage(err.Error())
|
||||
return ScreenMain
|
||||
}
|
||||
// Reload the list
|
||||
b := screen.buildBundle(screen.currentList, screen.currentFilter)
|
||||
if err := screen.reloadList(b); err != nil {
|
||||
screen.setMessage(err.Error())
|
||||
screen.setErrorMessage(err.Error())
|
||||
}
|
||||
}
|
||||
return ScreenMain
|
||||
@ -374,20 +390,20 @@ func (screen *MainScreen) archiveCurrentItem() int {
|
||||
|
||||
func (screen *MainScreen) unarchiveCurrentItem() int {
|
||||
if screen.currentList == MainBundleListTodo {
|
||||
screen.setMessage("Task is not archived")
|
||||
screen.setErrorMessage("Task is not archived")
|
||||
return ScreenMain
|
||||
}
|
||||
// Find the task under the cursor
|
||||
if len(*screen.displayList) > screen.cursor[screen.currentList] {
|
||||
t := (*screen.displayList)[screen.cursor[screen.currentList]]
|
||||
if err := app.unarchiveTask(t.Id); err != nil {
|
||||
screen.setMessage(err.Error())
|
||||
screen.setErrorMessage(err.Error())
|
||||
return ScreenMain
|
||||
}
|
||||
// Reload the list
|
||||
b := screen.buildBundle(screen.currentList, screen.currentFilter)
|
||||
if err := screen.reloadList(b); err != nil {
|
||||
screen.setMessage(err.Error())
|
||||
screen.setErrorMessage(err.Error())
|
||||
}
|
||||
}
|
||||
return ScreenMain
|
||||
@ -401,7 +417,7 @@ func (screen *MainScreen) startEditTaskScreen() int {
|
||||
b := Bundle{}
|
||||
b.setValue(TaskBundleTaskIdKey, t.Id)
|
||||
if err := app.screens[ScreenTask].initialize(b); err != nil {
|
||||
screen.setMessage(err.Error())
|
||||
screen.setErrorMessage(err.Error())
|
||||
return ScreenMain
|
||||
}
|
||||
return ScreenTask
|
||||
@ -436,7 +452,7 @@ func (screen *MainScreen) startAddNewTask() int {
|
||||
|
||||
func (screen *MainScreen) toggleTaskComplete() int {
|
||||
if screen.currentList == MainBundleListDone {
|
||||
screen.setMessage("Task is archived, unable to modify.")
|
||||
screen.setErrorMessage("Task is archived, unable to modify.")
|
||||
return ScreenMain
|
||||
}
|
||||
|
||||
@ -445,7 +461,7 @@ func (screen *MainScreen) toggleTaskComplete() int {
|
||||
t := (*screen.displayList)[screen.cursor[screen.currentList]]
|
||||
err := app.toggleTaskComplete(t.Id)
|
||||
if err != nil {
|
||||
screen.setMessage(err.Error())
|
||||
screen.setErrorMessage(err.Error())
|
||||
return ScreenMain
|
||||
}
|
||||
}
|
||||
@ -476,10 +492,20 @@ func (screen *MainScreen) startFilter() int {
|
||||
return ScreenMain
|
||||
}
|
||||
|
||||
func (screen *MainScreen) setErrorMessage(msg string) {
|
||||
screen.message = " " + msg + " "
|
||||
screen.messageTime = time.Now()
|
||||
screen.messageTimeout = time.Second * 2
|
||||
screen.messageColorBg = termbox.ColorRed
|
||||
screen.messageColorFg = termbox.ColorWhite | termbox.AttrBold
|
||||
}
|
||||
|
||||
func (screen *MainScreen) setMessage(msg string) {
|
||||
screen.message = msg
|
||||
screen.messageTime = time.Now()
|
||||
screen.messageTimeout = time.Second * 2
|
||||
screen.messageColorBg = DefaultBg
|
||||
screen.messageColorFg = DefaultFg
|
||||
}
|
||||
|
||||
/* setMessageWithTimeout lets you specify the timeout for the message
|
||||
@ -494,6 +520,8 @@ func (screen *MainScreen) setMessageWithTimeout(msg string, timeout time.Duratio
|
||||
func (screen *MainScreen) clearMessage() {
|
||||
screen.message = fmt.Sprintf("%d Total Tasks", len(*screen.activeList))
|
||||
screen.messageTimeout = -1
|
||||
screen.messageColorBg = DefaultBg
|
||||
screen.messageColorFg = DefaultFg
|
||||
}
|
||||
|
||||
func (screen *MainScreen) buildBundle(list, filter string) Bundle {
|
||||
|
@ -25,6 +25,7 @@ func uiLoop() int {
|
||||
app.layoutAndDrawScreen(displayScreen)
|
||||
eventChan := make(chan termbox.Event)
|
||||
go readUserInput(eventChan)
|
||||
go checkForUpdate(eventChan)
|
||||
for {
|
||||
event := <-eventChan
|
||||
if event.Type == termbox.EventKey {
|
||||
|
Loading…
Reference in New Issue
Block a user