Mercurial > code > home > repos > front-door-display
changeset 13:deb0c25655eb
cleanup, add FdClock and Countdown
author | drewp@bigasterisk.com |
---|---|
date | Thu, 06 Jun 2024 16:39:51 -0700 |
parents | 4092f674046d |
children | 899f12179b85 |
files | Dockerfile src/FdClock.ts src/WeekGuide.ts src/index.html src/main.css src/main.ts src/shared.ts |
diffstat | 7 files changed, 177 insertions(+), 60 deletions(-) [+] |
line wrap: on
line diff
--- a/Dockerfile Thu Jun 06 14:49:34 2024 -0700 +++ b/Dockerfile Thu Jun 06 16:39:51 2024 -0700 @@ -5,5 +5,5 @@ COPY package.json ./ RUN pnpm install --loglevel debug -COPY index.html tsconfig.json vite.config.ts ./ +COPY tsconfig.json vite.config.ts ./ COPY src ./src
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/FdClock.ts Thu Jun 06 16:39:51 2024 -0700 @@ -0,0 +1,28 @@ +import { css, html, LitElement } from "lit"; +import { shared } from "./shared"; + + +export class FdClock extends LitElement { + constructor() { + super(); + setInterval(() => { + this.requestUpdate(); + }, 1000); + } + static styles = [ + shared, + css` + :host { + display: inline block; + } + `, + ]; + render() { + const t = new Date(); + const h = t.getHours().toString().padStart(2, "0"); + const m = t.getMinutes().toString().padStart(2, "0"); + const s = t.getSeconds().toString().padStart(2, "0"); + return html` <span class="t">${h}:${m.slice(0, 2)}:${s.slice(0, 2)}</span> `; + } +} +customElements.define("fd-clock", FdClock);
--- a/src/WeekGuide.ts Thu Jun 06 14:49:34 2024 -0700 +++ b/src/WeekGuide.ts Thu Jun 06 16:39:51 2024 -0700 @@ -12,7 +12,8 @@ static styles = [ shared, css` - :host { + :host, + :host > div { display: inline-block; } .wday > span { @@ -48,3 +49,5 @@ `; } } + +
--- a/src/index.html Thu Jun 06 14:49:34 2024 -0700 +++ b/src/index.html Thu Jun 06 16:39:51 2024 -0700 @@ -10,9 +10,9 @@ <script type="module" src="./main.ts"></script> </head> <body> - <fd-week-guide></fd-week-guide> + <div><fd-week-guide></fd-week-guide><fd-clock></fd-clock></div> <fd-upcoming-events></fd-upcoming-events> - <iframe src="/fingerprint/"></iframe> + <fd-countdown></fd-countdown> <script type="module" src="https://bigasterisk.com/lib/bigast/v2/loginBar.js"></script> <bigast-loginbar></bigast-loginbar> <script>
--- a/src/main.css Thu Jun 06 14:49:34 2024 -0700 +++ b/src/main.css Thu Jun 06 16:39:51 2024 -0700 @@ -28,4 +28,9 @@ left: 12em; width: 480px; height: 380px; -} \ No newline at end of file +} +fd-countdown { + position: absolute; + left: 405px; + top: 52px; +}
--- a/src/main.ts Thu Jun 06 14:49:34 2024 -0700 +++ b/src/main.ts Thu Jun 06 16:39:51 2024 -0700 @@ -1,11 +1,12 @@ -import { addHours, endOfToday, endOfTomorrow, format, isAfter, isBefore, isToday, isTomorrow, isWithinInterval, parseISO, startOfToday } from "date-fns"; -import { css, html, LitElement, TemplateResult } from "lit"; +import { addHours, endOfToday, endOfTomorrow, format, isAfter, isWithinInterval, parseISO, startOfToday } from "date-fns"; +import { LitElement, TemplateResult, css, html } from "lit"; import { customElement, property } from "lit/decorators.js"; import { sortBy } from "lodash"; import { DataFactory, NamedNode, Parser, Quad_Predicate, Quad_Subject, Store, Term } from "n3"; import { hideFeeds, hideTitles } from "./private"; import { shared } from "./shared"; const { namedNode } = DataFactory; +export { FdClock } from "./FdClock"; export { WeekGuide } from "./WeekGuide"; const EV = "http://bigasterisk.com/event#"; const RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; @@ -39,6 +40,15 @@ const t = parseISO(this.start); return html`<span class="d">${format(t, "EEE, MMM d,")}</span> <span class="t">${format(t, "HH:mm")}</span>`; } + inHowLong(): TemplateResult { + // returns start()-now, like '5 days' + const t = parseISO(this.start).valueOf(); + const now = Date.now(); + const daysAway = (t - now) / 1000 / 86400; + const prec = daysAway < 2 ? 1 : 0; + const cls = "until " + (daysAway < 2 ? "until-2d" : daysAway < 7 ? "until-7d" : daysAway < 30 ? "until-1mo" : ""); + return html`<span class="${cls}">${daysAway.toFixed(prec)} days</span>`; + } show(): boolean { const now = new Date(); const t = parseISO(this.start); @@ -51,59 +61,33 @@ return isWithinInterval(t, { start, end }) && !hideTitles.has(this.title) && !hideFeeds.has(this.feed.value); } - toHtml(): TemplateResult { - return html` - <li> - <span class="date">${this.shortDate()}</span> ${this.title} - <!--${this.feed}--> - </li> - `; - } +} + +async function fetchGraph(url: string) { + return await fetch(url, { + method: "GET", + headers: { + Accept: "application/json", + "X-Pomerium-Authorization": document.cookie.substring(document.cookie.indexOf("=") + 1), + }, + }); +} + +async function parseGraph(r: Response, done: (store: Store) => void) { + const store = new Store(); + const n3txt = await r.text(); + const parser = new Parser({ format: "application/trig" }); + parser.parse(n3txt, (error, quad, prefixes) => { + if (quad) { + store.addQuad(quad); + } else { + done(store); + } + }); } @customElement("fd-upcoming-events") export class UpcomingEvents extends LitElement { - @property() evs: DisplayEvent[]; - constructor() { - super(); - this.evs = []; - this.load(); - setInterval(this.load.bind(this), 5 * 60 * 1000); - } - - async load() { - const store = new Store(); - const r = await fetch("/gcalendarwatch/graph/calendar/upcoming", - - { - method: 'GET', - headers: { - Accept: 'application/json', - 'X-Pomerium-Authorization': document.cookie.substring( - document.cookie.indexOf('=') + 1, - ), - }, - } - - ); - const n3txt = await r.text(); - const parser = new Parser({ format: "application/trig" }); - parser.parse(n3txt, (error, quad, prefixes) => { - if (quad) { - store.addQuad(quad); - } else { - const graph = namedNode(EV + "gcalendar"); - this.evs = []; - store.getSubjects(namedNode(RDF + "type"), namedNode(EV + "Event"), graph).forEach((ev: Quad_Subject) => { - const de = new DisplayEvent(store, graph, ev); - if (de.show()) { - this.evs = [...this.evs, de]; - } - }); - this.evs = sortBy(this.evs, "start"); - } - }); - } static styles = [ shared, css` @@ -120,17 +104,111 @@ span.d { opacity: 0.5; } - span.t { - color: #50fa7b; - } `, ]; + @property() evs: DisplayEvent[]; + constructor() { + super(); + this.evs = []; + this.load(); + setInterval(this.load.bind(this), 5 * 60 * 1000); + } + + async load() { + const r = await fetchGraph("/gcalendarwatch/graph/calendar/upcoming"); + await parseGraph(r, (store: Store) => { + const graph = namedNode(EV + "gcalendar"); + this.evs = []; + store.getSubjects(namedNode(RDF + "type"), namedNode(EV + "Event"), graph).forEach((ev: Quad_Subject) => { + const de = new DisplayEvent(store, graph, ev); + if (de.show()) { + this.evs = [...this.evs, de]; + } + }); + this.evs = sortBy(this.evs, "start"); + }); + } + render() { return html` <h1 data-text="Calendar">Calendar</h1> <ol> - ${this.evs.map((d) => d.toHtml())} + ${this.evs.map((d) => html`<li><span class="date">${d.shortDate()}</span> ${d.title}</li>`)} </ol> `; } } + +@customElement("fd-countdown") +export class FdCountdown extends LitElement { + static styles = [ + shared, + css` + ol { + list-style: none; + font-size: 16px; + background: #cd66bb2e; + padding: 9px; + width: fit-content; + position: relative; + top: -21px; + border-radius: 14px; + } + span.d { + opacity: 0.5; + } + li:has(.until) { + color: #666; + } + li:has(.until-2d) { + color: #fff; + } + li:has(.until-7d) { + color: #ccc; + } + li:has(.until-30d) { + color: #999; + } + li:has(.until)::before { + display: inline-block; + width: 1.4em; + content: ""; + } + li:has(.until-2d)::before { + content: "🌕"; + } + li:has(.until-7d)::before { + content: "🌙"; + } + li:has(.until-30d)::before { + content: "🌑"; + } + `, + ]; + @property() evs: DisplayEvent[]; + constructor() { + super(); + this.evs = []; + this.load(); + setInterval(this.load.bind(this), 5 * 60 * 1000); + } + async load() { + const store = new Store(); + const r = await fetchGraph("/gcalendarwatch/graph/calendar/countdown"); + await parseGraph(r, (store: Store) => { + const graph = namedNode(EV + "gcalendar"); + this.evs = []; + store.getSubjects(namedNode(RDF + "type"), namedNode(EV + "Event"), graph).forEach((ev: Quad_Subject) => { + const de = new DisplayEvent(store, graph, ev); + this.evs = [...this.evs, de]; + }); + this.evs = sortBy(this.evs, "start"); + }); + } + render() { + return html`<h1 data-text="Coming Soon">Coming Soon</h1> + <ol> + ${this.evs.map((d) => html`<li>In ${d.inHowLong()}, ${d.title}</li>`)} + </ol> `; + } +}