initial tui and keyring

This commit is contained in:
Casual 2024-06-21 21:38:41 +03:00
parent 10cdeaae12
commit fc9971696c
7 changed files with 661 additions and 131 deletions

202
caldav.go
View File

@ -1,32 +1,30 @@
package main
import (
// "github.com/studio-b12/gowebdav"
"context"
// "fmt"
webdav "github.com/emersion/go-webdav"
"github.com/emersion/go-webdav/caldav"
// "log"
"time"
"strings"
"time"
)
type TODO struct {
Name string
Description string
Time string
Name string
Desc string
Time string
// Priority int //TODO
// Subtasks []TODO //TODO
// Repeat //TODO
// Alarm //TODO
}
var clientWebDAV *webdav.Client
var client *caldav.Client // clientCalDAV
// var calendarObjects []caldav.CalendarObject
var ctx = context.Background()
// var authSession caldav.Client // clientCalDAV
func (options *Options) InitDAVclients() error {
@ -47,142 +45,143 @@ func (options *Options) InitDAVclients() error {
return nil
}
func GetCalendars() ([]caldav.Calendar,error) {
func GetCalendars() ([]caldav.Calendar, error) {
principal, err := clientWebDAV.FindCurrentUserPrincipal(ctx)
if err != nil {
// Handle error
return nil,err
return nil, err
}
// fmt.Println("principal: ",principal) TODO log
// Find the calendar home set
calendarHomeSet, err := client.FindCalendarHomeSet(ctx, principal)
if err != nil {
return nil,err
return nil, err
}
// Find calendars in the calendar home set
calendars, err := client.FindCalendars(ctx, calendarHomeSet)
if err != nil {
return nil,err
return nil, err
}
return calendars, nil
}
func GetTODOs(calendarPath string) (calendarObjects []caldav.CalendarObject,err error) {
func GetTODOs(calendarPath string) (calendarObjects []caldav.CalendarObject, err error) {
date := time.Now()
dateStart:= time.Date(date.Year(), date.Month(), date.Day(), 23, 59, 59, 0, date.Location()).AddDate(0, 0, -1) //TODO too complex - time.Now().Add(-92 * time.Hour),
dateStart := time.Date(date.Year(), date.Month(), date.Day(), 23, 59, 59, 0, date.Location()).AddDate(0, 0, -1) //TODO too complex - time.Now().Add(-92 * time.Hour),
// dateEnd:= time.Date(date.Year(), date.Month(), date.Day(), 23, 59, 59, 0, date.Location()) //date +1 day
dateEnd:= date.AddDate(0,1,0) //TODO we hard limit pulling events only for this month, to prevent getting tooo much events
dateEnd := date.AddDate(0, 1, 0) //TODO we hard limit pulling events only for this month, to prevent getting tooo much events
calQuery := caldav.CalendarQuery{
CompRequest: caldav.CalendarCompRequest{
Name: "VCALENDAR",
Comps: []caldav.CalendarCompRequest{{
Name: "VTODO",
Props: []string{
"UID",
"SUMMARY",
"COMPLETED",
"DESCRIPTION",
"DUE",
"PRIORITY",
"RELATED-TO",
},
},
},
},
CompFilter: caldav.CompFilter{
Name: "VCALENDAR",
Comps: []caldav.CompFilter{
{
Name: "VTODO",
Start: dateStart,
End: dateEnd,
// FYI: server-side filtering is not supported for Nextcloud
},
},
Name: "VCALENDAR",
Comps: []caldav.CalendarCompRequest{{
Name: "VTODO",
Props: []string{
"UID",
"SUMMARY",
"COMPLETED",
"DESCRIPTION",
"DUE",
"PRIORITY",
"RELATED-TO",
},
},
},
},
CompFilter: caldav.CompFilter{
Name: "VCALENDAR",
Comps: []caldav.CompFilter{
{
Name: "VTODO",
Start: dateStart,
End: dateEnd,
// FYI: server-side filtering is not supported for Nextcloud
},
},
},
}
calendarObjects, err = client.QueryCalendar(ctx, calendarPath, &calQuery)
if err != nil {
return nil,err
return nil, err
}
return calendarObjects,nil
return calendarObjects, nil
}
func ParseDueDateTODOs(calObjs []caldav.CalendarObject,date time.Time) ([]TODO,error) {
var output []TODO
func ParseDueDateTODOs(calObjs []caldav.CalendarObject, date time.Time) ([]TODO, error) {
var output []TODO
for _,calObj := range calObjs {
for _, calObj := range calObjs {
// fmt.Println((*(*calObj.Data).Children[0]).Name)
// TODO STATUS map[] COMPLETED
for _,event := range (*calObj.Data).Children {
for _, event := range (*calObj.Data).Children {
// if (*event).Name == "VTODO" {
var notCompletedTODO, withDate, fromToday bool
//TODO we can optimize there if we encounter wrong state to forcefully stop next analysis
// notCompletedTODO
if (*event).Props["COMPLETED"] == nil {
if ((*event).Props["STATUS"] == nil) { notCompletedTODO = true } else {
if ((*event).Props["STATUS"][0].Value != "COMPLETED") { notCompletedTODO = true }
var notCompletedTODO, withDate, fromToday bool
//TODO we can optimize there if we encounter wrong state to forcefully stop next analysis
// notCompletedTODO
if (*event).Props["COMPLETED"] == nil {
if (*event).Props["STATUS"] == nil {
notCompletedTODO = true
} else {
if (*event).Props["STATUS"][0].Value != "COMPLETED" {
notCompletedTODO = true
}
}
}
// withTodayDate
if (*event).Props["DUE"] != nil {
withDate = true
// fromToday
if strings.HasPrefix((*event).Props["DUE"][0].Value, date.Format("20060102")) { fromToday = true }
// withTodayDate
if (*event).Props["DUE"] != nil {
withDate = true
// fromToday
if strings.HasPrefix((*event).Props["DUE"][0].Value, date.Format("20060102")) {
fromToday = true
}
}
if notCompletedTODO && withDate && fromToday {
var tmpTODO TODO
if (*event).Props["SUMMARY"] != nil {
name := (*event).Props["SUMMARY"][0].Value
tmpTODO.Name = name
} else {
tmpTODO.Name = "<EMPTY>"
}
if (*event).Props["DESCRIPTION"] != nil {
description := (*event).Props["DESCRIPTION"][0].Value
tmpTODO.Desc = description
}
if notCompletedTODO && withDate && fromToday {
var todoTime string
due := (*event).Props["DUE"][0].Value
index := strings.Index(due, "T")
if index != -1 {
str := due[index+1:]
todoTime = str[:2] + ":" + str[2:4]
var tmpTODO TODO
if ((*event).Props["SUMMARY"] != nil) {
name := (*event).Props["SUMMARY"][0].Value
tmpTODO.Name = name
} else {
tmpTODO.Name = "<EMPTY>"
}
if ((*event).Props["DESCRIPTION"] != nil) {
description := (*event).Props["DESCRIPTION"][0].Value
tmpTODO.Description = description
}
var todoTime string
due := (*event).Props["DUE"][0].Value
index := strings.Index(due, "T")
if index != -1 {
str := due[index+1:]
todoTime = str[:2] + ":" + str[2:4]
tmpTODO.Time = todoTime
}
output = append(output, tmpTODO)
tmpTODO.Time = todoTime
}
output = append(output, tmpTODO)
}
// }
}
}
// //TODO sort: time, priority (if no time)
// // it means, put DUE at first, other DUE;VALUE=DATE in priority order
// //TODO color: priority, add time icon if have time
// //TODO UID:7ed30f40-fce1-422c-be3b-0486dcfe8943
// //TODO RELATED-TO:7ed30f40-fce1-422c-be3b-0486dcfe8943 # subtask
// //TODO PRIORITY:1 #1-high, 5-mid, 9-low
//TODO repeat function???
// //TODO sort: time, priority (if no time)
// // it means, put DUE at first, other DUE;VALUE=DATE in priority order
// //TODO color: priority, add time icon if have time
// //TODO UID:7ed30f40-fce1-422c-be3b-0486dcfe8943
// //TODO RELATED-TO:7ed30f40-fce1-422c-be3b-0486dcfe8943 # subtask
// //TODO PRIORITY:1 #1-high, 5-mid, 9-low
//TODO repeat function???
return output,nil
return output, nil
}
//TODO on complete -repeat function
// RRULE:FREQ=WEEKLY;INTERVAL=1
@ -200,20 +199,3 @@ func ParseDueDateTODOs(calObjs []caldav.CalendarObject,date time.Time) ([]TODO,e
// DESCRIPTION:Default Tasks.org description
// END:VALARM
// END:VTODO

33
go.mod
View File

@ -3,32 +3,61 @@ module git.sual.in/casual/tempus
go 1.22.3
require (
github.com/charmbracelet/bubbles v0.18.0
github.com/charmbracelet/bubbletea v0.26.4
github.com/charmbracelet/lipgloss v0.11.0
github.com/emersion/go-webdav v0.5.0
github.com/projectdiscovery/goflags v0.1.56
github.com/zalando/go-keyring v0.2.5
golang.org/x/term v0.18.0
)
require (
github.com/alessio/shellescape v1.4.1 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/charmbracelet/x/ansi v0.1.2 // indirect
github.com/charmbracelet/x/input v0.1.0 // indirect
github.com/charmbracelet/x/term v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.1.0 // indirect
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/microcosm-cc/bluemonday v1.0.25 // indirect
github.com/miekg/dns v1.1.56 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/projectdiscovery/blackrock v0.0.1 // indirect
github.com/projectdiscovery/utils v0.1.3 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
github.com/teambition/rrule-go v1.8.2 // indirect
github.com/tidwall/gjson v1.14.3 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.13.0 // indirect
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

80
go.sum
View File

@ -1,9 +1,31 @@
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
github.com/charmbracelet/bubbletea v0.26.4 h1:2gDkkzLZaTjMl/dQBpNVtnvcCxsh/FCkimep7FC9c40=
github.com/charmbracelet/bubbletea v0.26.4/go.mod h1:P+r+RRA5qtI1DOHNFn0otoNwB4rn+zNAzSj/EXz6xU0=
github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g=
github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8=
github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY=
github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ=
github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28=
github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI=
github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw=
github.com/charmbracelet/x/windows v0.1.0 h1:gTaxdvzDM5oMa/I2ZNF7wN78X/atWemG9Wph7Ika2k4=
github.com/charmbracelet/x/windows v0.1.0/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ=
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ=
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4=
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f h1:feGUUxxvOtWVOhTko8Cbmp33a+tU0IMZxMEmnkoAISQ=
@ -11,16 +33,42 @@ github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f/go.mod h1:2MKFUgf
github.com/emersion/go-vcard v0.0.0-20230815062825-8fda7d206ec9/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM=
github.com/emersion/go-webdav v0.5.0 h1:Ak/BQLgAihJt/UxJbCsEXDPxS5Uw4nZzgIMOq3rkKjc=
github.com/emersion/go-webdav v0.5.0/go.mod h1:ycyIzTelG5pHln4t+Y32/zBvmrM7+mV7x+V+Gx4ZQno=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg=
github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -31,8 +79,16 @@ github.com/projectdiscovery/goflags v0.1.56 h1:tJYiZN7s9Jk9DxfYOUiqOoybaIDlXyX4Z
github.com/projectdiscovery/goflags v0.1.56/go.mod h1:DsGF0NPpM5hGg75N3MTSvWJ4MIT7HFEAOEeWZ074+Fg=
github.com/projectdiscovery/utils v0.1.3 h1:yhHkrbYZA1eOO8e+fPDUvRMS5aUIalyM3Nab7rK4tpg=
github.com/projectdiscovery/utils v0.1.3/go.mod h1:gny8RbNYXE55IoamF6thRDQ8tcJEw+r0FOGAvncz/oQ=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y=
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/teambition/rrule-go v1.7.2/go.mod h1:mBJ1Ht5uboJ6jexKdNUJg2NcwP8uUMNvStWXlJD3MvU=
@ -44,20 +100,30 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/zalando/go-keyring v0.2.5 h1:Bc2HHpjALryKD62ppdEzaFG6VxL6Bc+5v0LYpN8Lba8=
github.com/zalando/go-keyring v0.2.5/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

121
keyring.go Normal file
View File

@ -0,0 +1,121 @@
package main
import (
// "log"
"github.com/zalando/go-keyring"
// "github.com/99designs/keyring"
"encoding/base64"
// "fmt"
"strings"
)
const (
service = "Tempus"
user = "login"
)
// func debugKeyring() {
// service := "Tempus"
// user := "login"
// password := "secr123123et"
// encoded := base64.StdEncoding.EncodeToString([]byte(password))
// // set password
// err := keyring.Set(service, user, encoded)
// if err != nil {
// log.Fatal(err)
// }
//
// // get password
// secret, err := keyring.Get(service, user)
// if err != nil {
// log.Fatal(err)
// }
//
// log.Println(secret)
//
// decodedByte, err := base64.StdEncoding.DecodeString(secret)
// decoded := string(decodedByte)
// if err != nil {
// fmt.Println("decode error:", err)
// return
// }
// log.Println(decoded)
// }
func storeCredentialsToKeyring(url,login,password string) error {
url = base64.StdEncoding.EncodeToString([]byte(url))
login = base64.StdEncoding.EncodeToString([]byte(login))
password = base64.StdEncoding.EncodeToString([]byte(password))
credentials := url + " " + login + " " + password
err := keyring.Set(service, user, credentials)
if err != nil {
return err
}
return nil
}
func getCredentialsFromKeyring() (url,login,password string, err error) {
secret, err := keyring.Get(service, user)
if err != nil {
return "","","",err
}
v := strings.Split(secret," ")
urlByte, err := base64.StdEncoding.DecodeString(v[0])
if err != nil {
return "","","",err
}
loginByte, err := base64.StdEncoding.DecodeString(v[1])
if err != nil {
return "","","",err
}
passwordByte, err := base64.StdEncoding.DecodeString(v[2])
if err != nil {
return "","","",err
}
url = string(urlByte)
login = string(loginByte)
password = string(passwordByte)
return
}
// func debugKeyring() {
//
// kr, err := keyring.Open(keyring.Config{
// AllowedBackends: []keyring.BackendType{
// keyring.SecretServiceBackend,
// },
// LibSecretCollectionName: "Defaultkeyring",
// ServiceName: "myapp",
// })
// if err != nil {
// log.Fatal(err)
// }
//
// err = kr.Set(keyring.Item{
// Key: "foo",
// // Data: []byte("secret-bar"),
// })
// if err != nil {
// log.Fatal(err)
// }
//
// v, err := kr.Get("llamas")
// if err != nil {
// log.Fatal(err)
// }
//
// log.Printf("llamas was %v", v)
//
//
//
// }

78
main.go
View File

@ -5,7 +5,12 @@ import (
"os"
"sync"
"time"
)
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/bubbles/list"
// "strconv"
)
var waitGroup sync.WaitGroup
@ -20,25 +25,82 @@ func main() {
options, err := ParseOptions()
errHandler(err, "Error parsing options")
// debugKeyring()
err = storeCredentialsToKeyring("https://pkg.go.dev/encoding/base64#Encoding.EncodeToString","casual","h>ÕdzPlÇqQ+çCQ{ð±;Kм7¸Âhð~Ümy)v")
errHandler(err, "Error parsing options")
// url1,login1,password1,err := getCredentialsFromKeyring()
// errHandler(err, "Error parsing options")
// m.loginInputs[url].Value()
// m.loginInputs[login].Value()
// m.loginInputs[pass].Value()
// if true {os.Exit(1)}
options.InitDAVclients()
calendars, err := GetCalendars()
errHandler(err, "Error getting calendars")
for _,calendar := range calendars {
for _, calendar := range calendars {
fmt.Println(calendar.Name, "-", calendar.Path)
}
calendarObjects, err := GetTODOs(calendars[1].Path)
errHandler(err, "Error getting TODOs")
today := time.Now()
todos,err := ParseDueDateTODOs(calendarObjects ,today)
todayTodos, err := ParseDueDateTODOs(calendarObjects, today)
tomorrow := time.Now().AddDate(0,0,1)
tomorrowTodos, err := ParseDueDateTODOs(calendarObjects, tomorrow)
fmt.Println(todos)
// fmt.Println(todos)
fmt.Println("In total we have",len(calendarObjects), "todos")
fmt.Println("In total we have", len(calendarObjects), "todos")
var itemsToday []list.Item
var itemsTomorrow []list.Item
for _,todo := range todayTodos {
itemsToday = append(itemsToday,todo)
}
for _,todo := range tomorrowTodos {
itemsTomorrow = append(itemsTomorrow,todo)
}
m := InitModel()
m.TodayTab = list.New(itemsToday, list.NewDefaultDelegate(), 0, 0)
m.TodayTab.Title = "Today"
m.TomorrowTab = list.New(itemsTomorrow, list.NewDefaultDelegate(), 0, 0)
m.TomorrowTab.Title = "Tomorrow"
// m.LoggedIn = true
m.ActiveWindow = "login"
p := tea.NewProgram(m, tea.WithAltScreen())
if _, err := p.Run(); err != nil {
fmt.Println("Error running program:", err)
os.Exit(1)
}
fmt.Println(m.loginInputs[login].Value())
}

270
tui.go Normal file
View File

@ -0,0 +1,270 @@
package main
import (
"fmt"
// "os"
// "strings"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/textinput"
// textblink "github.com/charmbracelet/bubbles/textinput"
// "github.com/erikgeiser/promptkit/textinput"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"golang.org/x/term"
)
var docStyle = lipgloss.NewStyle().Margin(1, 2)
var loginStyle = lipgloss.NewStyle().Width(40).Align(lipgloss.Center).BorderStyle(lipgloss.NormalBorder())
var inputStyle = lipgloss.NewStyle()
func (i TODO) Title() string { return i.Name }
func (i TODO) Description() string { return i.Desc }
func (i TODO) FilterValue() string { return i.Name }
type model struct {
Tabs []string
// TabContent []string
LoggedIn bool
ActiveWindow string
TodayTab list.Model
TomorrowTab list.Model
loginInputs []textinput.Model
focused int
err error
}
func (m model) Init() tea.Cmd {
return textinput.Blink
// return nil
}
const (
url = iota
login
pass
)
//TODO add changing calendar
func InitModel() model {
var inputs []textinput.Model = make([]textinput.Model, 3)
inputs[url] = textinput.New()
inputs[url].Placeholder = "https://nextcloud.example/remote.php/dav"
inputs[url].Focus()
// inputs[url].CharLimit = 20
inputs[url].Width = 30
inputs[url].Prompt = ""
// inputs[url].Validate = urlValidator
inputs[login] = textinput.New()
inputs[login].Placeholder = "username"
// inputs[login].CharLimit = 5
inputs[login].Width = 30
inputs[login].Prompt = ""
// inputs[login].Validate = loginValidator
inputs[pass] = textinput.New()
inputs[pass].Placeholder = "MySecurePassword"
// inputs[pass].CharLimit = 3
inputs[pass].Width = 30
inputs[pass].Prompt = ""
// inputs[pass].Validate = passValidator
output := model{
Tabs: []string{"Today", "Tomorrow", "Add"},
loginInputs: inputs,
focused: 0,
err: nil,
// TabContent: []string{"ERROR?", "Mascara Tab", "Foundation Tab"},
}
return output
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
if m.ActiveWindow == "login" {
switch keypress := msg.String(); keypress {
case "ctrl+c", "q":
return m, tea.Quit
case "enter":
if m.focused == len(m.loginInputs)-1 {
//TODO submit
// try login -> store -> move to getting stuff
return m, tea.Quit
}
m.nextInput()
case "shift+tab", "up":
m.prevInput()
case "tab", "down":
m.nextInput()
}
for i := range m.loginInputs {
m.loginInputs[i].Blur()
}
m.loginInputs[m.focused].Focus()
}
switch keypress := msg.String(); keypress {
case "ctrl+c", "q":
return m, tea.Quit
// case "right", "l", "n", "tab":
//TODO add to help
case "tab":
if m.LoggedIn {
h, v := docStyle.GetFrameSize()
width, height, _ := term.GetSize(0)
switch m.ActiveWindow {
case "today":
m.ActiveWindow = "tomorrow"
m.TomorrowTab.SetSize(width-h, height-v)
case "tomorrow":
m.ActiveWindow = "today"
m.TodayTab.SetSize(width-h, height-v)
}
return m, nil
}
// case "a" {
// TODO add new element
// return m, tea.Quit
// }
case "t":
// TODO add new element
return m, tea.Quit
}
case tea.WindowSizeMsg:
h, v := docStyle.GetFrameSize()
switch m.ActiveWindow {
case "login":
m.TodayTab.SetSize(msg.Width-h, msg.Height-v)
case "today":
m.TodayTab.SetSize(msg.Width-h, msg.Height-v)
case "tomorrow":
m.TomorrowTab.SetSize(msg.Width-h, msg.Height-v)
}
}
var cmd tea.Cmd
// text input
switch m.ActiveWindow {
case "login":
for i := range m.loginInputs {
m.loginInputs[i], cmd = m.loginInputs[i].Update(msg)
}
case "today":
m.TodayTab, cmd = m.TodayTab.Update(msg)
case "tomorrow":
m.TomorrowTab, cmd = m.TomorrowTab.Update(msg)
}
// m.TodayTab, cmd = m.TodayTab.Update(msg)
// m.TomorrowTab, cmd = m.TomorrowTab.Update(msg)
return m, cmd
}
func (m model) View() string {
var tabOutput string
switch m.ActiveWindow {
case "login":
width, height, _ := term.GetSize(0)
width -=2
height -=2
loginStyle = loginStyle.
// Width(30).
// Height(height/5).
MarginTop(height/5).
MarginLeft(width/2-20)
// MarginRight(width/3)
tabOutput = loginStyle.Render(m.RenderLogin())
// w, h := lipgloss.Size(tabOutput)
case "today":
tabOutput = docStyle.Render(m.TodayTab.View())
case "tomorrow":
tabOutput = docStyle.Render(m.TomorrowTab.View())
case "":
width, height, _ := term.GetSize(0)
width -=2
height -=2
loginStyle = loginStyle.
Width(width/3).
Height(1).
MarginTop(height/2).
MarginLeft(width/3+2).
MarginRight(width/3)
tabOutput = loginStyle.Render("ERROR")
}
// if m.activeTab == 0 {
// tabOutput = docStyle.Render(m.TodayTab.View())
// }
// if m.activeTab == 1 {
// tabOutput = docStyle.Render(m.TomorrowTab.View())
// }
return tabOutput
}
func (m model) RenderLogin() string {
return fmt.Sprintf(
`%s
%s
%s
%s
%s
%s
%s
%s
`,
inputStyle.Width(30).Align(lipgloss.Center).Render("Login"),
inputStyle.Width(30).Foreground(lipgloss.AdaptiveColor{Dark: "50"}).Render("WebDAV server URL"),
m.loginInputs[url].View(),
inputStyle.Width(30).Foreground(lipgloss.AdaptiveColor{Dark: "50"}).Render("Login"),
m.loginInputs[login].View(),
inputStyle.Width(30).Foreground(lipgloss.AdaptiveColor{Dark: "50"}).Render("Password"),
m.loginInputs[pass].View(), //TODO hide
inputStyle.Render("Continue ->"),
)
// .Align(lipgloss.Center).BorderStyle(lipgloss.NormalBorder())
}
func (m *model) nextInput() {
m.focused = (m.focused + 1) % len(m.loginInputs)
}
// prevInput focuses the previous input field
func (m *model) prevInput() {
m.focused--
// Wrap around
if m.focused < 0 {
m.focused = len(m.loginInputs) - 1
}
}