Live updates from the server reach the pages through the contexts declared
here. Three hold state: votes, likes, deletions and counts in one, the chat
in another, who is online in the third, split so that a feed doesn't
re-render with every chat message or presence change. The fourth passes on
events, each change to a dump, playlist, comment or profile and each
notification, to the components that subscribe to their kind. WSProvider
fills them all from its WebSocket, and components read them with useWS,
useWSChat, useWSPresence and useWSEvent.
Returns a bus with no subscribers.
Tells the user the server refused their chat edit or delete. WSProvider
sets it as chatError, and ChatModal shows it for a few seconds.
-
id: number
Tells apart two errors with the same message.
- message: string
Quotes the message a chat message replies to. ChatModal shows it in the
reply bar once the user picks it, and it stays on the pending message so
the quote shows before the server's echo. Only id goes to the server.
Keeps comment threads and comment counts live. WSProvider emits it as a
comment event on each comment_* message; the Dump page applies it to
its thread, and useDumpListSync to the counts on dump cards.
-
comment: Comment
Set when created or updated.
-
commentId: string
Set when deleted.
- dumpId: string
- type: "created" | "deleted" | "updated"
Keeps every dump on screen up to date. WSProvider emits it as a dump
event on each dump_created and dump_updated; useDumpListSync applies
it to lists, and the Dump page to its dump. A dump that turns public
comes created to those who couldn't see it.
Shows a chat message the moment the user sends it, and lets them retry it if
it fails. WSProvider holds it until the server echoes the message back, and
ChatModal shows it, as PendingItem, after the messages the server has.
- body: string
-
createdAt: Date
When this client sent it, to find it in the history after a reconnect.
- error: string
-
nonce: string
Sent with
chat_sendand returned with the echo or the error. A retry gets a new one, so a late echo of the first attempt can't retire it. - replyTo: ChatReplyTarget
-
status: "sending" | "failed"
failedwhen the server refused it, the socket was down or closed, or no echo came in 10 s. A failed message may still have reached the server.
Keeps the playlists on screen up to date, in lists, on a playlist's page and
on a dump's page. WSProvider emits it as a playlist event on each
playlist_* message; lists apply it through usePlaylistListSync, a
playlist's page through usePlaylist. Which fields are set depends on
type.
-
dumpIds: string[]
Set when the dumps changed. It lists only the dumps this user may see, in order.
-
lastDumpAt: Date
With
dumpIds, when the latest of them was added; undefined when there is none. -
playlist: Playlist
Set when created or updated.
- playlistId: string
- type: "created" | "updated" | "deleted" | "dumps_updated"
-
userId: string
The owner's id, set when deleted.
Keeps a user's profile, and their name on what they posted, up to date.
WSProvider emits it as a user event on each user_updated, which the
server sends on any change to a profile; useProfile applies it to the
profile page, and the list sync hooks to the names on cards.
Keeps a user's upvoted list live, on their profile's overview and tab.
WSProvider emits it as a vote event on each votes_update, and
useUpvotedList adds or drops the dump.
Carries the chat's live side, which ChatModal lays over the history it
fetches and the chat buttons show as a badge. WSProvider provides it
through WSChatContext, and components read it with useWSChat. The
socket sends chat only to signed-in users, so for a visitor it stays empty.
-
chatError: ChatError | null
Only for edits and deletes. A failed send shows on its pending message.
-
chatMessages: ChatMessage[]
Shows chat messages as they arrive. It holds the ones received this session, at most 200, which
ChatModalmerges with the history it fetches. A reconnect empties it while the chat is closed, sinceChatModalfetches everything again on open. -
chatReadAt: Date | null
Places the "new" divider when the chat opens. It holds the time of the newest message the user has seen, which the server keeps, and is null until the
welcome, and for a visitor. -
deleteChatMessage: (id: string) => void
Sends the deletion if the socket is open, as
editChatMessagedoes. -
deletedChatIds: Set<string>
Takes the messages deleted this session out of the history
ChatModalfetched. - discardChatMessage: (nonce: string) => void
- dismissChatError: () => void
-
editChatMessage: () => voidid: string,body: string
Sends the edit if the socket is open, else drops it. It shows once the server broadcasts it, or fails as
chatError. -
markChatRead: (at: Date) => void
Moves the read marker up to
at, the time of the newest message seen. It zeroesunreadChatCountat once and sends the marker at most once a second. A marker set offline goes out with the nextwelcome. - pendingChatMessages: PendingChatMessage[]
-
reconcileChat: () => voidmessages: ChatMessage[],from: Date,to: Date
Repairs the live buffer after a reconnect. It replaces its messages between
fromandtowithmessages, whichChatModalfetched. A message missing from them was deleted while the socket was down. A failed send found among them did reach the server, and leaves the pending list. - retryChatMessage: (nonce: string) => void
-
sendChatMessage: () => voidbody: string,replyTo?: ChatReplyTarget
Trims
bodyand sends it as a pending message. Ignores a blank body. -
setChatOpen: (open: boolean) => void
Spares the user a notification for a mention they read live. It tells the server whether the chat is open in a visible tab; while it is, the server sends no notification for a mention, and this client doesn't count the message unread.
-
setChatTyping: (typing: boolean) => void
Tells the server whether the user is typing, when the socket is open.
ChatModalresendstrueevery few seconds while typing continues, since the server forgets a typer after 6 s. -
typingUsers: OnlineUser[]
Shows who is typing. It includes the user themselves, whom
ChatModalfilters out. -
unreadChatCount: number
Drives the chat buttons' unread badge. It counts the messages from others after the read marker, which the server keeps, so the count agrees across tabs and devices.
Carries the live state but the chat and presence, for pages to lay over
what they fetched. WSProvider provides it through WSContext, and
components read it with useWS. The changes to single items come as events
instead, through WSEventsContext. After a reconnect,
connectionEpoch tells pages to fetch what they missed.
-
castCommentLike: () => voidcommentId: string,shownCount?: number
Likes a comment, as WSContextValue.castVote upvotes a dump.
-
castVote: () => voiddumpId: string,shownCount?: number
Upvotes a dump at once and sends
vote_cast. The server's answer confirms or reverts it, and so does a timeout of 5 s, which covers a closed socket.shownCountis the count on screen, used whenvoteCountshas none yet. Does nothing when the user already voted. -
clearUnreadNotifications: () => void
Zeroes the bell's count, once the notifications page has marked them read on the server.
-
connectionEpoch: number
Tells pages to fetch again what they missed while the socket was down. It goes up on each reconnect, never on the first connection.
-
deletedDumpIds: Set<string>
Takes deleted dumps off every list at once. It holds the dumps deleted this session, or made private by someone else; a dump made public again leaves it.
-
deletedPlaylistIds: Set<string>
Takes deleted playlists off every list at once, as
deletedDumpIdsdoes dumps. -
followsVersion: number
Keeps the Follow buttons right when the user follows or unfollows in another tab. It goes up on every follow change, in any tab, and
FollowProviderfetches the follow status again. -
injectDump: (dump: Dump) => void
Shows a dump this user just posted on the index without waiting for its broadcast.
DumpCreateModalcalls it, and it adds the dump torecentDumps. -
likeCounts: Record<string, number>
As
voteCounts, for comments. -
myLikes: Set<string>
As
myVotes, for comments. -
myVotes: Set<string>
Marks the dumps this user upvoted, for their vote buttons. The server's
welcomeresets it on every connection. -
recentDumps: Dump[]
Shows new dumps on the index as they are posted, merged by date with the feed
Indexfetched. It holds, newest first and at most 100, the ones the server announced this session, which are public, and the ones this user posted, which may be private. -
removeCommentLike: () => voidcommentId: string,shownCount?: number
Withdraws a like, as WSContextValue.castVote casts a vote.
-
removeVote: () => voiddumpId: string,shownCount?: number
Withdraws an upvote, as WSContextValue.castVote casts one.
-
unreadNotificationCount: number
Drives the bell's badge. It comes from the
welcome, then each new notification counts it up, andnotifications_readzeroes it. -
voteCounts: Record<string, number>
Shows vote counts live. It holds, by dump id, the counts the server pushed or this tab changed optimistically; a dump missing from it shows its fetched
voteCount. -
wsErrorMessage: string | null
Set while the socket is down, for the banner that says so.
- wsStatus: "connecting" | "connected" | "disconnected"
Delivers WSEvents to their subscribers. WSProvider creates one
with createWSEventBus and emits on it as messages arrive; components
subscribe through WSEventsContext, and tests emit on their own.
-
emit: <K extends keyof WSEvents>() => voidkind: K,event: WSEvents[K]
Calls the handlers of
kindnow, in the order they subscribed. -
subscribe: <K extends keyof WSEvents>() => () => voidkind: K,handler: (event: WSEvents[K]) => void
Calls
handlerwith each event ofkind, until the returned call.
The events WSProvider passes on, by kind, to the components that
subscribe with useWSEvent. Unlike the state of WSContext, every event
reaches every subscriber, however many arrive before the next render.
categories says the categories changed, and carries nothing.
Carries who is online, for PresenceRow. WSProvider provides it through
WSPresenceContext, and components read it with useWSPresence.
-
onlineUsers: OnlineUser[]
The signed-in users with a socket open, each once.
Lets a component render outside WSProvider, with an empty chat.
Lets a component render outside WSProvider, with nobody online.
Lets a component render outside WSProvider, as if the socket were
connecting.
Carries the chat's live stream and read state. Read it with useWSChat.
Carries the live state pages lay over what they fetched: the connection, the
votes and likes, and the latest event of each kind. Read it with useWS.
Carries the bus WSProvider emits its events on, for useWSEvent. Outside
WSProvider, it is a bus no one emits on.
Carries who is online. Read it with useWSPresence.
Usage
import * as mod from "src/contexts/ws/WSContext.ts";