From afeb6f75e811c9cc488a20498ee23d581baae794 Mon Sep 17 00:00:00 2001 From: Andy Burke Date: Sat, 8 Nov 2025 17:15:26 -0800 Subject: [PATCH] refactor: first pass on getting the client back into working order (still broken, but loading as a baseline) --- README.md | 12 +- public/api/channels/README.md | 28 +++ public/api/channels/index.ts | 2 +- public/api/events/:event_id/README.md | 7 + public/api/events/README.md | 16 +- .../users/:user_id/watches/:watch_id/index.ts | 4 +- public/api/users/:user_id/watches/index.ts | 42 ++-- public/api/users/index.ts | 2 + public/base.css | 30 +-- public/js/app.js | 82 +++---- public/js/reactions.js | 13 +- public/sidebar/sidebar.html | 182 +--------------- public/tabs/blurbs/blurbs.html | 5 +- public/tabs/blurbs/new_blurb.html | 4 +- public/tabs/calendar/README.md | 2 +- public/tabs/chat/channel_sidebar.html | 202 ++++++++++++++++++ public/tabs/chat/chat.html | 25 ++- public/tabs/essays/essays.html | 5 +- public/tabs/essays/new_essay.html | 4 +- public/tabs/forum/forum.html | 5 +- public/tabs/forum/new_post.html | 4 +- public/tabs/resources/README.md | 2 +- public/tabs/tabs.html | 2 +- 23 files changed, 358 insertions(+), 322 deletions(-) create mode 100644 public/tabs/chat/channel_sidebar.html diff --git a/README.md b/README.md index 4b7bc6b..42547e5 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Bringing the BBS back. These are in no particular order. Pull requests updating this section welcome for feature discussions. -- [X] should everything be an event in a topic? +- [X] the core is a stream of events - [X] get a first-pass podman/docker setup up - [X] sign up - [X] check for logged in user session @@ -21,14 +21,14 @@ feature discussions. - [X] logout button - [ ] profile editing - [X] avatar uploads -- [X] chat topics +- [X] chat channels - [X] chat messages - [ ] membership and presence - - [ ] add memberships to topics + - [ ] add memberships to channels - [ ] join to get notifications - [ ] join for additional permissions - - [ ] filters for allowing joining a topic based on criteria on the user? - - [ ] display topic members somehwere + - [ ] filters for allowing joining a channel based on criteria on the user? + - [ ] display channel members somehwere - [ ] emit presence events on join/leave - [ ] display user presence - [ ] chat message actions @@ -88,7 +88,7 @@ feature discussions. - [ ] if web notifications are enabled, emit on events - [ ] ability to mute - [ ] users - - [ ] topics + - [ ] channels - [ ] tags (#tags?) - [ ] admin panel - [ ] add invite code generation diff --git a/public/api/channels/README.md b/public/api/channels/README.md index e69de29..c16c81b 100644 --- a/public/api/channels/README.md +++ b/public/api/channels/README.md @@ -0,0 +1,28 @@ +# /api/channels + +Interact with channels. + +## POST /api/channels + +Create a new channel. + +``` +export type CHANNEL = { + id: string; // unique id for this channel + name: string; // the name of the channel (max 128 characters) + icon?: string; // optional url for a channel icon + topic?: string; // optional channel topic + tags?: string[]; // optional tags for the channel + meta?: Record; // optional metadata + limits: { + users: number; + user_messages_per_minute: number; + }; + creator_id: string; // user_id of the topic creator + emojis: Record; // either: string: emoji eg: { 'rofl: 🤣, ... } or { 'rofl': 🤣, 'blap': 'https://somewhere.someplace/image.jpg' } +}; +``` + +## GET /api/channels + +Get channels. diff --git a/public/api/channels/index.ts b/public/api/channels/index.ts index 067561e..30fde2a 100644 --- a/public/api/channels/index.ts +++ b/public/api/channels/index.ts @@ -19,7 +19,7 @@ export async function GET(_req: Request, meta: Record): Promise topic_entry.load()); + })).map((channel_entry) => channel_entry.load()); return Response.json(channels, { status: 200 diff --git a/public/api/events/:event_id/README.md b/public/api/events/:event_id/README.md index e69de29..269a9f4 100644 --- a/public/api/events/:event_id/README.md +++ b/public/api/events/:event_id/README.md @@ -0,0 +1,7 @@ +## PUT /api/events/:event_id + +Update an event. + +## DELETE /api/events/:event_id + +Delete an event. diff --git a/public/api/events/README.md b/public/api/events/README.md index 13582fa..71c7345 100644 --- a/public/api/events/README.md +++ b/public/api/events/README.md @@ -1,15 +1,11 @@ -# /api/topics/:topic_id/events +# /api/events -Interact with a events for a topic. +Interact with events. -## GET /api/topics/:topic_id/events +## GET /api/events -Get events for the given topic. +Get events. -## PUT /api/topics/:topic_id/events/:event_id +## POST /api/events -Update an event. - -## DELETE /api/topics/:topic_id/events/:event_id - -Delete an event. +Create an event. diff --git a/public/api/users/:user_id/watches/:watch_id/index.ts b/public/api/users/:user_id/watches/:watch_id/index.ts index aa7e7df..5aab276 100644 --- a/public/api/users/:user_id/watches/:watch_id/index.ts +++ b/public/api/users/:user_id/watches/:watch_id/index.ts @@ -5,7 +5,7 @@ import parse_body from '../../../../../../utils/bodyparser.ts'; export const PRECHECKS: PRECHECK_TABLE = {}; -// PUT /api/users/:user_id/watches/:watch_id - Update topic +// PUT /api/users/:user_id/watches/:watch_id - Update watch PRECHECKS.PUT = [get_session, get_user, require_user, async (_req: Request, meta: Record): Promise => { const watch_id: string = meta.params?.watch_id?.toLowerCase().trim() ?? ''; @@ -69,7 +69,7 @@ PRECHECKS.DELETE = [ return CANNED_RESPONSES.not_found(); } - meta.topic = watch; + meta.watch = watch; const user_owns_watch = watch.creator_id === meta.user.id; if (!user_owns_watch) { diff --git a/public/api/users/:user_id/watches/index.ts b/public/api/users/:user_id/watches/index.ts index 0808a98..4950a33 100644 --- a/public/api/users/:user_id/watches/index.ts +++ b/public/api/users/:user_id/watches/index.ts @@ -4,7 +4,7 @@ import * as CANNED_RESPONSES from '../../../../../utils/canned_responses.ts'; import { get_session, get_user, PRECHECK_TABLE, require_user } from '../../../../../utils/prechecks.ts'; import parse_body from '../../../../../utils/bodyparser.ts'; import lurid from '@andyburke/lurid'; -import { TOPICS } from '../../../../../models/topic.ts'; +import { CHANNELS } from '../../../../../models/channel.ts'; export const PRECHECKS: PRECHECK_TABLE = {}; @@ -99,34 +99,18 @@ export async function POST(req: Request, meta: Record): Promise[A-Za-z\-]+)\/?(?\w+)?/gm; -const UPDATE_TOPICS_FREQUENCY = 60_000; +const HASH_EXTRACTOR = /^\#\/(?\w+)(?:\/channel\/(?[A-Za-z\-]+)\/?)?/gm; +const UPDATE_CHANNELS_FREQUENCY = 60_000; const APP = { user: undefined, @@ -52,7 +52,7 @@ const APP = { extract_url_hash_info: async function () { HASH_EXTRACTOR.lastIndex = 0; // ugh, need this to have this work on multiple exec calls const { - groups: { topic_id, view }, + groups: { view, channel_id }, } = HASH_EXTRACTOR.exec(window.location.hash ?? "") ?? { groups: {}, }; @@ -61,36 +61,36 @@ const APP = { console.dir({ url: window.location.href, hash: window.location.hash, - topic_id, view, + channel_id, }); */ - if (!document.body.dataset.topic || document.body.dataset.topic !== topic_id) { - const previous = document.body.dataset.topic; + if (!document.body.dataset.channel || document.body.dataset.channel !== channel_id) { + const previous = document.body.dataset.channel; /* console.dir({ - topic_changed: { + channel_changed: { detail: { previous, - topic_id, + channel_id, }, }, }); */ - document.body.dataset.topic = topic_id; + document.body.dataset.channel = channel_id; - this._emit( 'topic_changed', { + this._emit( 'channel_changed', { previous, - topic_id + channel_id }); - if (!topic_id) { - const first_topic_id = this.TOPICS.TOPIC_LIST[0]?.id; - if (first_topic_id) { - window.location.hash = `/topic/${first_topic_id}/chat`; // TODO: allow a different default than chat + if (!channel_id) { + const first_channel_id = this.CHANNELS.CHANNEL_LIST[0]?.id; + if (first_channel_id) { + window.location.hash = `/chat/channel/${first_channel_id}`; // TODO: allow a different default than chat } } } @@ -149,7 +149,7 @@ const APP = { } window.addEventListener("locationchange", this.extract_url_hash_info.bind( this )); - window.addEventListener("locationchange", this.TOPICS.update ); + window.addEventListener("locationchange", this.CHANNELS.update ); this.check_if_logged_in(); this.extract_url_hash_info(); @@ -162,7 +162,7 @@ const APP = { document.body.dataset.user = JSON.stringify(user); document.body.dataset.perms = user.permissions.join(":"); - this.TOPICS.update(); + this.CHANNELS.update(); this.user_servers = []; try { @@ -231,56 +231,56 @@ const APP = { }, }, - TOPICS: { - _last_topic_update: undefined, - _update_topics_timeout: undefined, - TOPIC_LIST: [], + CHANNELS: { + _last_channel_update: undefined, + _update_channels_timeout: undefined, + CHANNEL_LIST: [], update: async () => { const now = new Date(); - const time_since_last_update = now - (APP.TOPICS._last_topic_update ?? 0); - if (time_since_last_update < UPDATE_TOPICS_FREQUENCY / 2) { + const time_since_last_update = now - (APP.CHANNELS._last_channel_update ?? 0); + if (time_since_last_update < UPDATE_CHANNELS_FREQUENCY / 2) { return; } - if (APP.TOPICS._update_topics_timeout) { - clearTimeout(APP.TOPICS._update_topics_timeout); - APP.TOPICS._update_topics_timeout = undefined; + if (APP.CHANNELS._update_channels_timeout) { + clearTimeout(APP.CHANNELS._update_channels_timeout); + APP.CHANNELS._update_channels_timeout = undefined; } try { - const topics_response = await api.fetch("/api/topics"); - if (topics_response.ok) { - const new_topics = await topics_response.json(); + const channels_response = await api.fetch("/api/channels"); + if (channels_response.ok) { + const new_channels = await channels_response.json(); const has_differences = - APP.TOPICS.TOPIC_LIST.length !== new_topics.length || - new_topics.some((topic, index) => { + APP.CHANNELS.CHANNEL_LIST.length !== new_channels.length || + new_channels.some((channel, index) => { return ( - APP.TOPICS.TOPIC_LIST[index]?.id !== topic.id || - APP.TOPICS.TOPIC_LIST[index]?.name !== topic.name + APP.CHANNELS.CHANNEL_LIST[index]?.id !== channel.id || + APP.CHANNELS.CHANNEL_LIST[index]?.name !== channel.name ); }); if (has_differences) { - APP.TOPICS.TOPIC_LIST = [...new_topics]; + APP.CHANNELS.CHANNEL_LIST = [...new_channels]; - APP._emit( 'topics_updated', { - topics: APP.TOPICS.TOPIC_LIST + APP._emit( 'channels_updated', { + channels: APP.CHANNELS.CHANNEL_LIST }); } - APP.TOPICS._last_topic_update = now; + APP.CHANNELS._last_channel_update = now; } } catch (error) { console.error(error); } - APP.TOPICS._update_topics_timeout = setTimeout( - APP.TOPICS.update, - UPDATE_TOPICS_FREQUENCY, + APP.CHANNELS._update_channels_timeout = setTimeout( + APP.CHANNELS.update, + UPDATE_CHANNELS_FREQUENCY, ); - // now that we have topics, make sure our url is all good + // now that we have channels, make sure our url is all good APP.extract_url_hash_info(); }, }, diff --git a/public/js/reactions.js b/public/js/reactions.js index 230be01..441ecaf 100644 --- a/public/js/reactions.js +++ b/public/js/reactions.js @@ -139,6 +139,7 @@ document.addEventListener("DOMContentLoaded", () => {
{ generator="() => { return APP.user?.id; }" /> + + { document.body.appendChild(reactions_popup); reactions_popup_form = document.getElementById("reactions-selection-form"); - APP.on("topic_changed", ({ topic_id }) => { - const reaction_topic_id = topic_id ?? document.body.dataset.topic; - reactions_popup_form.action = reaction_topic_id - ? `/api/topics/${reaction_topic_id}/events` - : ""; - }); reactions_popup_search_input = document.getElementById("reactions-search-input"); reactions_popup_parent_id_input = reactions_popup_form.querySelector('[name="parent_id"]'); diff --git a/public/sidebar/sidebar.html b/public/sidebar/sidebar.html index d118b03..ec00e2c 100644 --- a/public/sidebar/sidebar.html +++ b/public/sidebar/sidebar.html @@ -1,38 +1,4 @@ -
+
diff --git a/public/tabs/calendar/README.md b/public/tabs/calendar/README.md index d64b1dd..50fe4db 100644 --- a/public/tabs/calendar/README.md +++ b/public/tabs/calendar/README.md @@ -1,3 +1,3 @@ # Calendar -The calendar should help people coordinate events around a topic. +The calendar should help people coordinate events. diff --git a/public/tabs/chat/channel_sidebar.html b/public/tabs/chat/channel_sidebar.html new file mode 100644 index 0000000..b289723 --- /dev/null +++ b/public/tabs/chat/channel_sidebar.html @@ -0,0 +1,202 @@ + + + diff --git a/public/tabs/chat/chat.html b/public/tabs/chat/chat.html index bc66136..4a7f543 100644 --- a/public/tabs/chat/chat.html +++ b/public/tabs/chat/chat.html @@ -21,8 +21,8 @@
{ feed.__reset && feed.__reset(); }); + APP.on("channel_changed", () => { feed.__reset && feed.__reset(); }); APP.on("user_logged_in", () => { feed.__reset && feed.__reset(); }); const time_tick_tock_timeout = 60_000; @@ -147,7 +147,8 @@ { await document.getElementById( 'chat-content' ).__render(event); document.getElementById(event.id)?.classList.remove('sending'); }" on_parsed="async (event) => { await document.getElementById( 'chat-content' ).__render(event); document.getElementById(event.id)?.classList.add('sending'); }" > - - + +
+ diff --git a/public/tabs/essays/essays.html b/public/tabs/essays/essays.html index 4eb0945..46543b8 100644 --- a/public/tabs/essays/essays.html +++ b/public/tabs/essays/essays.html @@ -115,8 +115,8 @@
{ feed.__reset && feed.__reset(); }); APP.on("user_logged_in", () => { feed.__reset && feed.__reset(); }); feed.__target_element = (item) => { diff --git a/public/tabs/essays/new_essay.html b/public/tabs/essays/new_essay.html index 209d70a..fead5bb 100644 --- a/public/tabs/essays/new_essay.html +++ b/public/tabs/essays/new_essay.html @@ -22,7 +22,7 @@ display: inline-block; } -
+
diff --git a/public/tabs/resources/README.md b/public/tabs/resources/README.md index dcd6aa4..2771877 100644 --- a/public/tabs/resources/README.md +++ b/public/tabs/resources/README.md @@ -1,3 +1,3 @@ # Resources -Resources should be a wiki for organizing community knowledge on a topic. +Resources should be a wiki for organizing community knowledge. diff --git a/public/tabs/tabs.html b/public/tabs/tabs.html index b036fd3..db75630 100644 --- a/public/tabs/tabs.html +++ b/public/tabs/tabs.html @@ -14,7 +14,7 @@ const tab_selector = event.target; const view = tab_selector.dataset.view; if (view) { - window.location.hash = `/topic/${document.body.dataset.topic}/${view}`; + window.location.hash = `/${view}${ document.body.dataset.channel ? `/channel/${ document.body.dataset.channel }` : '' }`; } }); }