Merge 818efda38d
into 0d6966726e
This commit is contained in:
commit
ce7bc5ffff
5 changed files with 236 additions and 17 deletions
1
go.mod
1
go.mod
|
@ -12,6 +12,7 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/arran4/golang-ical v0.3.1 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.10.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/ebitengine/purego v0.8.2 // indirect
|
||||
|
|
11
go.sum
11
go.sum
|
@ -1,3 +1,6 @@
|
|||
github.com/arran4/golang-ical v0.3.1 h1:v13B3eQZ9VDHTAvT6M11vVzxYgcYmjyPBE2eAZl3VZk=
|
||||
github.com/arran4/golang-ical v0.3.1/go.mod h1:LZWxF8ZIu/sjBVUCV0udiVPrQAgq3V0aa0RfbO99Qkk=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/PuerkitoBio/goquery v1.10.1 h1:Y8JGYUkXWTGRB6Ars3+j3kN0xg1YqqlwvdTV8WTFQcU=
|
||||
github.com/PuerkitoBio/goquery v1.10.1/go.mod h1:IYiHrOMps66ag56LEH7QYDDupKXyo5A8qrjIx3ZtujY=
|
||||
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
||||
|
@ -17,6 +20,9 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
|||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4=
|
||||
|
@ -28,6 +34,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
|
@ -126,5 +133,9 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb
|
|||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -1094,6 +1094,64 @@ details[open] .summary::after {
|
|||
filter: invert(1);
|
||||
}
|
||||
|
||||
.calendar-day {
|
||||
width: calc(100% / 7);
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0.6rem 0;
|
||||
}
|
||||
|
||||
|
||||
.calendar-day-today {
|
||||
border-radius: var(--border-radius);
|
||||
background-color: hsl(
|
||||
var(--bghs),
|
||||
calc(var(--scheme) (var(--scheme) (var(--bgl)) + 6%))
|
||||
);
|
||||
color: var(--color-text-highlight);
|
||||
}
|
||||
|
||||
.calendar-isevent {
|
||||
color: var(--color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.tooltiptext {
|
||||
transition: 0.5s;
|
||||
}
|
||||
/* Tooltip text */
|
||||
.tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 150px;
|
||||
background-color: hsl(
|
||||
var(--bghs),
|
||||
calc(var(--scheme) (var(--scheme) (var(--bgl)) + 6%))
|
||||
);
|
||||
color: var(--color-text-highlight);
|
||||
text-align: center;
|
||||
padding: 5px 0;
|
||||
border-radius: 6px;
|
||||
opacity: 0;
|
||||
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-bottom: 5px;
|
||||
margin-left: -75px;
|
||||
}
|
||||
|
||||
.calendar-day:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.old-calendar-day {
|
||||
width: calc(100% / 7);
|
||||
text-align: center;
|
||||
|
|
|
@ -2,6 +2,45 @@
|
|||
|
||||
{{ define "widget-content" }}
|
||||
<div class="widget-small-content-bounds">
|
||||
<div class="calendar" data-first-day-of-week="{{ .FirstDay }}"></div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="color-highlight size-h1">{{ .Calendar.CurrentMonthName }}</div>
|
||||
<ul class="list-horizontal-text color-highlight size-h4">
|
||||
<li>Week {{ .Calendar.CurrentWeekNumber }}</li>
|
||||
<li>{{ .Calendar.CurrentYear }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap size-h6 margin-top-10 color-subdue">
|
||||
{{ if .StartSunday }}
|
||||
<div class="calendar-day">Su</div>
|
||||
{{ end }}
|
||||
<div class="calendar-day">Mo</div>
|
||||
<div class="calendar-day">Tu</div>
|
||||
<div class="calendar-day">We</div>
|
||||
<div class="calendar-day">Th</div>
|
||||
<div class="calendar-day">Fr</div>
|
||||
<div class="calendar-day">Sa</div>
|
||||
{{ if not .StartSunday }}
|
||||
<div class="calendar-day">Su</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap justify-center items-center">
|
||||
{{ range .Calendar.Days }}
|
||||
<div class="calendar-day{{ if eq .Day $.Calendar.CurrentDay }} calendar-day-today{{ end }} {{ if .IsEvent}} calendar-isevent{{ end }}">
|
||||
{{ if .IsEvent}}
|
||||
<div class="tooltip">
|
||||
<span class="tooltiptext">
|
||||
{{ range .Events }}
|
||||
{{ .EventHover }}
|
||||
<br>
|
||||
{{ end }}
|
||||
</span>
|
||||
</div>
|
||||
{{ end }}
|
||||
<span>{{ .Day }}</span>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
|
|
@ -1,32 +1,75 @@
|
|||
package glance
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ics "github.com/arran4/golang-ical"
|
||||
)
|
||||
|
||||
var calendarWidgetTemplate = mustParseTemplate("calendar.html", "widget-base.html")
|
||||
|
||||
var calendarWeekdaysToInt = map[string]time.Weekday{
|
||||
"sunday": time.Sunday,
|
||||
"monday": time.Monday,
|
||||
"tuesday": time.Tuesday,
|
||||
"wednesday": time.Wednesday,
|
||||
"thursday": time.Thursday,
|
||||
"friday": time.Friday,
|
||||
"saturday": time.Saturday,
|
||||
type CalendarEvent struct {
|
||||
StartedDay time.Time
|
||||
EventHover string
|
||||
}
|
||||
type CalendayDay struct {
|
||||
Day int
|
||||
IsEvent bool
|
||||
Events []CalendarEvent
|
||||
}
|
||||
|
||||
type calendarWidget struct {
|
||||
widgetBase `yaml:",inline"`
|
||||
FirstDayOfWeek string `yaml:"first-day-of-week"`
|
||||
FirstDay int `yaml:"-"`
|
||||
cachedHTML template.HTML `yaml:"-"`
|
||||
widgetBase `yaml:",inline"`
|
||||
Calendar *calendar
|
||||
StartSunday bool `yaml:"start-sunday"`
|
||||
Icsurl string
|
||||
}
|
||||
|
||||
func (widget *calendarWidget) initialize() error {
|
||||
widget.withTitle("Calendar").withError(nil)
|
||||
widget.withTitle("Calendar").withCacheOnTheHour()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (widget *calendarWidget) update(ctx context.Context) {
|
||||
widget.Calendar = newCalendar(time.Now(), widget.StartSunday, widget.Icsurl)
|
||||
fmt.Println(widget.Calendar.Days)
|
||||
widget.withError(nil).scheduleNextUpdate()
|
||||
}
|
||||
|
||||
func (widget *calendarWidget) Render() template.HTML {
|
||||
return widget.renderTemplate(widget, calendarWidgetTemplate)
|
||||
}
|
||||
|
||||
type calendar struct {
|
||||
CurrentDay int
|
||||
CurrentWeekNumber int
|
||||
CurrentMonthName string
|
||||
CurrentYear int
|
||||
Days []CalendayDay
|
||||
Icsurl string `yaml:"icsurl"`
|
||||
}
|
||||
|
||||
// TODO: very inflexible, refactor to allow more customizability
|
||||
// TODO: allow changing between showing the previous and next week and the entire month
|
||||
func newCalendar(now time.Time, startSunday bool, icsurl string) *calendar {
|
||||
year, week := now.Year(), int(now.Weekday())
|
||||
weekday := now.Weekday()
|
||||
if !startSunday {
|
||||
weekday = (weekday + 6) % 7 // Shift Monday to 0
|
||||
}
|
||||
|
||||
currentMonthDays := daysInMonth(now.Month(), year)
|
||||
|
||||
var previousMonthDays int
|
||||
|
||||
if widget.FirstDayOfWeek == "" {
|
||||
widget.FirstDayOfWeek = "monday"
|
||||
|
@ -34,12 +77,79 @@ func (widget *calendarWidget) initialize() error {
|
|||
return errors.New("invalid first day of week")
|
||||
}
|
||||
|
||||
widget.FirstDay = int(calendarWeekdaysToInt[widget.FirstDayOfWeek])
|
||||
widget.cachedHTML = widget.renderTemplate(widget, calendarWidgetTemplate)
|
||||
startDaysFrom := now.Day() - int(weekday) - 7
|
||||
|
||||
return nil
|
||||
days := make([]CalendayDay, 21)
|
||||
events, _ := ReadPublicIcs(icsurl)
|
||||
|
||||
for i := 0; i < 21; i++ {
|
||||
day := startDaysFrom + i
|
||||
month := now.Month()
|
||||
|
||||
if day < 1 {
|
||||
day = previousMonthDays + day
|
||||
month -= 1
|
||||
|
||||
} else if day > currentMonthDays {
|
||||
day = day - currentMonthDays
|
||||
month += 1
|
||||
|
||||
}
|
||||
if events != nil {
|
||||
for _, event := range events {
|
||||
var dayEvent CalendarEvent
|
||||
startAt, err := event.GetStartAt()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
fmt.Println(year)
|
||||
// fmt.Println(startAt.Day() == day && startAt.Month() == month)
|
||||
if startAt.Day() == day && startAt.Month() == month && startAt.Year() == year {
|
||||
dayEvent.StartedDay = startAt
|
||||
dayEvent.EventHover = event.GetProperty("SUMMARY").Value
|
||||
days[i].IsEvent = true
|
||||
days[i].Events = append(days[i].Events, dayEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
days[i].Day = day
|
||||
}
|
||||
|
||||
return &calendar{
|
||||
CurrentDay: now.Day(),
|
||||
CurrentWeekNumber: week,
|
||||
CurrentMonthName: now.Month().String(),
|
||||
CurrentYear: year,
|
||||
Days: days,
|
||||
}
|
||||
}
|
||||
|
||||
func (widget *calendarWidget) Render() template.HTML {
|
||||
return widget.cachedHTML
|
||||
}
|
||||
|
||||
func ParseEventsFromFile(file string) []*ics.VEvent {
|
||||
eventString, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
cal, err := ics.ParseCalendar(strings.NewReader(string(eventString)))
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
events := cal.Events()
|
||||
return events
|
||||
}
|
||||
func ReadPublicIcs(url string) ([]*ics.VEvent, error) {
|
||||
response, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
cal, err := ics.ParseCalendar(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events := cal.Events()
|
||||
return events, nil
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue