eleven / messenger Public

remove original chat button (#174)

* web: make the app chat the sole chat — remove the original-feed toggle

Drops the 'Use original chat' menu item, the useOriginalChat flag/embed option,
and simplifies chatAppFor so every group/DM always renders the app engine. The
classic-feed rendering code is now unreachable; a follow-up deletes it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XR2CLfAnugzdMGk7wGfVLx

* docs: mark the retired original-chat feed as dead — ignore, don't edit

Adds a CLAUDE.md note + an in-file banner so future readers (human or LLM) know
the classic #feed renderer in chat.js is dead/unreachable and must not be edited
or extended — all chat behaviour lives in apps/chat.js. Pending deletion (phase 2).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XR2CLfAnugzdMGk7wGfVLx

---------
Co-authored-by: Claude <noreply@anthropic.com>
James Adam af2a0f71a459417df757dcc8eee7acacff30f51f parent 7d3b0aa
3 files changed, +22 −32
  • CLAUDE.md +9 −2
  • web/pages/index.html +0 −1
  • web/static/chat.js +13 −29
diff --git a/CLAUDE.md b/CLAUDE.md
index 333c4e9..f7e5a49 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -80,8 +80,15 @@ in, so deploy is one file (no cgo).
Frontend module layout:
- `crypto.js` — AES-GCM helpers (`encrypt`/`decrypt` for JSON, `encryptBytes`/
`decryptBytes` for binary). Format is base64url/`iv(12)||ciphertext`.
-- `chat.js` — the chat page (render, WebSocket, composer, reactions, attachments,
- link previews).
+- `chat.js` — the chat page **host/shell**: sidebar, thread list, WebSocket,
+ crypto/keys, uploads, unfurl, push, and the bridge that mounts the app chat
+ engine. ⚠️ It also still holds the **retired original-chat feed** — the dead,
+ unreachable classic `#feed` renderer (`appendMessage`, `renderItem`,
+ `sendMessage`, `renderTyping`, `toggleReaction`, `openMessageSheet`,
+ `buildFileCard`, `openLightbox`, the classic composer/message-action wiring, …).
+ **Ignore that code — don't read it as a reference, edit it, or extend it. All
+ chat behaviour lives in `apps/chat.js`.** It is pending deletion (phase 2 of
+ retiring the original chat).
- `format.js`, `ui.js` — **pure, framework-free helpers kept separate so they're
unit-testable in Node** (`tokenizeText`, `formatBytes`, `shouldSend`).
- `apps.js` + `apps/*.js` — the mini-app framework and the apps themselves (one
diff --git a/web/pages/index.html b/web/pages/index.html
index 715b125..4dce565 100644
--- a/web/pages/index.html
+++ b/web/pages/index.html
@@ -75,7 +75,6 @@
<div class="menu hidden" id="menu" role="menu">
<button class="menu-item" id="menu-notif" role="menuitem">Notifications</button>
<button class="menu-item" id="menu-theme" role="menuitem">Theme</button>
- <button class="menu-item" id="menu-original-chat" role="menuitem">Use original chat</button>
<button class="menu-item" id="menu-add-device" role="menuitem">Add a device / back up</button>
<button class="menu-item" id="menu-rename" role="menuitem">Edit profile</button>
<button class="menu-item danger" id="menu-leave" role="menuitem">Leave</button>
diff --git a/web/static/chat.js b/web/static/chat.js
index e667c47..5883949 100644
--- a/web/static/chat.js
+++ b/web/static/chat.js
@@ -1104,24 +1104,6 @@ function renderApp() {
wireItem("menu-rename", editProfile);
wireItem("menu-leave", leave);
- // Chat engine switch: the app feed is the default; this flips a device to
- // the original feed (and back) and reloads. The label reflects the state.
- const engineItem = document.getElementById("menu-original-chat");
- if (engineItem) {
- engineItem.textContent = useOriginalChat ? "Use new chat" : "Use original chat";
- engineItem.onclick = () => {
- if (useOriginalChat) localStorage.removeItem("lchat-original-chat");
- else localStorage.setItem("lchat-original-chat", "1");
- location.reload();
- };
- }
- // The subtle mode indicator: on the original feed, the composer placeholder
- // says so — visible exactly where you type, no extra chrome.
- if (useOriginalChat) {
- const composerText = document.getElementById("text");
- if (composerText) composerText.placeholder = "Message (original chat)";
- }
-
// Notifications toggle (hidden where push isn't usable, e.g. iOS before install).
const notif = document.getElementById("menu-notif");
const refreshNotif = async () => {
@@ -2111,6 +2093,15 @@ function visibleThreadIds() {
return ids.filter(Boolean).slice(0, PREBUILD_MAX);
}
+// ============================================================================
+// RETIRED — original-chat feed. DEAD, UNREACHABLE CODE (pending deletion).
+// The app engine (web/static/apps/chat.js) is the sole chat; `chatAppFor` never
+// routes here anymore. Everything from here to the "mini-apps" section below is
+// the classic `#feed` renderer + composer + message actions.
+// DO NOT edit, extend, or copy from this — all chat behaviour lives in
+// apps/chat.js. (See CLAUDE.md.) Shared helpers still used by the app host
+// (processImage, loadBlob, encryptBytes, unfurl fetch) live outside this block.
+// ============================================================================
// `container` lets the first open build the whole history into a detached fragment
// (see openThread) so there's no per-message reflow; live appends omit it and
// target the real #feed. Scroll/query still resolve against #feed either way.
@@ -3509,16 +3500,10 @@ function setAppThreadMode(on) {
if (tv) tv.classList.toggle("app-mode", on);
}
-// The chat mini-app (web/static/apps/chat.js) is the DEFAULT chat experience:
-// group/DM threads render it full-pane. The original feed remains available
-// per device — ⋮ menu → "Use original chat", or
-// localStorage.setItem("lchat-original-chat", "1")
-// — and the native shell can pin its webview to it by setting originalChat on
-// the injected __lchatEmbed config. Both feeds speak one protocol, so mixed
-// groups interoperate seamlessly.
-const useOriginalChat = embed ? !!embed.originalChat : localStorage.getItem("lchat-original-chat") === "1";
+// The chat mini-app (web/static/apps/chat.js) is the sole chat experience:
+// every group/DM thread renders it full-pane.
function chatAppFor(id) {
- if (useOriginalChat || threadApp[id]) return null;
+ if (threadApp[id]) return null;
const t = threads.find((x) => x.id === id);
if (!t || (t.kind !== "group" && t.kind !== "dm")) return null;
// A pending DM (invitee not joined) still renders the chat app — its pane is
@@ -4763,8 +4748,7 @@ function connectWS() {
if (tid === activeId) {
// A chat-app thread already drew this message via renderActiveSurfaces()
// above; the classic-feed appendMessage would only re-decrypt the same blob
- // and paint a phantom bubble into the hidden #feed, so skip it there. Original
- // clients (chatAppFor null, incl. useOriginalChat) still get the real append.
+ // and paint a phantom bubble into the hidden #feed, so skip it there.
if (!chatAppFor(tid)) await appendMessage(tid, msg, true);
} else {
unread[tid] = !ownMessage(payload, myUserId, session.name);