feat(chat): add presence metadata and improve web chat core
Some checks failed
CI / test (push) Failing after 22s

- add user last_seen_at with alembic migration and persist on realtime disconnect
- extend chat serialization with private online/last_seen, group members/online, channel subscribers
- add Redis batch presence lookup helper
- update web chat list/header to display status counters and last-seen labels
- improve delivery receipt handling using last_delivered/last_read boundaries
- include chat info panel and related API/type updates
This commit is contained in:
2026-03-08 02:02:09 +03:00
parent 51275692ac
commit e6a271f8be
17 changed files with 564 additions and 6 deletions

View File

@@ -145,7 +145,7 @@ export function ChatList() {
</span>
)}
</div>
<p className="truncate text-xs text-slate-400">{chat.type}</p>
<p className="truncate text-xs text-slate-400">{chatMetaLabel(chat)}</p>
</div>
</div>
</button>
@@ -283,3 +283,46 @@ function chatLabel(chat: { display_title?: string | null; title: string | null;
if (chat.type === "group") return "Group";
return "Channel";
}
function chatMetaLabel(chat: {
type: "private" | "group" | "channel";
is_saved?: boolean;
counterpart_is_online?: boolean | null;
counterpart_last_seen_at?: string | null;
members_count?: number | null;
online_count?: number | null;
subscribers_count?: number | null;
}): string {
if (chat.is_saved) {
return "Personal cloud chat";
}
if (chat.type === "private") {
if (chat.counterpart_is_online) {
return "online";
}
if (chat.counterpart_last_seen_at) {
return `last seen ${formatLastSeen(chat.counterpart_last_seen_at)}`;
}
return "offline";
}
if (chat.type === "group") {
const members = chat.members_count ?? 0;
const online = chat.online_count ?? 0;
return `${members} members, ${online} online`;
}
const subscribers = chat.subscribers_count ?? chat.members_count ?? 0;
return `${subscribers} subscribers`;
}
function formatLastSeen(value: string): string {
const date = new Date(value);
if (Number.isNaN(date.getTime())) {
return "recently";
}
return date.toLocaleString(undefined, {
day: "2-digit",
month: "short",
hour: "2-digit",
minute: "2-digit"
});
}