Redesign message viewer as Telegram-style chat with bubbles, replies, infinite scroll, and orientation

- Chat bubbles with 1px border-radius, angular tail arrows
- Own messages right (blue), others left (dark) via sender_id vs user_id
- Reply preview with left border accent, clickable scroll to original
- Infinite scroll up via IntersectionObserver on scroll-sentinel
- Date separators (Today/Yesterday/date) between messages
- Full-height messages panel with sidebar (channels) + header
- Backend: reply_to_message data in load_messages(), user_id exposed in auth snapshot
- Auto-scroll to bottom on channel load (triple scrollTop fallback)
- history.scrollRestoration = manual to prevent mid-page jumps
This commit is contained in:
2026-05-27 01:01:18 +02:00
parent 1dfe41950e
commit c5361f2271
4 changed files with 532 additions and 106 deletions
+39
View File
@@ -205,6 +205,37 @@ def load_messages(
"reactions": row["reactions"],
}
)
reply_ids = list({m["reply_to"] for m in messages if m["reply_to"]})
if reply_ids:
placeholders = ",".join("?" for _ in reply_ids)
try:
conn2 = sqlite3.connect(str(db_path))
conn2.row_factory = sqlite3.Row
try:
rows2 = conn2.execute(
f"SELECT message_id, message, first_name, last_name, username "
f"FROM messages WHERE message_id IN ({placeholders})",
reply_ids,
).fetchall()
reply_map: Dict[int, Dict[str, str]] = {}
for r in rows2:
r_sender = " ".join(
part for part in [r["first_name"], r["last_name"]] if part
).strip() or (r["username"] or "")
reply_map[r["message_id"]] = {
"text": (r["message"] or "")[:300],
"sender_name": r_sender,
}
for m in messages:
rt = m.get("reply_to")
if rt and rt in reply_map:
m["reply_to_message"] = reply_map[rt]
finally:
conn2.close()
except sqlite3.Error:
pass
return messages
@@ -328,6 +359,7 @@ class TelegramAuthManager:
self.thread = threading.Thread(target=self._run_loop, daemon=True)
self.thread.start()
self.client: Optional[TelegramClient] = None
self.user_id: Optional[int] = None
self.qr_login = None
self.qr_wait_task = None
self.phone = None
@@ -359,6 +391,7 @@ class TelegramAuthManager:
auth = auth_status()
with self.lock:
snapshot = dict(self.state)
snapshot["user_id"] = self.user_id
snapshot["saved_credentials"] = {
"api_id": load_state().get("api_id"),
"api_hash_present": bool(load_state().get("api_hash")),
@@ -376,6 +409,12 @@ class TelegramAuthManager:
self.client = TelegramClient(str(SESSION_DIR / "session"), api_id, api_hash)
if not self.client.is_connected():
await self.client.connect()
if self.user_id is None and await self.client.is_user_authorized():
try:
me = await self.client.get_me()
self.user_id = me.id
except Exception:
pass
return self.client
async def _is_authorized(self) -> bool: