github.com/wtfutil/wtf@v0.43.0/modules/asana/widget.go (about) 1 package asana 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 "sync" 8 9 "github.com/rivo/tview" 10 "github.com/wtfutil/wtf/utils" 11 "github.com/wtfutil/wtf/view" 12 ) 13 14 type TaskType int 15 16 const ( 17 TASK_TYPE TaskType = iota 18 TASK_SECTION 19 TASK_BREAK 20 ) 21 22 type TaskItem struct { 23 name string 24 numSubtasks int32 25 dueOn string 26 id string 27 url string 28 taskType TaskType 29 completed bool 30 assignee string 31 } 32 33 type Widget struct { 34 view.ScrollableWidget 35 36 tasks []*TaskItem 37 38 mu sync.Mutex 39 err error 40 settings *Settings 41 tviewApp *tview.Application 42 } 43 44 func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages *tview.Pages, settings *Settings) *Widget { 45 widget := &Widget{ 46 ScrollableWidget: view.NewScrollableWidget(tviewApp, redrawChan, pages, settings.Common), 47 48 tviewApp: tviewApp, 49 settings: settings, 50 } 51 52 widget.SetRenderFunction(widget.Render) 53 widget.initializeKeyboardControls() 54 55 return widget 56 } 57 58 /* -------------------- Exported Functions -------------------- */ 59 60 func (widget *Widget) Refresh() { 61 widget.tasks = nil 62 widget.err = nil 63 widget.SetItemCount(0) 64 65 widget.mu.Lock() 66 defer widget.mu.Unlock() 67 tasks, err := widget.Fetch( 68 widget.settings.workspaceId, 69 widget.settings.projectId, 70 widget.settings.mode, 71 widget.settings.sections, 72 widget.settings.allUsers, 73 ) 74 if err != nil { 75 widget.err = err 76 } else { 77 widget.tasks = tasks 78 widget.SetItemCount(len(tasks)) 79 } 80 81 widget.Render() 82 } 83 84 func (widget *Widget) Render() { 85 widget.Redraw(widget.content) 86 } 87 88 func (widget *Widget) Fetch(workspaceId, projectId, mode string, sections []string, allUsers bool) ([]*TaskItem, error) { 89 90 availableModes := map[string]interface{}{ 91 "project": nil, 92 "project_sections": nil, 93 "workspace": nil, 94 } 95 96 if _, ok := availableModes[mode]; !ok { 97 return nil, fmt.Errorf("missing mode, or mode is invalid - please set to project, project_sections or workspace") 98 } 99 100 if widget.settings.apiKey != "" { 101 widget.settings.token = widget.settings.apiKey 102 } else { 103 widget.settings.token = os.Getenv("WTF_ASANA_TOKEN") 104 } 105 106 if widget.settings.token == "" { 107 return nil, fmt.Errorf("missing environment variable token or apikey config") 108 } 109 110 subMode := mode 111 if allUsers && mode != "workspace" { 112 subMode += "_all" 113 } 114 115 if projectId == "" && strings.HasPrefix(subMode, "project") { 116 return nil, fmt.Errorf("missing project id") 117 } 118 119 if workspaceId == "" && subMode == "workspace" { 120 return nil, fmt.Errorf("missing workspace id") 121 } 122 123 var tasks []*TaskItem 124 var err error 125 126 switch { 127 case strings.HasPrefix(subMode, "project_sections"): 128 tasks, err = fetchTasksFromProjectSections(widget.settings.token, projectId, sections, subMode) 129 case strings.HasPrefix(subMode, "project"): 130 tasks, err = fetchTasksFromProject(widget.settings.token, projectId, subMode) 131 case subMode == "workspace": 132 tasks, err = fetchTasksFromWorkspace(widget.settings.token, workspaceId, subMode) 133 default: 134 err = fmt.Errorf("no mode found") 135 } 136 137 if err != nil { 138 return nil, err 139 } 140 141 return tasks, nil 142 143 } 144 145 /* -------------------- Unexported Functions -------------------- */ 146 147 func (widget *Widget) content() (string, string, bool) { 148 149 title := widget.CommonSettings().Title 150 if widget.err != nil { 151 return title, widget.err.Error(), true 152 } 153 154 data := widget.tasks 155 if len(data) == 0 { 156 return title, "No data", false 157 } 158 159 var str string 160 161 for idx, taskItem := range data { 162 switch { 163 case taskItem.taskType == TASK_TYPE: 164 if widget.settings.hideComplete && taskItem.completed { 165 continue 166 } 167 168 rowColor := widget.RowColor(idx) 169 170 completed := "[ []" 171 if taskItem.completed { 172 completed = "[x[]" 173 } 174 175 row := "" 176 177 if widget.settings.allUsers && taskItem.assignee != "" { 178 row = fmt.Sprintf( 179 "[%s] %s %s: %s", 180 rowColor, 181 completed, 182 taskItem.assignee, 183 taskItem.name, 184 ) 185 } else { 186 row = fmt.Sprintf( 187 "[%s] %s %s", 188 rowColor, 189 completed, 190 taskItem.name, 191 ) 192 } 193 194 if taskItem.numSubtasks > 0 { 195 row += fmt.Sprintf(" (%d)", taskItem.numSubtasks) 196 } 197 198 if taskItem.dueOn != "" { 199 row += fmt.Sprintf(" due: %s", taskItem.dueOn) 200 } 201 202 row += " [white]" 203 204 str += utils.HighlightableHelper(widget.View, row, idx, len(taskItem.name)) 205 206 case taskItem.taskType == TASK_SECTION: 207 if idx > 1 { 208 row := "[white] " 209 210 str += utils.HighlightableHelper(widget.View, row, idx, len(taskItem.name)) 211 } 212 row := fmt.Sprintf( 213 "[white] %s [white]", 214 taskItem.name, 215 ) 216 217 str += utils.HighlightableHelper(widget.View, row, idx, len(taskItem.name)) 218 219 row = "[white] " 220 221 str += utils.HighlightableHelper(widget.View, row, idx, len(taskItem.name)) 222 223 } 224 225 } 226 227 return title, str, false 228 229 } 230 231 func (widget *Widget) openTask() { 232 sel := widget.GetSelected() 233 234 if sel >= 0 && widget.tasks != nil && sel < len(widget.tasks) { 235 task := widget.tasks[sel] 236 if task.taskType == TASK_TYPE && task.url != "" { 237 utils.OpenFile(task.url) 238 } 239 } 240 } 241 242 func (widget *Widget) toggleTaskCompletion() { 243 sel := widget.GetSelected() 244 245 if sel >= 0 && widget.tasks != nil && sel < len(widget.tasks) { 246 task := widget.tasks[sel] 247 if task.taskType == TASK_TYPE { 248 widget.mu.Lock() 249 250 err := toggleTaskCompletionById(widget.settings.token, task.id) 251 if err != nil { 252 widget.err = err 253 } 254 255 widget.mu.Unlock() 256 widget.Refresh() 257 } 258 } 259 }