Skip to content

Direct messages beta: desktop right-click parity, release notes, and production hardening - #1

Merged
DaDevMikey merged 6 commits into
masterfrom
copilot/improve-website-performance-and-design
Aug 11, 2026
Merged

Direct messages beta: desktop right-click parity, release notes, and production hardening#1
DaDevMikey merged 6 commits into
masterfrom
copilot/improve-website-performance-and-design

Conversation

Copilot AI commented Aug 11, 2026

Copy link
Copy Markdown

Adds a Direct Messages feature (1:1, end-to-end encrypted, auto-cleared after 24h) behind a gradual rollout, an in-app release notes dialog, and confirms desktop right-click parity for mobile long-press actions. Follow-up work widens the rollout (random per-load enrolment plus a Settings toggle) and hardens the app so the new surface can't be exploited.

Direct messages

  • Every account gets a random handle (swift-otter-4821); handles are the only way to start a DM.
  • Threads live in a dedicated direct_threads collection, not rooms. This matters for security rules: a participant-only read must be provable from the query. Keeping DMs in rooms would have forced a disjunctive read rule, which breaks the public-room listener, join-by-code, and logout ownership queries.
// provable — rules can match this against `participants`
db.collection('direct_threads').where('participants', 'array-contains', uid)
  • ChatRoomView is parameterised with isDirect + a roomRef, so both collections share one chat UI.
  • Threads expire after 24h, are swept on load, deleted on logout, and can be cleared early from the DM list.

Rollout controls

  • tags.beta on the user document gates the feature; absent/false means no access.
  • 25% at signup, 5% re-roll on every load. Enrolment is one-way — a roll can only ever set beta: true.
  • Settings toggle for explicit opt-in/out; opting out sets tags.betaOptOut, which suppresses future rolls. Re-roll runs in a transaction so it can't clobber a concurrent opt-out.

Identity and security rules

  • New one-shot reservation documents usernames/{lowercase} and handles/{handle} bind a name to an auth uid. Lookups are get-by-id, so users documents are now readable only by their owner (previously enumerable, and handle lookup via .limit(1) query was capture-prone).
  • Rules bind a message's sender and a room/thread's creator to the caller's reservation; message creates require membership in the target thread; direct-thread membership, key and expiry are immutable; room/message writes are owner/author-scoped, with reaction-only updates for everyone else.
  • Room and thread codes double as encryption secrets and now come from crypto.getRandomValues rather than Math.random.
  • reply_to.content previews are encrypted (previously stored as plaintext inside otherwise-encrypted threads).

Release notes

  • src/releaseNotes.js holds versioned entries; ReleaseNotesModal auto-opens once per CURRENT_RELEASE and is reopenable from Settings. The DM entry is labelled as a gradual beta rollout.

Robustness

  • All cleanup deletes route through src/utils/batch.js, chunked under Firestore's 500-write batch limit.
  • Accounts with no tags map are seeded and re-rolled; a username claimed by another account signs the stale session out with a clear message.

