aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/caldav/routes.go60
-rw-r--r--themes/alps/assets/style.css101
-rw-r--r--themes/alps/calendar-header.html18
-rw-r--r--themes/alps/calendar.html84
4 files changed, 262 insertions, 1 deletions
diff --git a/plugins/caldav/routes.go b/plugins/caldav/routes.go
index cb8aa2a..d7c31f7 100644
--- a/plugins/caldav/routes.go
+++ b/plugins/caldav/routes.go
@@ -18,9 +18,16 @@ import (
type CalendarRenderData struct {
alps.BaseRenderData
Time time.Time
+ Now time.Time
+ Dates [7 * 6]time.Time
Calendar *caldav.Calendar
Events []CalendarObject
PrevPage, NextPage string
+ PrevTime, NextTime time.Time
+
+ EventsForDate func(time.Time) []CalendarObject
+ DaySuffix func(n int) string
+ Sub func(a, b int) int
}
type EventRenderData struct {
@@ -96,13 +103,66 @@ func registerRoutes(p *alps.GoPlugin, u *url.URL) {
return fmt.Errorf("failed to query calendar: %v", err)
}
+ // TODO: Time zones are hard
+ var dates [7 * 6]time.Time
+ initialDate := start.UTC()
+ initialDate = initialDate.AddDate(0, 0, -int(initialDate.Weekday()))
+ for i := 0; i < len(dates); i += 1 {
+ dates[i] = initialDate
+ initialDate = initialDate.AddDate(0, 0, 1)
+ }
+
+ eventMap := make(map[time.Time][]CalendarObject)
+ for _, ev := range events {
+ ev := ev // make a copy
+ // TODO: include event on each date for which it is active
+ co := ev.Data.Events()[0]
+ startTime, _ := co.DateTimeStart(nil)
+ startTime = startTime.UTC().Truncate(time.Hour * 24)
+ eventMap[startTime] = append(eventMap[startTime], CalendarObject{&ev})
+ }
+
return ctx.Render(http.StatusOK, "calendar.html", &CalendarRenderData{
BaseRenderData: *alps.NewBaseRenderData(ctx),
Time: start,
+ Now: time.Now(), // TODO: Use client time zone
Calendar: calendar,
+ Dates: dates,
Events: newCalendarObjectList(events),
PrevPage: start.AddDate(0, -1, 0).Format(monthPageLayout),
NextPage: start.AddDate(0, 1, 0).Format(monthPageLayout),
+ PrevTime: start.AddDate(0, -1, 0),
+ NextTime: start.AddDate(0, 1, 0),
+
+ EventsForDate: func(when time.Time) []CalendarObject {
+ if events, ok := eventMap[when.Truncate(time.Hour * 24)]; ok {
+ return events
+ }
+ return nil
+ },
+
+ DaySuffix: func(n int) string {
+ if n % 100 >= 11 && n % 100 <= 13 {
+ return "th"
+ }
+ return map[int]string{
+ 0: "th",
+ 1: "st",
+ 2: "nd",
+ 3: "rd",
+ 4: "th",
+ 5: "th",
+ 6: "th",
+ 7: "th",
+ 8: "th",
+ 9: "th",
+ }[n % 10]
+ },
+
+ Sub: func (a, b int) int {
+ // Why isn't this built-in, come on Go
+ return a - b
+ },
})
})
diff --git a/themes/alps/assets/style.css b/themes/alps/assets/style.css
index c62fbbc..8ce6549 100644
--- a/themes/alps/assets/style.css
+++ b/themes/alps/assets/style.css
@@ -261,6 +261,11 @@ main.new-contact .actions > *:not(:last-child) {
margin-right: 1rem;
}
+.actions h3 {
+ align-self: center;
+ margin: 0 1.3rem 0 1rem;
+}
+
.message-list-subject a { color: #77c; }
.message-list-unread.message-list-sender,
@@ -370,7 +375,7 @@ main table tfoot {
padding: 0.3rem 0.5rem;
}
-.actions-pagination .button-link:first-child:not(:last-child) {
+.actions-pagination .button-link:not(:last-child) {
margin-right: 0.3rem;
}
@@ -444,6 +449,100 @@ main table tfoot {
margin-top: 1rem;
}
+main.calendar .dates {
+ flex-grow: 1;
+ padding: 0.3rem;
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
+ grid-template-rows: auto 1fr 1fr 1fr 1fr 1fr 1fr auto;
+ grid-template-areas:
+ "sunday-top monday-top wednesday-top tuesday-top thursday-top friday-top saturday-top"
+ "dates dates dates dates dates dates dates"
+ "dates dates dates dates dates dates dates"
+ "dates dates dates dates dates dates dates"
+ "dates dates dates dates dates dates dates"
+ "dates dates dates dates dates dates dates"
+ "dates dates dates dates dates dates dates"
+ "sunday-bottom monday-bottom wednesday-bottom tuesday-bottom thursday-bottom friday-bottom saturday-bottom";
+ grid-gap: 0.3rem;
+}
+
+main.calendar .dates .weekday {
+ text-align: center;
+ font-size: 1.1rem;
+ font-weight: normal;
+}
+
+main.calendar .dates .sunday-top { grid-area: sunday-top; }
+main.calendar .dates .monday-top { grid-area: monday-top; }
+main.calendar .dates .tuesday-top { grid-area: tuesday-top; }
+main.calendar .dates .wednesday-top { grid-area: wednesday-top; }
+main.calendar .dates .thursday-top { grid-area: thursday-top; }
+main.calendar .dates .friday-top { grid-area: friday-top; }
+main.calendar .dates .saturday-top { grid-area: saturday-top; }
+main.calendar .dates .sunday-bottom { grid-area: sunday-bottom; }
+main.calendar .dates .monday-bottom { grid-area: monday-bottom; }
+main.calendar .dates .tuesday-bottom { grid-area: tuesday-bottom; }
+main.calendar .dates .wednesday-bottom { grid-area: wednesday-bottom; }
+main.calendar .dates .thursday-bottom { grid-area: thursday-bottom; }
+main.calendar .dates .friday-bottom { grid-area: friday-bottom; }
+main.calendar .dates .saturday-bottom { grid-area: saturday-bottom; }
+
+main.calendar .date {
+ border: 1px solid #eee;
+ padding: 0.3rem;
+ background: white;
+ display: flex;
+ flex-direction: column;
+ position: relative;
+}
+
+main.calendar .date.active {
+ background-color: #f6fff6;
+ border: 1px solid #afa;
+}
+
+main.calendar .date .date-link {
+ position: absolute;
+ top: 0; right: 0; bottom: 0; left: 0;
+}
+
+main.calendar .date.extra {
+ background: transparent;
+ border: none;
+}
+
+main.calendar .date ul {
+ list-style: none;
+ margin: 0;
+ padding-left: 0.3rem;
+}
+
+main.calendar .date .events {
+ flex-grow: 1;
+}
+
+main.calendar .date.extra .events {
+ visibility: hidden;
+}
+
+main.calendar .events .start-time {
+ color: #444;
+}
+
+main.calendar .events .overflow {
+ color: #444;
+ text-align: right;
+}
+
+main.calendar .date h4 {
+ font-weight: normal;
+ text-align: right;
+ color: #666;
+}
+
+main.calendar .date h4 .da { font-size: 1.2rem; }
+
input[type="submit"],
.button,
button,
diff --git a/themes/alps/calendar-header.html b/themes/alps/calendar-header.html
new file mode 100644
index 0000000..d902806
--- /dev/null
+++ b/themes/alps/calendar-header.html
@@ -0,0 +1,18 @@
+<div class="actions-wrap">
+ <div class="actions-pagination" style="margin-left: 0;">
+ <a href="?month={{.PrevPage}}" class="button-link">« {{.PrevTime.Format "January"}}</a>
+ <h3>{{.Time.Format "January 2006"}}</h3>
+ <a href="?month={{.NextPage}}" class="button-link">{{.NextTime.Format "January"}} »</a>
+ {{if ne .Time.Month .Now.Month}}
+ <a href="/calendar" class="button-link">Today »</a>
+ {{end}}
+ </div>
+
+ <form method="get" class="actions-search action-group">
+ <input
+ type="text"
+ name="query"
+ placeholder="Search {{.Calendar.Name}} events...">
+ <button>Search</button>
+ </form>
+</div>
diff --git a/themes/alps/calendar.html b/themes/alps/calendar.html
new file mode 100644
index 0000000..5bdd6eb
--- /dev/null
+++ b/themes/alps/calendar.html
@@ -0,0 +1,84 @@
+{{template "head.html" .}}
+{{template "nav.html" .}}
+
+<div class="page-wrap">
+ <aside>
+ <a href="/calendar/create" class="new">New event</a>
+ <!-- TODO: fetch list of address books -->
+ <a href="#" class="active">{{.Calendar.Name}}</a>
+ <a href="#">Personal</a>
+ </aside>
+
+ <div class="container">
+ <main class="calendar">
+ <section class="actions">
+ {{ template "calendar-header.html" . }}
+ </section>
+
+ <section class="dates">
+ <h4 class="weekday sunday-top">Sunday</h4>
+ <h4 class="weekday monday-top">Monday</h4>
+ <h4 class="weekday tuesday-top">Tuesday</h4>
+ <h4 class="weekday wednesday-top">Wednesday</h4>
+ <h4 class="weekday thursday-top">Thursday</h4>
+ <h4 class="weekday friday-top">Friday</h4>
+ <h4 class="weekday saturday-top">Saturday</h4>
+
+ {{$base := .}}
+ {{range .Dates}}
+ <div class="date
+ {{if ne $base.Time.Month .Month}}extra{{end}}
+ {{if and (eq $base.Now.Month .Month) (eq $base.Now.Day .Day)}}active{{end}}
+ ">
+ {{if eq $base.Time.Month .Month}}
+ <a href="#" class="date-link"></a>
+ {{end}}
+ <div class="events">
+ {{$events := (call $base.EventsForDate .)}}
+ {{if $events}}
+ <ul>
+ {{$overflow := 0}}
+ {{if gt (len $events) 3}}
+ {{$overflow = call $base.Sub (len $events) 3}}
+ {{$events = slice $events 0 3}}
+ {{end}}
+
+ {{range $events}}
+ {{$event := index .Data.Events 0}}
+ <li>
+ <span class="start-time">
+ {{($event.DateTimeStart nil).Format "15:04"}}
+ </span>
+ {{$event.Props.Text "SUMMARY"}}
+ </li>
+ {{end}}
+ {{if ne $overflow 0}}
+ <li class="overflow">...and {{$overflow}} more</li>
+ {{end}}
+ </ul>
+ {{end}}
+ </div>
+ <h4>
+ <span class="mo">{{.Format "Jan"}}</span>
+ <span class="da">{{.Format "2"}}{{call $base.DaySuffix .Day}}</span>
+ </h4>
+ </div>
+ {{end}}
+
+ <h4 class="weekday sunday-bottom">Sunday</h4>
+ <h4 class="weekday monday-bottom">Monday</h4>
+ <h4 class="weekday tuesday-bottom">Tuesday</h4>
+ <h4 class="weekday wednesday-bottom">Wednesday</h4>
+ <h4 class="weekday thursday-bottom">Thursday</h4>
+ <h4 class="weekday friday-bottom">Friday</h4>
+ <h4 class="weekday saturday-bottom">Saturday</h4>
+ </section>
+
+ <section class="actions">
+ {{ template "calendar-header.html" . }}
+ </section>
+ </main>
+ </div>
+</div>
+
+{{template "foot.html"}}