Blur gallery images by default, sync progress UI, and add chat/monitor tests

- Blur NSFW media gallery images until hover/reveal
- Surface chat sync progress in the dashboard header
- Extend chat service, cache, and monitor with delta-media handling
- Add tests for chat cooldown, delta media, and monitor flows

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jjbruntonandClaude Opus 4.7 committed 2026-05-22 10:01:09 +01:00
1 parent 633bb0ec04
commit 2260388dc8
15 files changed
+675 -41

No files matched your search

+28 -9
View File
@@ -32,13 +32,20 @@ export function createConversationsRouter(ctx: WebContext): Router {
return;
}
try {
// Pull in any new activity from the homeserver before we read the cache,
// so a stale "waiting for them to reply" view picks up the reply they
// already sent. Cheap (~one /sync call) and queued behind any in-flight sync.
try {
await ctx.services.chat.refreshTopChats();
} catch (err) {
ctx.logger.warn('Pre-open delta sync failed:', err);
// If a sync is already in flight (e.g. a Full sync the user kicked off
// from the dashboard), don't queue *another* delta + lazy-load behind it
// — that's what makes the modal sit on "Loading…" for minutes. Render
// from cache and let the frontend show a "sync in progress" banner; the
// user can reopen once it's done.
const syncStatus = ctx.services.chat.getSyncStatus();
const syncInFlight = syncStatus.syncing;
if (!syncInFlight) {
try {
await ctx.services.chat.refreshTopChats();
} catch (err) {
ctx.logger.warn('Pre-open delta sync failed:', err);
}
}
const conversation = await findConversation(decodeURIComponent(req.params.id));
if (!conversation) {
@@ -53,7 +60,9 @@ export function createConversationsRouter(ctx: WebContext): Router {
? conversation.messages[conversation.messages.length - 1].timestamp
: 0;
const hasNewerActivity = (conversation.lastMessageTime ?? 0) > cachedLastTs;
if (!conversation.messages || conversation.messages.length === 0 || hasNewerActivity) {
const needsLazyLoad =
!conversation.messages || conversation.messages.length === 0 || hasNewerActivity;
if (needsLazyLoad && !syncInFlight) {
try {
await ctx.services.chat.loadConversationDetails(conversation);
await ctx.services.chat.saveCacheExternal();
@@ -88,7 +97,17 @@ export function createConversationsRouter(ctx: WebContext): Router {
ctx.logger.warn(`Failed to look up triggering post for ${conversation.username}:`, err);
}
res.json({ ...conversation, triggeringPost });
res.json({
...conversation,
triggeringPost,
syncInProgress: syncInFlight
? {
kind: syncStatus.syncingKind,
progress: syncStatus.progress,
staleMessages: needsLazyLoad,
}
: null,
});
} catch (error) {
ctx.logger.error('Failed to fetch conversation:', error);
res.status(500).json({ error: String(error) });