changeset 15:20d1fa4250c0

refactor
author drewp@bigasterisk.com
date Thu, 06 Jun 2024 17:52:28 -0700
parents 899f12179b85
children 719c8cc4d8b2
files src/DisplayEvent.ts src/FdClock.ts src/FdCountdown.ts src/UpcomingEvents.ts src/main.ts src/parseRdf.ts
diffstat 6 files changed, 234 insertions(+), 214 deletions(-) [+]
line wrap: on
line diff
--- /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`<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);
+
+    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);
+  }
+}
--- 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` <span class="t">${h}:${m.slice(0, 2)}:${s.slice(0, 2)}</span> `;
   }
 }
-customElements.define("fd-clock", FdClock);
--- /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`<h1 data-text="Coming Soon">Coming Soon</h1>
+      <ol>
+        ${this.evs.map((d) => html`<li>In ${d.inHowLong()}, ${d.title}</li>`)}
+      </ol> `;
+  }
+}
--- /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`
+      <h1 data-text="Calendar">Calendar</h1>
+      <ol>
+        ${this.evs.map((d) => html`<li><span class="date">${d.shortDate()}</span> ${d.title}</li>`)}
+      </ol>
+    `;
+  }
+}
--- 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`<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);
-
-    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`
-      <h1 data-text="Calendar">Calendar</h1>
-      <ol>
-        ${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> `;
-  }
-}
--- /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);
+    }
+  });
+}