Round 4: hidden-msg pagination, iOS audio unlock, iOS long-press callout, draft hardening, pinned-by
Older-messages pagination (couple of chats wouldn't scroll back): the thread query
returned the latest 40 rows and JS filtered out hidden (delete-for-me) messages
AFTER the LIMIT, so a chat with a hidden recent message returned <40 → the client
read that as "no older history." Now excluded in SQL (repos.thread /
threadByConversation take the viewer id), so a page is always 40 VISIBLE rows.
Verified locally: hide 3 recent → page still returns 40 (older ones fill in).
#2 iOS in-chat tone was silent: WebAudio context is created suspended and only
resumes inside a user gesture. Added unlockAudio() on first tap/click (resume +
0-gain blip), re-armed each gesture so a background→foreground re-suspend recovers.
#9 Long-press "works once then stops" on images was iOS's native touch-callout
(Save Image / selection magnifier) hijacking the gesture. Disabled
-webkit-touch-callout/user-select on #msgs bubbles; added a Save action to the
sheet so image-saving isn't lost.
#13 Pin/unpin WAS being audited (verified: message.pin in /api/audit) — there's just
no in-app viewer. Surfaced "Pinned by X" in the pinned bar for immediate context.
#14 Hardened draft save: it now runs BEFORE maybeAutocorrect/autoGrow (wrapped) in the
input handler, so a throw there can't skip persisting the draft.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+12
-5
@@ -230,12 +230,17 @@ const messages = {
|
||||
// and silently dropped everything newer once a thread passed 300 — so new messages "disappeared".
|
||||
// The `before` cursor is added CONDITIONALLY (not as `? IS NULL OR …`): an all-NULL param has no type
|
||||
// for Postgres to infer. The subquery also needs an alias (`t`) — Postgres requires it. Both portable.
|
||||
// `a` is the VIEWER (u.id). Exclude the viewer's "deleted for me" (message_hidden) rows in SQL — not in
|
||||
// JS afterwards — so the LIMIT counts only VISIBLE messages. Filtering after the LIMIT returned < PAGE rows
|
||||
// whenever a recent message had been hidden, and the client read that as "no older history" and stopped
|
||||
// paginating (a chat with a deleted recent message wouldn't scroll back).
|
||||
thread: (teamId, a, b, limit = 500, before = null) => {
|
||||
const cond = before != null ? ' AND created_at < ?' : '';
|
||||
const args = before != null ? [teamId, a, b, b, a, before, limit] : [teamId, a, b, b, a, limit];
|
||||
const args = before != null ? [teamId, a, b, b, a, a, before, limit] : [teamId, a, b, b, a, a, limit];
|
||||
return db.prepare(`SELECT * FROM (
|
||||
SELECT * FROM messages WHERE team_id=? AND conversation_id IS NULL
|
||||
AND ((sender_id=? AND recipient_id=?) OR (sender_id=? AND recipient_id=?))${cond}
|
||||
AND ((sender_id=? AND recipient_id=?) OR (sender_id=? AND recipient_id=?))
|
||||
AND id NOT IN (SELECT message_id FROM message_hidden WHERE user_id=?)${cond}
|
||||
ORDER BY created_at DESC LIMIT ?
|
||||
) t ORDER BY created_at ASC`).all(...args);
|
||||
},
|
||||
@@ -252,11 +257,13 @@ const messages = {
|
||||
db.prepare('SELECT * FROM messages WHERE team_id=? AND conversation_id IS NULL AND (sender_id=? OR recipient_id=?) ORDER BY created_at DESC LIMIT ?')
|
||||
.all(teamId, userId, userId, limit),
|
||||
// Group conversation helpers.
|
||||
threadByConversation: (conversationId, limit = 500, before = null) => {
|
||||
threadByConversation: (conversationId, userId, limit = 500, before = null) => {
|
||||
const cond = before != null ? ' AND created_at < ?' : '';
|
||||
const args = before != null ? [conversationId, before, limit] : [conversationId, limit];
|
||||
const args = before != null ? [conversationId, userId, before, limit] : [conversationId, userId, limit];
|
||||
return db.prepare(`SELECT * FROM (
|
||||
SELECT * FROM messages WHERE conversation_id=?${cond} ORDER BY created_at DESC LIMIT ?
|
||||
SELECT * FROM messages WHERE conversation_id=?
|
||||
AND id NOT IN (SELECT message_id FROM message_hidden WHERE user_id=?)${cond}
|
||||
ORDER BY created_at DESC LIMIT ?
|
||||
) t ORDER BY created_at ASC`).all(...args);
|
||||
},
|
||||
searchConversation: (conversationId, like, limit = 300) =>
|
||||
|
||||
Reference in New Issue
Block a user