diff --git a/account_struct.go b/account_struct.go index c240c64..39de5dd 100644 --- a/account_struct.go +++ b/account_struct.go @@ -8,6 +8,7 @@ import ( "fmt" "log" "net/http" + "time" "golang.org/x/oauth2" "golang.org/x/oauth2/google" @@ -16,8 +17,10 @@ import ( ) type Account struct { - CC *CalClient - Service *calendar.Service + CC *CalClient + Service *calendar.Service + CalendarList []Calendar + CalListUseBy time.Time } func GetAccount(secret, token []byte) (*Account, error) { @@ -43,20 +46,24 @@ func (a *Account) GetTodaysEvents() []Event { } func (a *Account) GetDefaultCalendar() *Calendar { - c, _ := state.account.Service.CalendarList.Get("primary").Do() + c, _ := a.Service.CalendarList.Get("primary").Do() return GoogleCalendarToLocal(c) } func (a *Account) GetCalendarList() []Calendar { - // TODO: Check if we have the calendar list cached + if time.Now().Before(a.CalListUseBy) { + return a.CalendarList + } var ret []Calendar - calList, err := state.account.Service.CalendarList.List().Do() + calList, err := a.Service.CalendarList.List().Do() if err != nil { return ret } for _, c := range calList.Items { ret = append(ret, *GoogleCalendarToLocal(c)) } + a.CalendarList = ret + a.CalListUseBy = time.Now().Add(time.Hour * 6) return ret } diff --git a/main.go b/main.go index 5376a49..18917a0 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,17 @@ package main import ( + "encoding/json" "fmt" "io/ioutil" "log" "os" "sort" "strings" + "time" "github.com/br0xen/user-config" + "github.com/jroimartin/gocui" ) const ( @@ -17,11 +20,14 @@ const ( ) type AppState struct { - Name string - Version int - ClientSecret []byte - cfg *userConfig.Config - account *Account + Name string + Version int + ClientSecret []byte + cfg *userConfig.Config + account *Account + defaultCalendars []string + + StatusMsg string } var state *AppState @@ -38,9 +44,6 @@ func main() { if len(os.Args) > 1 { op = os.Args[1] - switch os.Args[1] { - case "--reinit": - } } else { op = "today" } @@ -54,6 +57,7 @@ func main() { fmt.Println("Deleting Key: " + v) state.cfg.DeleteKey(v) } + case "today": // Show everything on the calendar for today InitComm() @@ -61,12 +65,16 @@ func main() { list := state.account.GetCalendarList() var todayEvents []Event if len(list) > 0 { - for i := range list { - if list[i].Deleted { - // Deleted calendar, next please - continue + 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()...) + } } - todayEvents = append(todayEvents, list[i].GetTodaysEvents()...) } sort.Sort(ByStartTime(todayEvents)) for _, e := range todayEvents { @@ -80,6 +88,10 @@ func main() { fmt.Printf("No calendars found.\n") } + case "config": + InitComm() + DoConfig() + case "defaults": // Show Defaults InitComm() @@ -97,34 +109,8 @@ func main() { // Just initialize communications and bail InitComm() } - - //fmt.Println("\n-====-\n") - - /* - t := time.Now().Format(time.RFC3339) - events, err := srv.Events.List("primary").ShowDeleted(false). - SingleEvents(true).TimeMin(t).MaxResults(10).OrderBy("startTime").Do() - if err != nil { - log.Fatalf("Unable to retrieve next ten of the user's events. %v", err) - } - - fmt.Println("Upcoming events:") - if len(events.Items) > 0 { - for _, i := range events.Items { - var when string - // If the DateTime is an empty string the Event is an all-day Event. - // So only Date is available. - if i.Start.DateTime != "" { - when = i.Start.DateTime - } else { - when = i.Start.Date - } - fmt.Printf("%s (%s)\n", i.Summary, when) - } - } else { - fmt.Printf("No upcoming events found.\n") - } - */ + // All done, save the account state + saveAccountState() } func InitComm() { @@ -140,15 +126,20 @@ func InitComm() { // Save the Raw Token state.cfg.SetBytes("Token", state.account.CC.GetRawToken()) - // If the 'defaultCalendars' cfg is set to 'primary', we need to actually but the primary cal id in there + // If the 'defaultCalendars' cfg is set to 'primary' (or not at all) + // we need to actually but the primary cal id in there 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 { - state.cfg.SetArray("defaultCalendars", []string{gCal.Id}) + defCal = []string{gCal.Id} + state.cfg.SetArray("defaultCalendars", defCal) } } + state.defaultCalendars = defCal + + loadAccountState() } func DoVersionCheck() { @@ -178,3 +169,171 @@ func DoVersionCheck() { confVer, _ = state.cfg.GetInt("version") } } + +// 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 //