mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
Widget titles can now be specified in the config file via a 'title' key. Example: wtf: mods: todo: title: Tada which can include emoji. No need to force everyone to look at my emoji, now they can define their own.
260 lines
5.8 KiB
Go
260 lines
5.8 KiB
Go
package gcal
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/olebedev/config"
|
|
"github.com/senorprogrammer/wtf/wtf"
|
|
"google.golang.org/api/calendar/v3"
|
|
)
|
|
|
|
// Config is a pointer to the global config object
|
|
var Config *config.Config
|
|
|
|
type Widget struct {
|
|
wtf.TextWidget
|
|
}
|
|
|
|
func NewWidget() *Widget {
|
|
widget := Widget{
|
|
TextWidget: wtf.NewTextWidget(" Calendar ", "gcal", false),
|
|
}
|
|
|
|
return &widget
|
|
}
|
|
|
|
/* -------------------- Exported Functions -------------------- */
|
|
|
|
func (widget *Widget) Refresh() {
|
|
if widget.Disabled() {
|
|
return
|
|
}
|
|
|
|
events, _ := Fetch()
|
|
|
|
widget.UpdateRefreshedAt()
|
|
|
|
widget.View.SetText(fmt.Sprintf("%s", widget.contentFrom(events)))
|
|
}
|
|
|
|
/* -------------------- Unexported Functions -------------------- */
|
|
|
|
// conflicts returns TRUE if this event conflicts with another, FALSE if it does not
|
|
func (widget *Widget) conflicts(event *calendar.Event, events *calendar.Events) bool {
|
|
conflict := false
|
|
|
|
for _, otherEvent := range events.Items {
|
|
if event == otherEvent {
|
|
continue
|
|
}
|
|
|
|
eventStart, _ := time.Parse(time.RFC3339, event.Start.DateTime)
|
|
eventEnd, _ := time.Parse(time.RFC3339, event.End.DateTime)
|
|
|
|
otherEnd, _ := time.Parse(time.RFC3339, otherEvent.End.DateTime)
|
|
otherStart, _ := time.Parse(time.RFC3339, otherEvent.Start.DateTime)
|
|
|
|
if eventStart.Before(otherEnd) && eventEnd.After(otherStart) {
|
|
conflict = true
|
|
break
|
|
}
|
|
}
|
|
|
|
return conflict
|
|
}
|
|
|
|
func (widget *Widget) contentFrom(events *calendar.Events) string {
|
|
if events == nil {
|
|
return ""
|
|
}
|
|
|
|
var prevEvent *calendar.Event
|
|
|
|
str := ""
|
|
for _, event := range events.Items {
|
|
conflict := widget.conflicts(event, events)
|
|
|
|
str = str + fmt.Sprintf(
|
|
"%s %s[%s]%s[white]\n %s[%s]%s %s[white]\n\n",
|
|
widget.dayDivider(event, prevEvent),
|
|
widget.responseIcon(event),
|
|
widget.titleColor(event),
|
|
widget.eventSummary(event, conflict),
|
|
widget.location(event),
|
|
widget.descriptionColor(event),
|
|
widget.eventTimestamp(event),
|
|
widget.until(event),
|
|
)
|
|
|
|
prevEvent = event
|
|
}
|
|
|
|
return str
|
|
}
|
|
|
|
func (widget *Widget) dayDivider(event, prevEvent *calendar.Event) string {
|
|
if prevEvent != nil {
|
|
prevStartTime, _ := time.Parse(time.RFC3339, prevEvent.Start.DateTime)
|
|
currStartTime, _ := time.Parse(time.RFC3339, event.Start.DateTime)
|
|
|
|
if currStartTime.Day() != prevStartTime.Day() {
|
|
return "\n"
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func (widget *Widget) descriptionColor(event *calendar.Event) string {
|
|
color := Config.UString("wtf.mods.gcal.colors.description", "white")
|
|
|
|
if widget.eventIsPast(event) {
|
|
color = Config.UString("wtf.mods.gcal.colors.past", "gray")
|
|
}
|
|
|
|
return color
|
|
}
|
|
|
|
func (widget *Widget) eventSummary(event *calendar.Event, conflict bool) string {
|
|
summary := event.Summary
|
|
|
|
if widget.eventIsNow(event) {
|
|
summary = fmt.Sprintf(
|
|
"%s %s",
|
|
Config.UString("wtf.mods.gcal.currentIcon", "🔸"),
|
|
event.Summary,
|
|
)
|
|
}
|
|
|
|
if conflict {
|
|
return fmt.Sprintf("%s %s", Config.UString("wtf.mods.gcal.conflictIcon", "🚨"), summary)
|
|
} else {
|
|
return summary
|
|
}
|
|
}
|
|
|
|
func (widget *Widget) eventTimestamp(event *calendar.Event) string {
|
|
startTime, _ := time.Parse(time.RFC3339, event.Start.DateTime)
|
|
return startTime.Format(wtf.FriendlyDateTimeFormat)
|
|
}
|
|
|
|
// eventIsNow returns true if the event is happening now, false if it not
|
|
func (widget *Widget) eventIsNow(event *calendar.Event) bool {
|
|
startTime, _ := time.Parse(time.RFC3339, event.Start.DateTime)
|
|
endTime, _ := time.Parse(time.RFC3339, event.End.DateTime)
|
|
|
|
return time.Now().After(startTime) && time.Now().Before(endTime)
|
|
}
|
|
|
|
func (widget *Widget) eventIsPast(event *calendar.Event) bool {
|
|
ts, _ := time.Parse(time.RFC3339, event.Start.DateTime)
|
|
return (widget.eventIsNow(event) == false) && ts.Before(time.Now())
|
|
}
|
|
|
|
func (widget *Widget) titleColor(event *calendar.Event) string {
|
|
color := Config.UString("wtf.mods.gcal.colors.title", "white")
|
|
|
|
for _, untypedArr := range Config.UList("wtf.mods.gcal.colors.highlights") {
|
|
highlightElements := wtf.ToStrs(untypedArr.([]interface{}))
|
|
|
|
match, _ := regexp.MatchString(
|
|
strings.ToLower(highlightElements[0]),
|
|
strings.ToLower(event.Summary),
|
|
)
|
|
|
|
if match == true {
|
|
color = highlightElements[1]
|
|
}
|
|
}
|
|
|
|
if widget.eventIsPast(event) {
|
|
color = Config.UString("wtf.mods.gcal.colors.past", "gray")
|
|
}
|
|
|
|
return color
|
|
}
|
|
|
|
func (widget *Widget) location(event *calendar.Event) string {
|
|
if Config.UBool("wtf.mods.gcal.displayLocation", true) == false {
|
|
return ""
|
|
}
|
|
|
|
if event.Location == "" {
|
|
return ""
|
|
}
|
|
|
|
return fmt.Sprintf(
|
|
"[%s]%s\n ",
|
|
widget.descriptionColor(event),
|
|
event.Location,
|
|
)
|
|
}
|
|
|
|
func (widget *Widget) responseIcon(event *calendar.Event) string {
|
|
if false == Config.UBool("wtf.mods.gcal.displayResponseStatus", true) {
|
|
return ""
|
|
}
|
|
|
|
response := ""
|
|
|
|
for _, attendee := range event.Attendees {
|
|
if attendee.Email == Config.UString("wtf.mods.gcal.email") {
|
|
response = attendee.ResponseStatus
|
|
break
|
|
}
|
|
}
|
|
|
|
icon := "[gray]"
|
|
|
|
switch response {
|
|
case "accepted":
|
|
icon = icon + "✔︎ "
|
|
case "declined":
|
|
icon = icon + "✘ "
|
|
case "needsAction":
|
|
icon = icon + "? "
|
|
case "tentative":
|
|
icon = icon + "~ "
|
|
default:
|
|
icon = icon + ""
|
|
}
|
|
|
|
return icon
|
|
}
|
|
|
|
// until returns the number of hours or days until the event
|
|
// If the event is in the past, returns nil
|
|
func (widget *Widget) until(event *calendar.Event) string {
|
|
startTime, _ := time.Parse(time.RFC3339, event.Start.DateTime)
|
|
duration := time.Until(startTime)
|
|
|
|
duration = duration.Round(time.Minute)
|
|
|
|
if duration < 0 {
|
|
return ""
|
|
}
|
|
|
|
days := duration / (24 * time.Hour)
|
|
duration -= days * (24 * time.Hour)
|
|
|
|
hours := duration / time.Hour
|
|
duration -= hours * time.Hour
|
|
|
|
mins := duration / time.Minute
|
|
|
|
untilStr := ""
|
|
|
|
if days > 0 {
|
|
untilStr = fmt.Sprintf("%dd", days)
|
|
} else if hours > 0 {
|
|
untilStr = fmt.Sprintf("%dh", hours)
|
|
} else {
|
|
untilStr = fmt.Sprintf("%dm", mins)
|
|
}
|
|
|
|
return "[lightblue]" + untilStr + "[white]"
|
|
}
|