import type { ChatMessage } from "./chat.ts"; import type { Comment } from "./comment.ts"; import type { Dump } from "./dump.ts"; import type { Notification } from "./notification.ts"; import type { Playlist } from "./playlist.ts"; import type { PublicUser } from "./user.ts"; /** * The socket protocol. Every message has a `type` discriminant, so each union * narrows with a switch. The server sends these as JSON, so the client reads * `Wire`: its Dates arrive as strings. */ // ── Client → Server ────────────────────────────────────────────────────────── export interface PingMessage { type: "ping"; } export interface PongMessage { type: "pong"; } export interface VoteCastMessage { type: "vote_cast"; dumpId: string; } export interface VoteRemoveMessage { type: "vote_remove"; dumpId: string; } export interface CommentLikeCastMessage { type: "comment_like_cast"; commentId: string; } export interface CommentLikeRemoveMessage { type: "comment_like_remove"; commentId: string; } export interface ChatSendMessage { type: "chat_send"; body: string; /** Id of the message being replied to, if this is a reply. */ replyToId?: string; /** Client-chosen id echoed on the broadcast (or on the error), so the * sender can match the outcome to its pending message. */ nonce?: string; } // Sent while the user is composing (true) and when they stop (false). The // server rebroadcasts the live set of typers to everyone as ChatTypingUpdate. export interface ChatTypingMessage { type: "chat_typing"; typing: boolean; } // Tells the server whether this client currently has the chatbox open, so that // mention notifications can be skipped for users who are already reading chat. export interface ChatFocusMessage { type: "chat_focus"; open: boolean; } export interface ChatEditMessage { type: "chat_edit"; id: string; body: string; } export interface ChatDeleteMessage { type: "chat_delete"; id: string; } // The user has seen the chat up to the message created at `at`. export interface ChatReadMessage { type: "chat_read"; at: string; } export type ClientToServerMessage = | PingMessage | PongMessage | VoteCastMessage | VoteRemoveMessage | CommentLikeCastMessage | CommentLikeRemoveMessage | ChatSendMessage | ChatTypingMessage | ChatFocusMessage | ChatEditMessage | ChatDeleteMessage | ChatReadMessage; // ── Server → Client ────────────────────────────────────────────────────────── export interface OnlineUser { userId: string; username: string; hasAvatar: boolean; avatarVersion?: number; } export interface WelcomeMessage { type: "welcome"; users: OnlineUser[]; myVotes: string[]; myCommentLikes: string[]; unreadNotificationCount: number; /** The user's chat read marker and the unread count behind it (signed-in * users only). */ chatReadAt?: string; unreadChatCount: number; } export interface PresenceUpdateMessage { type: "presence_update"; users: OnlineUser[]; } export interface VotesUpdateMessage { type: "votes_update"; dumpId: string; voteCount: number; voterId: string; action: "cast" | "remove"; } export interface VoteAckMessage { type: "vote_ack"; dumpId: string; action: "cast" | "remove"; voteCount: number; } export interface CommentLikesUpdateMessage { type: "comment_likes_update"; commentId: string; likeCount: number; likerId: string; action: "cast" | "remove"; } export interface CommentLikeAckMessage { type: "comment_like_ack"; commentId: string; action: "cast" | "remove"; likeCount: number; } export interface DumpCreatedMessage { type: "dump_created"; dump: Dump; } export interface DumpUpdatedMessage { type: "dump_updated"; dump: Dump; } export interface DumpDeletedMessage { type: "dump_deleted"; dumpId: string; } export interface PlaylistCreatedMessage { type: "playlist_created"; playlist: Playlist; } export interface PlaylistUpdatedMessage { type: "playlist_updated"; playlist: Playlist; } export interface PlaylistDeletedMessage { type: "playlist_deleted"; playlistId: string; userId: string; } export interface PlaylistDumpsUpdatedMessage { type: "playlist_dumps_updated"; playlistId: string; /** The members this recipient can see, in order. */ dumpIds: string[]; /** Every member, as Playlist.dumpCount counts them. */ dumpCount: number; } export interface UserUpdatedMessage { type: "user_updated"; user: PublicUser; } export interface CommentCreatedMessage { type: "comment_created"; comment: Comment; } export interface CommentUpdatedMessage { type: "comment_updated"; comment: Comment; } export interface CommentDeletedMessage { type: "comment_deleted"; commentId: string; dumpId: string; } export interface NotificationCreatedMessage { type: "notification_created"; notification: Notification; } export interface ErrorMessage { type: "error"; message?: string; // Context for optimistic-action failures so the client can revert the exact // pending vote/like immediately instead of waiting for its ACK timeout. dumpId?: string; commentId?: string; action?: "cast" | "remove"; /** Set when a chat action failed, so the client can surface it in the chat. */ scope?: "chat"; /** Echo of the failed chat_send's nonce. */ nonce?: string; } export interface ForceLogoutMessage { type: "force_logout"; } export interface ChatMessageMessage { type: "chat_message"; message: ChatMessage; /** The sender's nonce, so its pending copy can be retired. */ nonce?: string; } export interface ChatMessageUpdatedMessage { type: "chat_message_updated"; message: ChatMessage; } export interface ChatMessageDeletedMessage { type: "chat_message_deleted"; id: string; } // The current set of users composing a chat message. Reuses OnlineUser so the // client can render the same avatars as the presence row. export interface ChatTypingUpdateMessage { type: "chat_typing_update"; users: OnlineUser[]; } // The user's read marker moved (from any of their tabs or devices). export interface ChatReadUpdateMessage { type: "chat_read_update"; readAt: string; unread: number; } export type ServerToClientMessage = | PingMessage | PongMessage | WelcomeMessage | PresenceUpdateMessage | VotesUpdateMessage | VoteAckMessage | CommentLikesUpdateMessage | CommentLikeAckMessage | DumpCreatedMessage | DumpUpdatedMessage | DumpDeletedMessage | PlaylistCreatedMessage | PlaylistUpdatedMessage | PlaylistDeletedMessage | PlaylistDumpsUpdatedMessage | UserUpdatedMessage | CommentCreatedMessage | CommentUpdatedMessage | CommentDeletedMessage | NotificationCreatedMessage | ErrorMessage | ForceLogoutMessage | ChatMessageMessage | ChatMessageUpdatedMessage | ChatMessageDeletedMessage | ChatTypingUpdateMessage | ChatReadUpdateMessage;