This commit is contained in:
develoopeer 2025-02-23 14:16:46 +00:00 committed by GitHub
commit ce7bc5ffff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 236 additions and 17 deletions

1
go.mod
View file

@ -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
View file

@ -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=

View file

@ -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;

View file

@ -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 }}

View file

@ -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
}