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> `;
+  }
+}
--- a/src/shared.ts	Thu Jun 06 14:49:34 2024 -0700
+++ b/src/shared.ts	Thu Jun 06 16:39:51 2024 -0700
@@ -29,4 +29,7 @@
   h1:nth-child(2) {
     padding-left: 2.25rem;
   }
+  span.t {
+    color: #50fa7b;
+  }
 `;