feat: web UI chat render, panels, presence + analytics
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import asyncpg
|
||||
|
||||
from utils.read.accounts import self_user_id
|
||||
from utils.read.message_view import build_message_view, load_raw, media_ref_from
|
||||
from utils.read.models import (
|
||||
ChatListItem,
|
||||
@@ -8,6 +9,7 @@ from utils.read.models import (
|
||||
MessageView,
|
||||
Page,
|
||||
)
|
||||
from utils.read.read_receipts import read_up_to
|
||||
|
||||
_MESSAGE_COLS = (
|
||||
"chat_id, message_id, date, sender_id, text, has_media, is_self_destruct, "
|
||||
@@ -52,35 +54,43 @@ async def list_chats(
|
||||
pool: asyncpg.Pool, account_id: int, page: Page
|
||||
) -> list[ChatListItem]:
|
||||
rows = await pool.fetch(
|
||||
"SELECT m.chat_id, count(*) AS message_count, max(m.date) AS last_date, "
|
||||
"WITH ids AS ("
|
||||
"SELECT DISTINCT chat_id FROM messages WHERE account_id = $1 "
|
||||
"UNION SELECT chat_id FROM dialogs WHERE account_id = $1), "
|
||||
"agg AS (SELECT chat_id, count(*) AS message_count, max(date) AS last_date "
|
||||
"FROM messages WHERE account_id = $1 GROUP BY chat_id) "
|
||||
"SELECT ids.chat_id, COALESCE(agg.message_count, 0) AS message_count, "
|
||||
"agg.last_date AS last_date, "
|
||||
"(SELECT p.first_name FROM peers p "
|
||||
"WHERE p.account_id = $1 AND p.peer_id = m.chat_id) AS first_name, "
|
||||
"WHERE p.account_id = $1 AND p.peer_id = ids.chat_id) AS first_name, "
|
||||
"(SELECT p.last_name FROM peers p "
|
||||
"WHERE p.account_id = $1 AND p.peer_id = m.chat_id) AS last_name, "
|
||||
"WHERE p.account_id = $1 AND p.peer_id = ids.chat_id) AS last_name, "
|
||||
"(SELECT p.username FROM peers p "
|
||||
"WHERE p.account_id = $1 AND p.peer_id = m.chat_id) AS username, "
|
||||
"WHERE p.account_id = $1 AND p.peer_id = ids.chat_id) AS username, "
|
||||
"(SELECT ch.title FROM chat_history ch "
|
||||
"WHERE ch.account_id = $1 AND ch.chat_id = m.chat_id "
|
||||
"WHERE ch.account_id = $1 AND ch.chat_id = ids.chat_id "
|
||||
"AND ch.title IS NOT NULL ORDER BY ch.ts DESC LIMIT 1) AS group_title, "
|
||||
"EXISTS (SELECT 1 FROM avatars a "
|
||||
"WHERE a.account_id = $1 AND a.owner_id = m.chat_id) AS has_avatar, "
|
||||
"(SELECT COALESCE((p.raw->>'is_bot')::bool, (p.raw->>'bot')::bool, false) "
|
||||
"FROM peers p WHERE p.account_id = $1 AND p.peer_id = m.chat_id) AS is_bot, "
|
||||
"WHERE a.account_id = $1 AND a.owner_id = ids.chat_id) AS has_avatar, "
|
||||
"(SELECT COALESCE((p.raw->>'is_bot')::bool, (p.raw->>'bot')::bool, "
|
||||
"p.raw->>'type' = 'ChatType.BOT', false) "
|
||||
"FROM peers p WHERE p.account_id = $1 AND p.peer_id = ids.chat_id) AS is_bot, "
|
||||
"(SELECT COALESCE((p.raw->>'is_contact')::bool, (p.raw->>'contact')::bool, "
|
||||
"false) FROM peers p "
|
||||
"WHERE p.account_id = $1 AND p.peer_id = m.chat_id) AS is_contact, "
|
||||
"(SELECT ch.raw->'chat'->>'type' = 'ChatType.CHANNEL' FROM chat_history ch "
|
||||
"WHERE ch.account_id = $1 AND ch.chat_id = m.chat_id "
|
||||
"AND ch.raw->'chat'->>'type' IS NOT NULL "
|
||||
"WHERE p.account_id = $1 AND p.peer_id = ids.chat_id) AS is_contact, "
|
||||
"(SELECT COALESCE(ch.raw->'chat'->>'type', ch.raw->>'type') "
|
||||
"= 'ChatType.CHANNEL' FROM chat_history ch "
|
||||
"WHERE ch.account_id = $1 AND ch.chat_id = ids.chat_id "
|
||||
"AND COALESCE(ch.raw->'chat'->>'type', ch.raw->>'type') IS NOT NULL "
|
||||
"ORDER BY ch.ts DESC LIMIT 1) AS is_broadcast, "
|
||||
"(SELECT lm.text FROM messages lm "
|
||||
"WHERE lm.account_id = $1 AND lm.chat_id = m.chat_id "
|
||||
"WHERE lm.account_id = $1 AND lm.chat_id = ids.chat_id "
|
||||
"ORDER BY lm.date DESC, lm.message_id DESC LIMIT 1) AS last_text, "
|
||||
"(SELECT lm.sender_id FROM messages lm "
|
||||
"WHERE lm.account_id = $1 AND lm.chat_id = m.chat_id "
|
||||
"WHERE lm.account_id = $1 AND lm.chat_id = ids.chat_id "
|
||||
"ORDER BY lm.date DESC, lm.message_id DESC LIMIT 1) AS last_sender_id "
|
||||
"FROM messages m WHERE m.account_id = $1 "
|
||||
"GROUP BY m.chat_id ORDER BY last_date DESC LIMIT $2 OFFSET $3",
|
||||
"FROM ids LEFT JOIN agg ON agg.chat_id = ids.chat_id "
|
||||
"ORDER BY last_date DESC NULLS LAST, ids.chat_id DESC LIMIT $2 OFFSET $3",
|
||||
account_id,
|
||||
page.capped_limit,
|
||||
page.offset,
|
||||
@@ -146,9 +156,43 @@ async def get_chat_history(
|
||||
else:
|
||||
views.append(_build_album(members, media_by_key))
|
||||
index = end
|
||||
await _apply_read_status(pool, account_id, chat_id, views)
|
||||
return views
|
||||
|
||||
|
||||
async def get_message(
|
||||
pool: asyncpg.Pool, account_id: int, chat_id: int, message_id: int
|
||||
) -> MessageView | None:
|
||||
row = await pool.fetchrow(
|
||||
f"SELECT {_MESSAGE_COLS} FROM messages " # noqa: S608
|
||||
"WHERE account_id = $1 AND chat_id = $2 AND message_id = $3",
|
||||
account_id,
|
||||
chat_id,
|
||||
message_id,
|
||||
)
|
||||
if row is None:
|
||||
return None
|
||||
media_by_key = await _media_map(pool, account_id, [row])
|
||||
raw = load_raw(row["raw"])
|
||||
view = build_message_view(row, raw, _single_media(row, raw, media_by_key))
|
||||
await _apply_read_status(pool, account_id, chat_id, [view])
|
||||
return view
|
||||
|
||||
|
||||
async def _apply_read_status(
|
||||
pool: asyncpg.Pool, account_id: int, chat_id: int, views: list[MessageView]
|
||||
) -> None:
|
||||
self_id = await self_user_id(pool, account_id)
|
||||
if self_id is None:
|
||||
return
|
||||
marker = await read_up_to(pool, account_id, chat_id)
|
||||
if marker is None:
|
||||
return
|
||||
for view in views:
|
||||
if view.sender_id == self_id and view.message_id <= marker:
|
||||
view.read = True
|
||||
|
||||
|
||||
def _build_album(
|
||||
members: list[tuple[asyncpg.Record, dict]],
|
||||
media_by_key: dict[tuple[int, int], asyncpg.Record],
|
||||
|
||||
Reference in New Issue
Block a user