diff --git a/static/ts/app.ts b/static/ts/app.ts index 4912464..454173f 100644 --- a/static/ts/app.ts +++ b/static/ts/app.ts @@ -56,4 +56,10 @@ class App extends HTMLElement { customElements.define("rr-app", App); -export default (document.querySelector("rr-app") || new App()) as App; \ No newline at end of file +export function dispatchEvent(name: string) { + return app.dispatchEvent(new Event(name)); +} + +const app = document.querySelector("rr-app") || new App(); + +export default app; \ No newline at end of file diff --git a/static/ts/data/articles.ts b/static/ts/data/articles.ts index 2c3dbc7..5444711 100644 --- a/static/ts/data/articles.ts +++ b/static/ts/data/articles.ts @@ -1,7 +1,7 @@ import { Article, ArticleFilters, ArticleId, SubscriptionId } from "data/types"; import * as counters from "data/counters"; -import * as pubsub from "util/pubsub"; import api from "util/api"; +import { dispatchEvent } from "app"; const articles: Map = new Map(); @@ -34,13 +34,13 @@ export async function markRead(ids?: ArticleId[]) { .forEach(a => a.read = true); counters.sync(); - pubsub.publish("articles-updated"); + dispatchEvent("articles-updated"); } export async function edit(id: ArticleId, data: Partial
) { let res = await api("PATCH", `/api/articles/${id}/`, data); res.ok && articles.set(id, res.data as Article); - pubsub.publish("articles-updated"); + dispatchEvent("articles-updated"); return articles.get(id); } @@ -48,5 +48,5 @@ export function syncRead(ids: SubscriptionId[]) { Array.from(articles.values()) .filter(a => ids.includes(a.subscription_id)) .forEach(a => a.read = true); - pubsub.publish("articles-updated"); + dispatchEvent("articles-updated"); } diff --git a/static/ts/data/categories.ts b/static/ts/data/categories.ts index 4d5c8f4..1c2c917 100644 --- a/static/ts/data/categories.ts +++ b/static/ts/data/categories.ts @@ -2,8 +2,8 @@ import { CategoryId, Category } from "data/types"; import * as articles from "data/articles"; import * as subscriptions from "data/subscriptions"; import * as counters from "data/counters"; -import * as pubsub from "util/pubsub"; import api from "util/api"; +import { dispatchEvent } from "app"; const categories: Map = new Map(); @@ -32,7 +32,7 @@ export async function add(data: Partial) { let res = await api("POST", "/api/categories/", data); if (!res.ok) { return res; } categories.set(res.data.id as CategoryId, res.data as Category); - pubsub.publish("categories-changed"); + dispatchEvent("categories-changed"); return res; } @@ -40,7 +40,7 @@ export async function edit(id: CategoryId, data: Partial) { let res = await api("PATCH", `/api/categories/${id}/`, data); if (!res.ok) { return res; } categories.set(res.data.id as CategoryId, res.data as Category); - pubsub.publish("categories-changed"); + dispatchEvent("categories-changed"); return res; } @@ -49,7 +49,7 @@ export async function remove(id: CategoryId) { if (!res.ok) { return res; } categories.delete(id); subscriptions.sync(); - pubsub.publish("categories-changed"); + dispatchEvent("categories-changed"); return res; } diff --git a/static/ts/data/counters.ts b/static/ts/data/counters.ts index 278d345..6ede491 100644 --- a/static/ts/data/counters.ts +++ b/static/ts/data/counters.ts @@ -1,13 +1,13 @@ import { SubscriptionId } from "data/types"; import api from "util/api"; -import * as pubsub from "util/pubsub"; +import app, { dispatchEvent } from "app"; const counters: Map = new Map(); export function init() { sync(); setInterval(sync, 60*1000); - pubsub.subscribe("articles-updated", sync); + app.addEventListener("articles-updated", sync); } export async function sync() { @@ -21,7 +21,9 @@ export async function sync() { counters.set(Number(id), count as number) } - prevSum != sum() && pubsub.publish("counters-updated"); + if (prevSum != sum()) { + dispatchEvent("counters-updated"); + } } export function list() { return counters; } diff --git a/static/ts/data/settings.ts b/static/ts/data/settings.ts index 609a6c4..496292c 100644 --- a/static/ts/data/settings.ts +++ b/static/ts/data/settings.ts @@ -1,5 +1,5 @@ import { Settings } from "data/types"; -import * as pubsub from "util/pubsub"; +import { dispatchEvent } from "app"; const DEFAULTS: Settings = { navWidth: "20%", @@ -27,7 +27,7 @@ export function getItem(key: K) { export function setItem(key: K, value: NonNullable) { localStorage.setItem(key, JSON.stringify(value)); key == "theme" && onThemeChange(); - pubsub.publish("settings-changed"); + dispatchEvent("settings-changed"); } export function removeItem(key: K) { diff --git a/static/ts/data/subscriptions.ts b/static/ts/data/subscriptions.ts index ef129c0..95ff53a 100644 --- a/static/ts/data/subscriptions.ts +++ b/static/ts/data/subscriptions.ts @@ -1,8 +1,8 @@ import { SubscriptionId, Subscription, CategoryId } from "data/types"; import * as counters from "data/counters"; import * as articles from "data/articles"; -import * as pubsub from "util/pubsub"; import api from "util/api"; +import { dispatchEvent } from "app"; const subscriptions: Map = new Map(); @@ -14,7 +14,7 @@ export async function sync() { let res = await api("GET", "/api/subscriptions/"); if (!res.ok) { return; } (res.data as Subscription[]).forEach(s => subscriptions.set(s.id, s)); - pubsub.publish("subscriptions-changed"); + dispatchEvent("subscriptions-changed"); } export function list(categoryId?: CategoryId) { @@ -27,7 +27,7 @@ export async function add(data: Partial) { let res = await api("POST", "/api/subscriptions/", data); if (!res.ok) { return res; } subscriptions.set((res.data as Subscription).id, res.data as Subscription); - pubsub.publish("subscriptions-changed"); + dispatchEvent("subscriptions-changed"); return res; } @@ -35,7 +35,7 @@ export async function edit(id: SubscriptionId, data: Partial) { let res = await api("PATCH", `/api/subscriptions/${id}/`, data); if (!res.ok) { return res; } subscriptions.set((res.data as Subscription).id, res.data as Subscription); - pubsub.publish("subscriptions-changed"); + dispatchEvent("subscriptions-changed"); return res; } @@ -43,7 +43,7 @@ export async function remove(id: SubscriptionId) { let res = await api("DELETE", `/api/subscriptions/${id}/`); if (!res.ok) { return res; } subscriptions.delete(id); - pubsub.publish("subscriptions-changed"); + dispatchEvent("subscriptions-changed"); return res; } diff --git a/static/ts/ui/articles.ts b/static/ts/ui/articles.ts index b40d807..826247e 100644 --- a/static/ts/ui/articles.ts +++ b/static/ts/ui/articles.ts @@ -4,7 +4,6 @@ import * as settings from "data/settings"; import * as articles from "data/articles"; import * as format from "util/format"; -import * as pubsub from "util/pubsub"; import Swipe from "util/swipe"; import FeedIcon from "ui/widget/feed-icon"; @@ -23,24 +22,29 @@ export default class Articles extends HTMLElement { {root: this, rootMargin: "0% 0% 20% 0%"} ); - constructor() { - super(); - this.addEventListener("click", this); - this.addEventListener("scroll", this); + connectedCallback() { + this.build(); let swipe = new Swipe(this); swipe.onSwipe = async dir => { dir == "right" && app.toggleNav(true); }; + + this.addEventListener("click", this); + this.addEventListener("scroll", this); + app.addEventListener("articles-updated", this); } - connectedCallback() { - this.build(); + disconnectedCallback() { + this.removeEventListener("click", this); + this.removeEventListener("scroll", this); + app.removeEventListener("articles-updated", this); } handleEvent(e: Event) { e.type == "click" && this.onClick(e); e.type == "scroll" && this.onScroll(e); + e.type == "articles-updated" && this.items.forEach(i => i.sync()); } update() { @@ -239,16 +243,6 @@ class Item extends HTMLElement { if (this.data.image_url && settings.getItem("showImages")) { this.append(buildPicture(this.data.image_url)); } - - pubsub.subscribe("articles-updated", this); - } - - disconnectCallback() { - pubsub.unsubscribe("articles-updated", this); - } - - handleMessage(message:string, publisher?: any, data?: any) { - message == "articles-updated" && this.sync(); } } diff --git a/static/ts/ui/counter.ts b/static/ts/ui/counter.ts index 893903b..e0c31b4 100644 --- a/static/ts/ui/counter.ts +++ b/static/ts/ui/counter.ts @@ -1,17 +1,17 @@ -import * as pubsub from "util/pubsub"; +import app from "app"; export default class Counter extends HTMLElement { connectedCallback() { this.sync(); - pubsub.subscribe("counters-updated", this); + app.addEventListener("counters-updated", this); } - disconnectCallback() { - pubsub.unsubscribe("counters-updated", this); + disconnectedCallback() { + app.removeEventListener("counters-updated", this); } - handleMessage(message:string, publisher?: any, data?: any) { - message == "counters-updated" && this.sync(); + handleEvent() { + this.sync(); } getCount(): Number { return 0; } diff --git a/static/ts/ui/detail.ts b/static/ts/ui/detail.ts index 774902a..96c9750 100644 --- a/static/ts/ui/detail.ts +++ b/static/ts/ui/detail.ts @@ -5,7 +5,6 @@ import * as settings from "data/settings"; import Swipe from "util/swipe"; import * as format from "util/format"; -import * as pubsub from "util/pubsub"; import app from "app"; import Icon from "ui/icon"; @@ -205,15 +204,15 @@ class ArticleContent extends HTMLElement { this.normalize(); this.setCSS(); - pubsub.subscribe("settings-changed", this); + app.addEventListener("settings-changed", this); } disconnectCallback() { - pubsub.unsubscribe("settings-changed", this); + app.removeEventListener("settings-changed", this); } - handleMessage(message:string, publisher?: any, data?: any) { - message == "settings-changed" && this.setCSS(); + handleEvent(event: Event) { + event.type == "settings-changed" && this.setCSS(); } normalize() { diff --git a/static/ts/ui/feeds.ts b/static/ts/ui/feeds.ts index a419714..a536ea0 100644 --- a/static/ts/ui/feeds.ts +++ b/static/ts/ui/feeds.ts @@ -5,8 +5,6 @@ import * as categories from "data/categories"; import * as subscriptions from "data/subscriptions"; import * as articles from "data/articles"; -import * as pubsub from "util/pubsub"; - import Icon from "ui/icon"; import Counter from "ui/counter"; import FeedIcon from "ui/widget/feed-icon"; @@ -19,12 +17,6 @@ import { confirm } from "ui/widget/dialog"; import app from "app"; export default class Feeds extends HTMLElement { - constructor() { - super(); - this.addEventListener("click", this); - this.addEventListener("contextmenu", this); - } - get items() { return [...this.querySelectorAll("rr-item-feeds")] as Item[]; }; @@ -37,27 +29,25 @@ export default class Feeds extends HTMLElement { this.replaceChildren(...buildItems()); this.items[0].active = true; - pubsub.subscribe("subscriptions-changed", this); - pubsub.subscribe("categories-changed", this); - } - - disconnectCallback() { - pubsub.unsubscribe("subscriptions-changed", this); - pubsub.unsubscribe("categories-changed", this); + this.addEventListener("click", this); + this.addEventListener("contextmenu", this); + app.addEventListener("subscriptions-changed", this); + app.addEventListener("categories-changed", this); } - handleMessage(message:string, publisher?: any, data?: any) { - this.replaceChildren(...buildItems()); + disconnectedCallback() { + this.removeEventListener("click", this); + this.removeEventListener("contextmenu", this); + app.removeEventListener("subscriptions-changed", this); + app.removeEventListener("categories-changed", this); } - handleEvent(e: PointerEvent) { - e.stopPropagation(); - + handleEvent(e: Event) { let item = (e.target as HTMLElement).closest("rr-item-feeds") as Item; - if (!item) { return; } - switch(e.type) { case "click": + if (!item) { return; } + e.stopPropagation(); this.items.forEach(i => i.active = false); item.active = true; @@ -66,8 +56,14 @@ export default class Feeds extends HTMLElement { app.toggleDetail(false); break; case "contextmenu": + if (!item) { return; } + e.stopPropagation(); e.preventDefault(); - showContextMenu(item, e); + showContextMenu(item, e as PointerEvent); + break; + case "subscriptions-changed": + case "categories-changed": + this.replaceChildren(...buildItems()); break; } } diff --git a/static/ts/util/pubsub.ts b/static/ts/util/pubsub.ts deleted file mode 100644 index d41b1b7..0000000 --- a/static/ts/util/pubsub.ts +++ /dev/null @@ -1,23 +0,0 @@ -const storage = Object.create(null); - -type Listener = (message:string, publisher?: any, data?: any) => void; -type Subscriber = Listener | {handleMessage:Listener}; - -export function publish(message: string, publisher?: any, data?: any) { - let subscribers = storage[message] || []; - subscribers.forEach((subscriber:Subscriber) => { - typeof(subscriber) == "function" - ? subscriber(message, publisher, data) - : subscriber.handleMessage(message, publisher, data); - }); -} - -export function subscribe(message:string, subscriber:Subscriber) { - if (!(message in storage)) { storage[message] = []; } - storage[message].push(subscriber); -} - -export function unsubscribe(message:string, subscriber:Subscriber) { - let index = (storage[message] || []).indexOf(subscriber); - if (index > -1) { storage[message].splice(index, 1); } -}