# HG changeset patch
# User drewp@bigasterisk.com
# Date 1717721548 25200
# Node ID 20d1fa4250c0137ff6024c26bd9bb6eba63450ab
# Parent 899f12179b853d03de008c8457191ad242f1ca90
refactor
diff -r 899f12179b85 -r 20d1fa4250c0 src/DisplayEvent.ts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/DisplayEvent.ts Thu Jun 06 17:52:28 2024 -0700
@@ -0,0 +1,45 @@
+import { addHours, endOfToday, endOfTomorrow, format, isAfter, isWithinInterval, parseISO, startOfToday } from "date-fns";
+import { TemplateResult, html } from "lit";
+import { DataFactory, NamedNode, Quad_Subject, Store, Term } from "n3";
+import { getLiteral } from "./parseRdf";
+import { hideFeeds, hideTitles } from "./private";
+const EV = "http://bigasterisk.com/event#";
+const { namedNode } = DataFactory;
+
+export class DisplayEvent {
+ constructor(private store: Store, private graph: Term, public uri: Quad_Subject) {}
+ get title(): string {
+ return getLiteral(this.store, this.graph, this.uri, namedNode(EV + "title"), "(unnamed)");
+ }
+ get start(): string {
+ return getLiteral(this.store, this.graph, this.uri, namedNode(EV + "start"), null);
+ }
+ get feed(): NamedNode {
+ return namedNode(getLiteral(this.store, this.graph, this.uri, namedNode(EV + "feed"), null));
+ }
+ shortDate(): TemplateResult {
+ const t = parseISO(this.start);
+ return html`${format(t, "EEE, MMM d,")} ${format(t, "HH:mm")}`;
+ }
+ 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`${daysAway.toFixed(prec)} days`;
+ }
+ show(): boolean {
+ const now = new Date();
+ const t = parseISO(this.start);
+
+ const start = startOfToday();
+ let end = endOfToday();
+ if (isAfter(now, addHours(startOfToday(), 18))) {
+ end = endOfTomorrow();
+ }
+
+ return isWithinInterval(t, { start, end }) && !hideTitles.has(this.title) && !hideFeeds.has(this.feed.value);
+ }
+}
diff -r 899f12179b85 -r 20d1fa4250c0 src/FdClock.ts
--- a/src/FdClock.ts Thu Jun 06 17:51:38 2024 -0700
+++ b/src/FdClock.ts Thu Jun 06 17:52:28 2024 -0700
@@ -1,7 +1,8 @@
import { css, html, LitElement } from "lit";
import { shared } from "./shared";
+import { customElement } from "lit/decorators.js";
-
+@customElement("fd-clock")
export class FdClock extends LitElement {
constructor() {
super();
@@ -25,4 +26,3 @@
return html` ${h}:${m.slice(0, 2)}:${s.slice(0, 2)} `;
}
}
-customElements.define("fd-clock", FdClock);
diff -r 899f12179b85 -r 20d1fa4250c0 src/FdCountdown.ts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/FdCountdown.ts Thu Jun 06 17:52:28 2024 -0700
@@ -0,0 +1,83 @@
+import { LitElement, css, html } from "lit";
+import { customElement, property } from "lit/decorators.js";
+import { sortBy } from "lodash";
+import { DataFactory, Quad_Subject, Store } from "n3";
+import { shared } from "./shared";
+import { EV, fetchGraph, parseGraph, RDF } from "./parseRdf";
+import { DisplayEvent } from "./DisplayEvent";
+
+const { namedNode } = DataFactory;
+
+@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`
Coming Soon
+
+ ${this.evs.map((d) => html`- In ${d.inHowLong()}, ${d.title}
`)}
+
`;
+ }
+}
diff -r 899f12179b85 -r 20d1fa4250c0 src/UpcomingEvents.ts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/UpcomingEvents.ts Thu Jun 06 17:52:28 2024 -0700
@@ -0,0 +1,62 @@
+import { LitElement, css, html } from "lit";
+import { customElement, property } from "lit/decorators.js";
+import { sortBy } from "lodash";
+import { DataFactory, Quad_Subject, Store } from "n3";
+import { shared } from "./shared";
+import { EV, fetchGraph, parseGraph, RDF } from "./parseRdf";
+import { DisplayEvent } from "./DisplayEvent";
+
+const { namedNode } = DataFactory;
+
+@customElement("fd-upcoming-events")
+export class UpcomingEvents extends LitElement {
+ static styles = [
+ shared,
+ css`
+ ol {
+ list-style-type: circle;
+ font-size: 16px;
+ background: #cd66bb2e;
+ padding: 9px;
+ width: fit-content;
+ position: relative;
+ top: -21px;
+ border-radius: 14px;
+ }
+ span.d {
+ opacity: 0.5;
+ }
+ `,
+ ];
+ @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`
+ Calendar
+
+ ${this.evs.map((d) => html`- ${d.shortDate()} ${d.title}
`)}
+
+ `;
+ }
+}
diff -r 899f12179b85 -r 20d1fa4250c0 src/main.ts
--- a/src/main.ts Thu Jun 06 17:51:38 2024 -0700
+++ b/src/main.ts Thu Jun 06 17:52:28 2024 -0700
@@ -1,214 +1,4 @@
-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 { FdCountdown } from "./FdCountdown";
+export { UpcomingEvents } from "./UpcomingEvents";
export { WeekGuide } from "./WeekGuide";
-const EV = "http://bigasterisk.com/event#";
-const RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
-
-function getLiteral(store: Store, graph: Term, subj: Quad_Subject, pred: Quad_Predicate, missing: string | null): string {
- let out = null;
- store.getObjects(subj, pred, graph).forEach((attr) => {
- out = attr.value;
- });
- if (!out) {
- if (missing === null) {
- throw new Error();
- }
- return missing;
- }
- return out;
-}
-
-class DisplayEvent {
- constructor(private store: Store, private graph: Term, public uri: Quad_Subject) {}
- get title(): string {
- return getLiteral(this.store, this.graph, this.uri, namedNode(EV + "title"), "(unnamed)");
- }
- get start(): string {
- return getLiteral(this.store, this.graph, this.uri, namedNode(EV + "start"), null);
- }
- get feed(): NamedNode {
- return namedNode(getLiteral(this.store, this.graph, this.uri, namedNode(EV + "feed"), null));
- }
- shortDate(): TemplateResult {
- const t = parseISO(this.start);
- return html`${format(t, "EEE, MMM d,")} ${format(t, "HH:mm")}`;
- }
- 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`${daysAway.toFixed(prec)} days`;
- }
- show(): boolean {
- const now = new Date();
- const t = parseISO(this.start);
-
- const start = startOfToday();
- let end = endOfToday();
- if (isAfter(now, addHours(startOfToday(), 18))) {
- end = endOfTomorrow();
- }
-
- return isWithinInterval(t, { start, end }) && !hideTitles.has(this.title) && !hideFeeds.has(this.feed.value);
- }
-}
-
-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 {
- static styles = [
- shared,
- css`
- ol {
- list-style-type: circle;
- font-size: 16px;
- background: #cd66bb2e;
- padding: 9px;
- width: fit-content;
- position: relative;
- top: -21px;
- border-radius: 14px;
- }
- span.d {
- opacity: 0.5;
- }
- `,
- ];
- @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`
- Calendar
-
- ${this.evs.map((d) => html`- ${d.shortDate()} ${d.title}
`)}
-
- `;
- }
-}
-
-@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`Coming Soon
-
- ${this.evs.map((d) => html`- In ${d.inHowLong()}, ${d.title}
`)}
-
`;
- }
-}
diff -r 899f12179b85 -r 20d1fa4250c0 src/parseRdf.ts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/parseRdf.ts Thu Jun 06 17:52:28 2024 -0700
@@ -0,0 +1,40 @@
+import { Parser, Quad_Predicate, Quad_Subject, Store, Term } from "n3";
+export const EV = "http://bigasterisk.com/event#";
+export const RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+
+export function getLiteral(store: Store, graph: Term, subj: Quad_Subject, pred: Quad_Predicate, missing: string | null): string {
+ let out = null;
+ store.getObjects(subj, pred, graph).forEach((attr) => {
+ out = attr.value;
+ });
+ if (!out) {
+ if (missing === null) {
+ throw new Error();
+ }
+ return missing;
+ }
+ return out;
+}
+
+export 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),
+ },
+ });
+}
+
+export 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);
+ }
+ });
+}