feat: implement room privacy system with password protection and mess…#111
Open
prajwallshetty wants to merge 2 commits into
Open
feat: implement room privacy system with password protection and mess…#111prajwallshetty wants to merge 2 commits into
prajwallshetty wants to merge 2 commits into
Conversation
|
@prajwallshetty is attempting to deploy a commit to the freemanjiang's projects Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Introduces support for private rooms protected by a 6‑digit PIN, plus “unsend” for chat messages, with shared type updates and corresponding server/client wiring.
Changes:
- Added room privacy/PIN primitives (HTTP endpoints, WS actions/events, RoomManager password hashing & validation).
- Added chat message soft-delete (“unsend”) flow end-to-end (schema, handler, broadcast, client UI/state).
- Added tests and updated persisted chat fixtures to include
isDeleted.
Reviewed changes
Copilot reviewed 25 out of 26 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/shared/types/basic.ts | Adds isDeleted to chat message schema/type. |
| packages/shared/types/WSRequest.ts | Adds WS actions + schemas for setting room PIN and unsending messages. |
| packages/shared/types/WSBroadcast.ts | Adds broadcast event schemas for message deletion and room privacy changes. |
| packages/shared/types/HTTPRequest.ts | Extends room preview with isPrivate + adds /room-info and /check-password request/response schemas. |
| bun.lock | Updates lockfile for added bcrypt dependencies. |
| apps/server/src/websocket/registry.ts | Registers new WS actions and handlers. |
| apps/server/src/websocket/handlers/handleUnsendMessage.ts | Implements UNSEND_MESSAGE handling + broadcast. |
| apps/server/src/websocket/handlers/handleSetRoomPassword.ts | Implements SET_ROOM_PASSWORD handling + broadcast. |
| apps/server/src/routes/websocket.ts | Adds WS upgrade-time PIN enforcement for private rooms. |
| apps/server/src/routes/roomInfo.ts | Adds room privacy info endpoint for pre-WS UX. |
| apps/server/src/routes/checkPassword.ts | Adds PIN verification endpoint. |
| apps/server/src/managers/RoomManager.ts | Adds password hashing/storage + privacy state + unsend delegation. |
| apps/server/src/managers/GlobalManager.ts | Hides private rooms from discovery. |
| apps/server/src/managers/ChatManager.ts | Adds soft-delete implementation for chat messages. |
| apps/server/src/index.ts | Wires new HTTP routes and async WS upgrade handler. |
| apps/server/src/tests/privateRoomAndUnsend.test.ts | Adds tests for PIN + unsend behavior. |
| apps/server/src/tests/chatPersistence.test.ts | Updates chat persistence test data with isDeleted. |
| apps/server/package.json | Adds bcryptjs + types dependency. |
| apps/client/src/store/chat.tsx | Adds isRoomPrivate + delete message action to client chat store. |
| apps/client/src/lib/api.ts | Adds client API calls for /room-info and /check-password. |
| apps/client/src/components/room/WebSocketManager.tsx | Sends password on WS connect; handles new ROOM_EVENT types. |
| apps/client/src/components/room/TopBar.tsx | Adds admin UI to set/clear room PIN via WS. |
| apps/client/src/components/dashboard/right/Chat.tsx | Adds private badge + per-message “unsend” UI and WS request. |
| apps/client/src/components/PasswordModal.tsx | Adds pre-join PIN entry modal + verification flow. |
| apps/client/src/components/NewSyncer.tsx | Adds pre-WS privacy check + password gate before connecting. |
| apps/client/src/components/Join.tsx | Adds “create private room” UI + local PIN persistence on creation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+96
to
99
| const password = typeof window !== "undefined" ? sessionStorage.getItem(`room-password-${roomId}`) : null; | ||
| const passwordParam = password ? `&password=${encodeURIComponent(password)}` : ""; | ||
| const SOCKET_URL = `${getWsUrl()}?roomId=${roomId}&username=${username}&clientId=${clientId}${adminParam}${creatorParam}${passwordParam}`; | ||
| console.log("Creating new WS connection to", SOCKET_URL); |
| } | ||
| }; | ||
|
|
||
| const handleKeyDown = (index: number, e: React.KeyboardEvent<HTMLInputElement>) => { |
| } | ||
| }; | ||
|
|
||
| const handlePaste = (e: React.ClipboardEvent) => { |
Comment on lines
+40
to
+61
| // Pre-check if the room is private before connecting the WebSocket | ||
| useEffect(() => { | ||
| let cancelled = false; | ||
| fetchRoomInfo(roomId) | ||
| .then((info) => { | ||
| if (cancelled) return; | ||
| setIsPrivate(info.isPrivate); | ||
| if (!info.isPrivate) setPasswordVerified(true); | ||
| }) | ||
| .catch(() => { | ||
| if (!cancelled) setPasswordVerified(true); // Fail open — let WS handle auth | ||
| }) | ||
| .finally(() => { | ||
| if (!cancelled) setRoomInfoLoaded(true); | ||
| }); | ||
| return () => { | ||
| cancelled = true; | ||
| }; | ||
| }, [roomId]); | ||
|
|
||
| const showPasswordGate = roomInfoLoaded && isPrivate && !passwordVerified; | ||
| const canConnect = roomInfoLoaded && (!isPrivate || passwordVerified); |
Comment on lines
+84
to
103
| const handleConfirmCreate = () => { | ||
| if (makePrivate) { | ||
| if (!/^\d{6}$/.test(createPin)) { | ||
| setCreateError("PIN must be exactly 6 digits"); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| setIsCreating(true); | ||
|
|
||
| // Generate a random 6-digit room ID | ||
| const newRoomId = Math.floor(100000 + Math.random() * 900000).toString(); | ||
|
|
||
| if (makePrivate) { | ||
| sessionStorage.setItem(`room-password-${newRoomId}`, createPin); | ||
| sessionStorage.setItem(`password-verified-${newRoomId}`, "true"); | ||
| } | ||
|
|
||
| router.push(`/room/${newRoomId}`); | ||
| }; |
Comment on lines
+49
to
+52
| const MessageDeletedSchema = z.object({ | ||
| type: z.literal("MESSAGE_DELETED"), | ||
| messageId: z.number(), | ||
| }); |
Comment on lines
+21
to
+27
| if (!deleted) { | ||
| // Message not found or the requester is not the sender — silently ignore | ||
| console.warn( | ||
| `Room ${ws.data.roomId}: UNSEND_MESSAGE ignored for messageId=${message.messageId} by ${ws.data.clientId}` | ||
| ); | ||
| return; | ||
| } |
Comment on lines
+136
to
+148
| <button | ||
| onClick={() => { | ||
| setPinDialogOpen((o) => !o); | ||
| setPinError(null); | ||
| }} | ||
| title={isRoomPrivate ? "Change room PIN" : "Set room PIN"} | ||
| className={cn( | ||
| "flex items-center gap-1 text-xs transition-colors outline-none rounded-sm", | ||
| isRoomPrivate ? "text-primary hover:text-primary/80" : "text-neutral-400 hover:text-white" | ||
| )} | ||
| > | ||
| {isRoomPrivate ? <Lock className="h-3.5 w-3.5" /> : <LockOpen className="h-3.5 w-3.5" />} | ||
| </button> |
Comment on lines
+259
to
+269
| <motion.button | ||
| initial={{ opacity: 0, scale: 0.8 }} | ||
| animate={{ opacity: 1, scale: 1 }} | ||
| exit={{ opacity: 0, scale: 0.8 }} | ||
| transition={{ duration: 0.12 }} | ||
| onClick={() => handleUnsend(msg.id)} | ||
| title="Unsend message" | ||
| className="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-neutral-800 text-neutral-500 hover:bg-red-900/40 hover:text-red-400 transition-colors duration-150" | ||
| > | ||
| <Trash2 className="h-3 w-3" /> | ||
| </motion.button> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add Private Rooms with Password Protection and Message Unsend
Description
This PR introduces two privacy-focused features to improve the Beatsync room experience:
Changes Made
Private Rooms
Message Unsend
Why This Change?
Currently, anyone with a room link can access room chats. Password protection gives hosts better control over who can join.
Additionally, users may accidentally send messages and have no way to remove them. The unsend feature provides a better user experience and aligns with modern messaging platforms.
Testing
Future Improvements
Screenshots
Add screenshots or screen recordings here.
Checklist