Notes for reviewers

  • Right-click already opened the same action sheet as long-press (MessageBubble onContextMenu); no change was needed, only documentation.
  • Accepted trade-offs, documented in firestore.rules and the README: message ciphertext plus metadata is readable by any signed-in client (a room_id query can't prove thread membership); private room codes are visible to anyone listing rooms, which is what makes join-by-code work; typing/presence in public rooms isn't identity-bound.
  • firebase-tools could not be installed in this environment, so the rules were reviewed by hand. Worth running firebase emulators:start --only firestore against them before deploying.

@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
temp-chats Ready Ready Preview Aug 11, 2026 3:02pm

@DaDevMikey
DaDevMikey marked this pull request as ready for review August 11, 2026 15:04
Copilot AI lite review requested due to automatic review settings August 11, 2026 15:04
@DaDevMikey
DaDevMikey merged commit 05b68ed into master Aug 11, 2026
2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces a beta Direct Messages surface (separate direct_threads collection with handles + rollout tags), adds an in-app Release Notes modal, and applies a broader UI/UX refresh plus production-hardening changes (cleanup batching, crypto-grade codes, chat perf improvements).

Changes:

  • Added Direct Messages UI, handle/username reservation utilities, rollout controls, and DM/thread cleanup flows.
  • Hardened Firestore rules and client behavior around identity, message encryption (incl. reply previews), and cleanup batching.
  • Added “What’s new” release notes (data + modal) and multiple UI/accessibility improvements across views and components.

Reviewed changes

Copilot reviewed 25 out of 26 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
vite.config.js Build output tuning (chunking + build flags).
src/views/LoginView.jsx Username reservation-based login + beta tag seeding + DM handle allocation; login UI refresh.
src/views/HomeView.jsx Home UI refresh; join-by-code validation; DM entry point behind beta flag.
src/views/DirectMessagesView.jsx New DM list + “start DM by handle” + delete thread flow.
src/views/CreateRoomView.jsx Secure room code generation + UI/accessibility polish.
src/views/ChatRoomView.jsx DM/room unification (isDirect), message windowing + decryption cache, reply preview encryption, composer UX updates.
src/utils/profile.js New identity reservation + profile sync + beta preference helpers.
src/utils/beta.js New rollout utilities + secure code / DM handle generation constants.
src/utils/batch.js New chunked batch delete helper for Firestore 500-write limit.
src/styles.css Large design-system update (tokens, One UI ergonomics, dialogs, chips, chat layout).
src/releaseNotes.js New release notes data + CURRENT_RELEASE.
src/components/UserSettingsModal.jsx Settings UI refactor; DM beta toggle; entry point to release notes.
src/components/TopAppBar.jsx App bar layout/accessibility improvements.
src/components/Snackbar.jsx ARIA live region + layout tweaks.
src/components/RoomCard.jsx Memoization + semantic markup + minor timer robustness.
src/components/ReleaseNotesModal.jsx New release notes modal implementation.
src/components/PrivacyModal.jsx Refactored content + accessibility attributes.
src/components/PrivacyBanner.jsx Accessibility improvements; replaces anchor with button.
src/components/MessageBubble.jsx Touch action sheet via long-press + portal; toolbar/accessibility updates.
src/components/DialogModal.jsx Escape-to-close + scroll lock; better dialog semantics; optional cancel button.
src/App.jsx Adds DM routes, release notes gating, profile sync, cleanup batching, beta preference toggle, and purge adjustments.
README.md Updates feature list + documents data model/security posture.
package.json Version bump to 2.2.0.
package-lock.json Version bump to 2.2.0.
index.html Viewport/safe-area tuning + PWA-ish meta tags + defer firebase config load.
firestore.rules Major rules hardening + new collections (direct_threads, usernames, handles) + identity binding.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread firestore.rules
Comment on lines +28 to +33
function ownsName(name) {
return name is string
&& name.size() > 0
&& (!exists(usernamePath(name))
|| get(usernamePath(name)).data.authUid == request.auth.uid);
}
Comment thread firestore.rules
Comment on lines +219 to +223
// Authors edit their own text; anyone in the conversation can react.
allow update: if isSignedIn() && (
(isMessageAuthor() && onlyChanged(['content', 'edited', 'reactions']))
|| onlyChanged(['reactions'])
);
Comment thread src/App.jsx
Comment on lines +71 to +76
if (updated.usernameConflict) {
localStorage.removeItem('tempchats_user');
setUser(null);
showSnackbar('That username now belongs to someone else. Please pick a new one.', 'error');
return;
}
Comment thread src/utils/profile.js
Comment on lines +87 to +95
const tags = await db.runTransaction(async (tx) => {
const fresh = await tx.get(ref);
const freshTags = (fresh.exists && fresh.data().tags && typeof fresh.data().tags === 'object')
? fresh.data().tags
: null;

const current = freshTags || {};
if (!shouldEnrolOnRefresh(current)) {
if (freshTags) return freshTags;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants