2017-02-02 17:03:02 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2017-02-23 17:20:29 +00:00
|
|
|
"encoding/json"
|
2017-02-02 17:03:02 +00:00
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"os"
|
2017-02-16 15:59:33 +00:00
|
|
|
"sort"
|
|
|
|
"strings"
|
2017-02-23 17:20:29 +00:00
|
|
|
"time"
|
2017-02-02 17:03:02 +00:00
|
|
|
|
|
|
|
"github.com/br0xen/user-config"
|
2017-02-23 17:20:29 +00:00
|
|
|
"github.com/jroimartin/gocui"
|
2017-02-02 17:03:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
AppName = "gocal"
|
|
|
|
AppVersion = 1
|
|
|
|
)
|
|
|
|
|
|
|
|
type AppState struct {
|
2017-02-23 17:20:29 +00:00
|
|
|
Name string
|
|
|
|
Version int
|
|
|
|
ClientSecret []byte
|
|
|
|
cfg *userConfig.Config
|
|
|
|
account *Account
|
|
|
|
defaultCalendars []string
|
|
|
|
|
|
|
|
StatusMsg string
|
2017-02-02 17:03:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var state *AppState
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
var err error
|
2017-02-16 15:59:33 +00:00
|
|
|
var op string
|
2017-02-02 17:03:02 +00:00
|
|
|
state = &AppState{Name: AppName, Version: AppVersion}
|
|
|
|
state.cfg, err = userConfig.NewConfig(state.Name)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2017-02-16 15:59:33 +00:00
|
|
|
if len(os.Args) > 1 {
|
|
|
|
op = os.Args[1]
|
|
|
|
} else {
|
|
|
|
op = "today"
|
2017-02-02 17:03:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DoVersionCheck()
|
|
|
|
|
2017-02-16 15:59:33 +00:00
|
|
|
switch op {
|
|
|
|
case "--reinit":
|
|
|
|
// Reset all config
|
|
|
|
for _, v := range state.cfg.GetKeyList() {
|
|
|
|
fmt.Println("Deleting Key: " + v)
|
|
|
|
state.cfg.DeleteKey(v)
|
|
|
|
}
|
2017-02-23 17:20:29 +00:00
|
|
|
|
2017-02-16 15:59:33 +00:00
|
|
|
case "today":
|
|
|
|
// Show everything on the calendar for today
|
|
|
|
InitComm()
|
|
|
|
// TODO: Get calendars flagged as default
|
|
|
|
list := state.account.GetCalendarList()
|
|
|
|
var todayEvents []Event
|
|
|
|
if len(list) > 0 {
|
2017-02-23 17:20:29 +00:00
|
|
|
for defIdx := range state.defaultCalendars {
|
|
|
|
for i := range list {
|
|
|
|
if list[i].Deleted {
|
|
|
|
// Deleted calendar, next please
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if state.defaultCalendars[defIdx] == list[i].Id {
|
|
|
|
todayEvents = append(todayEvents, list[i].GetTodaysEvents()...)
|
|
|
|
}
|
2017-02-16 15:59:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
sort.Sort(ByStartTime(todayEvents))
|
|
|
|
for _, e := range todayEvents {
|
|
|
|
if e.GetStartTime() == "00:00:00" {
|
|
|
|
fmt.Println("[All Day ] " + e.Summary)
|
|
|
|
} else {
|
|
|
|
fmt.Println("[" + e.GetStartTime() + "] " + e.Summary)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fmt.Printf("No calendars found.\n")
|
|
|
|
}
|
2017-02-02 17:03:02 +00:00
|
|
|
|
2017-02-23 17:20:29 +00:00
|
|
|
case "config":
|
|
|
|
InitComm()
|
|
|
|
DoConfig()
|
|
|
|
|
2017-02-16 15:59:33 +00:00
|
|
|
case "defaults":
|
|
|
|
// Show Defaults
|
|
|
|
InitComm()
|
|
|
|
|
|
|
|
case "add":
|
|
|
|
// Quick event add to primary calendar
|
|
|
|
quickAddText := strings.Join(os.Args[2:], " ")
|
|
|
|
e, err := state.account.GetDefaultCalendar().QuickAdd(quickAddText)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err.Error())
|
|
|
|
} else {
|
|
|
|
fmt.Println(e.ToCLIString())
|
|
|
|
}
|
|
|
|
case "bail":
|
|
|
|
// Just initialize communications and bail
|
|
|
|
InitComm()
|
|
|
|
}
|
2017-02-23 17:20:29 +00:00
|
|
|
// All done, save the account state
|
|
|
|
saveAccountState()
|
2017-02-16 15:59:33 +00:00
|
|
|
}
|
2017-02-02 17:03:02 +00:00
|
|
|
|
2017-02-16 15:59:33 +00:00
|
|
|
func InitComm() {
|
|
|
|
var err error
|
|
|
|
sec := state.cfg.GetBytes("ClientSecret")
|
|
|
|
tkn := state.cfg.GetBytes("Token")
|
2017-02-02 17:03:02 +00:00
|
|
|
|
2017-02-16 15:59:33 +00:00
|
|
|
state.account, err = GetAccount(sec, tkn)
|
2017-02-02 17:03:02 +00:00
|
|
|
if err != nil {
|
2017-02-16 15:59:33 +00:00
|
|
|
log.Fatalf("Unable to get Account: %v", err)
|
2017-02-02 17:03:02 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 15:59:33 +00:00
|
|
|
// Save the Raw Token
|
|
|
|
state.cfg.SetBytes("Token", state.account.CC.GetRawToken())
|
|
|
|
|
2017-02-23 17:20:29 +00:00
|
|
|
// If the 'defaultCalendars' cfg is set to 'primary' (or not at all)
|
|
|
|
// we need to actually but the primary cal id in there
|
2017-02-16 15:59:33 +00:00
|
|
|
var defCal []string
|
|
|
|
defCal, err = state.cfg.GetArray("defaultCalendars")
|
|
|
|
if len(defCal) == 0 || (len(defCal) == 1 && defCal[0] == "primary") {
|
|
|
|
gCal, err := state.account.Service.CalendarList.Get("primary").Do()
|
|
|
|
if err == nil {
|
2017-02-23 17:20:29 +00:00
|
|
|
defCal = []string{gCal.Id}
|
|
|
|
state.cfg.SetArray("defaultCalendars", defCal)
|
2017-02-02 17:03:02 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-23 17:20:29 +00:00
|
|
|
state.defaultCalendars = defCal
|
|
|
|
|
|
|
|
loadAccountState()
|
2017-02-02 17:03:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func DoVersionCheck() {
|
|
|
|
confVer, _ := state.cfg.GetInt("version")
|
|
|
|
for confVer < state.Version {
|
|
|
|
// Update from confVer to state.Version
|
|
|
|
switch confVer {
|
|
|
|
case 0: // Initializing the app
|
|
|
|
fmt.Println("Initializing gocal")
|
|
|
|
for {
|
|
|
|
fmt.Println("Client Secret Filename (Ctrl-C to exit): ")
|
|
|
|
var fn string
|
|
|
|
if _, err := fmt.Scan(&fn); err == nil {
|
|
|
|
var b []byte
|
|
|
|
b, err = ioutil.ReadFile(fn)
|
|
|
|
if len(b) > 0 && err == nil {
|
|
|
|
state.cfg.SetBytes("ClientSecret", b)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
state.cfg.SetInt("version", 1)
|
2017-02-16 15:59:33 +00:00
|
|
|
state.cfg.SetArray("defaultCalendars", []string{"primary"})
|
2017-02-02 17:03:02 +00:00
|
|
|
}
|
|
|
|
state.ClientSecret = state.cfg.GetBytes("ClientSecret")
|
|
|
|
// Refetch the version from the config
|
|
|
|
confVer, _ = state.cfg.GetInt("version")
|
|
|
|
}
|
|
|
|
}
|
2017-02-23 17:20:29 +00:00
|
|
|
|
|
|
|
// saveAccountState saves all the bits of cache from the account
|
|
|
|
// into our config
|
|
|
|
func saveAccountState() {
|
|
|
|
var err error
|
|
|
|
var calListJson []byte
|
|
|
|
if calListJson, err = json.Marshal(state.account.CalendarList); err == nil {
|
|
|
|
if err = state.cfg.SetBytes("calendarList", calListJson); err != nil {
|
|
|
|
fmt.Println("saveAccountState:calendarList error:", err)
|
|
|
|
}
|
|
|
|
if err = state.cfg.SetDateTime("calListUseBy", state.account.CalListUseBy); err != nil {
|
|
|
|
fmt.Println("saveAccountState:calListUseBy error:", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err = state.cfg.SetArray("defaultCalendars", state.defaultCalendars); err != nil {
|
|
|
|
fmt.Println("saveAccountState:defaultCalendars error:", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// loadAccountState loads all the bits of cache from the config
|
|
|
|
// into the account
|
|
|
|
func loadAccountState() {
|
|
|
|
var err error
|
|
|
|
var calListJson []byte
|
|
|
|
calListJson = state.cfg.GetBytes("calendarList")
|
|
|
|
if err = json.Unmarshal(calListJson, &state.account.CalendarList); err == nil {
|
|
|
|
if len(state.account.CalendarList) > 0 {
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("error: ", err)
|
|
|
|
}
|
|
|
|
if state.account.CalListUseBy, err = state.cfg.GetDateTime("calListUseBy"); err != nil {
|
|
|
|
fmt.Println("error: ", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fmt.Println("error: ", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// BEGIN CONFIG SCREEN FUNCTIONS //
|
|
|
|
func DoConfig() {
|
|
|
|
g, err := gocui.NewGui(gocui.Output256)
|
|
|
|
if err != nil {
|
|
|
|
log.Panicln(err)
|
|
|
|
}
|
|
|
|
defer g.Close()
|
|
|
|
g.Cursor = true
|
|
|
|
g.SetManagerFunc(configLayout)
|
|
|
|
|
|
|
|
// Set up some keybindings
|
|
|
|
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
|
|
|
|
log.Panicln(err)
|
|
|
|
}
|
|
|
|
if err := g.SetKeybinding("default_calendars", gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil {
|
|
|
|
log.Panicln(err)
|
|
|
|
}
|
|
|
|
if err := g.SetKeybinding("default_calendars", 'k', gocui.ModNone, cursorUp); err != nil {
|
|
|
|
log.Panicln(err)
|
|
|
|
}
|
|
|
|
if err := g.SetKeybinding("default_calendars", gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil {
|
|
|
|
log.Panicln(err)
|
|
|
|
}
|
|
|
|
if err := g.SetKeybinding("default_calendars", 'j', gocui.ModNone, cursorDown); err != nil {
|
|
|
|
log.Panicln(err)
|
|
|
|
}
|
|
|
|
if err := g.SetKeybinding("default_calendars", gocui.KeySpace, gocui.ModNone, toggleCalendar); err != nil {
|
|
|
|
log.Panicln(err)
|
|
|
|
}
|
|
|
|
if err := g.SetKeybinding("default_calendars", gocui.KeyEnter, gocui.ModNone, toggleCalendar); err != nil {
|
|
|
|
log.Panicln(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Kick off the main loop
|
|
|
|
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
|
|
|
|
log.Panicln(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func configLayout(g *gocui.Gui) error {
|
|
|
|
list := state.account.GetCalendarList()
|
|
|
|
height := len(list) + 4
|
|
|
|
width, _ := g.Size()
|
|
|
|
if v, err := g.SetView("default_calendars", 0, 0, width-1, height); err != nil {
|
|
|
|
v.Highlight = true
|
|
|
|
v.SelBgColor = gocui.ColorGreen
|
|
|
|
v.SelFgColor = gocui.ColorBlack
|
|
|
|
if err != gocui.ErrUnknownView {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
drawCalList(g, v)
|
|
|
|
|
|
|
|
if _, err := g.SetCurrentView("default_calendars"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func quit(g *gocui.Gui, v *gocui.View) error {
|
|
|
|
return gocui.ErrQuit
|
|
|
|
}
|
|
|
|
|
|
|
|
func toggleCalendar(g *gocui.Gui, v *gocui.View) error {
|
|
|
|
_, cy := v.Cursor()
|
|
|
|
calList := state.account.GetCalendarList()
|
|
|
|
if v != nil {
|
|
|
|
for i := range state.defaultCalendars {
|
|
|
|
state.StatusMsg = time.Now().Format(time.RFC3339)
|
|
|
|
if state.defaultCalendars[i] == calList[cy].Id {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
drawCalList(g, v)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func cursorUp(g *gocui.Gui, v *gocui.View) error {
|
|
|
|
if v != nil {
|
|
|
|
ox, oy := v.Origin()
|
|
|
|
cx, cy := v.Cursor()
|
|
|
|
if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
|
|
|
|
if err := v.SetOrigin(ox, oy-1); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func cursorDown(g *gocui.Gui, v *gocui.View) error {
|
|
|
|
if v != nil {
|
|
|
|
cx, cy := v.Cursor()
|
|
|
|
if err := v.SetCursor(cx, cy+1); err != nil {
|
|
|
|
ox, oy := v.Origin()
|
|
|
|
if err := v.SetOrigin(ox, oy+1); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func drawCalList(g *gocui.Gui, v *gocui.View) error {
|
|
|
|
width, _ := g.Size()
|
|
|
|
list := state.account.GetCalendarList()
|
|
|
|
for i := range list {
|
|
|
|
isDef := false
|
|
|
|
for defIdx := range state.defaultCalendars {
|
|
|
|
if list[i].Id == state.defaultCalendars[defIdx] {
|
|
|
|
isDef = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
calSumTxt := "["
|
|
|
|
if isDef {
|
|
|
|
calSumTxt += "*"
|
|
|
|
} else {
|
|
|
|
calSumTxt += " "
|
|
|
|
}
|
|
|
|
calSumTxt += "] " + list[i].Summary
|
|
|
|
spc := strings.Repeat(" ", width-len(calSumTxt)-2)
|
|
|
|
fmt.Fprintln(v, calSumTxt+spc)
|
|
|
|
}
|
|
|
|
fmt.Fprintln(v, "Status: "+state.StatusMsg)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// END CONFIG SCREEN FUNCTIONS //
|