fix(messages): the welcome reads in the reader's language, not the one it was created in

E31 S1: the welcome is rendered once, at account creation, so an account that
switched language kept it in the old one (the owner's, English under a
Hungarian UI). A stored copy is recognised by its opening line (derived from
WELCOME) and served in the reader's language; other system messages stay as
stored.
This commit is contained in:
2026-10-01 21:17:09 +02:00
parent 29c0715237
commit 2543d44860
2 changed files with 75 additions and 5 deletions
+22 -5
View File
@@ -94,13 +94,30 @@ def _system_partner() -> dict:
return {"id": SYSTEM_PARTNER_ID, "name": SYSTEM_NAME, "avatar_url": None}
def _serialize_msg(m: Message) -> dict:
# The welcome's opening line in every language — how a stored copy is recognised. It was rendered
# once, in the language of the day it was created, so an account that later switched language kept
# reading it in the old one (E31 S1: the owner's, in English). Recognised here, it is served in the
# reader's language instead. Derived from WELCOME itself, so only a change to an OPENING line would
# orphan the copies already stored.
_WELCOME_OPENINGS = {text.partition("\n")[0] for text in WELCOME.values()}
def _system_body(body: str | None, lang: str | None) -> str | None:
"""A system message's text for a reader of `lang`: the welcome in their language, any other as
stored."""
if body is None or lang is None or body.partition("\n")[0] not in _WELCOME_OPENINGS:
return body
return WELCOME.get(lang, WELCOME["en"])
def _serialize_msg(m: Message, lang: str | None = None) -> dict:
"""`lang`: the reader's language, for a system message's text (a user message is ciphertext)."""
return {
"id": m.id,
"kind": m.kind,
"sender_id": m.sender_id,
"recipient_id": m.recipient_id,
"body": m.body, # only set for system messages
"body": _system_body(m.body, lang) if m.kind == "system" else m.body,
"ciphertext": m.ciphertext,
"iv": m.iv,
"read_at": iso(m.read_at),
@@ -124,7 +141,7 @@ def ensure_welcome(db: Session, user: User) -> None:
db.add(m)
db.commit()
db.refresh(m)
manager.push(user.id, {"type": "message", "message": _serialize_msg(m)})
manager.push(user.id, {"type": "message", "message": _serialize_msg(m, lang_for_user(user))})
# --- E2EE key directory / blob store -----------------------------------------
@@ -313,7 +330,7 @@ def list_conversations(user: User = Depends(require_human), db: Session = Depend
items.append(
{
"partner": partner,
"last_message": _serialize_msg(m),
"last_message": _serialize_msg(m, lang_for_user(user)),
"unread": int(unread.get(pid, 0)),
}
)
@@ -366,7 +383,7 @@ def get_thread(
# refreshes live instead of waiting for the next 20s poll. The client re-fetches the
# count on this signal, so no number is carried here.
manager.push(user.id, {"type": "unread"})
return {"partner": partner, "items": [_serialize_msg(m) for m in msgs]}
return {"partner": partner, "items": [_serialize_msg(m, lang_for_user(user)) for m in msgs]}
@router.post("")
+53
View File
@@ -0,0 +1,53 @@
"""E31 S1: the welcome message is rendered once, when the account is created, in the language of that day.
An account that switched language afterwards kept reading it in the old one (the owner's: English under a
Hungarian UI). It is served in the reader's language now; any other system message stays as stored.
Needs the DB."""
from fastapi.testclient import TestClient
import app.state as state
from app.auth import current_user
from app.db import get_db
from app.main import app
from app.models import Message, User
from app.routes.messages import WELCOME
def _client(db, user, monkeypatch, override_dep) -> TestClient:
monkeypatch.setattr(state, "_configured_cache", True)
override_dep(get_db, db.info["request"])
override_dep(current_user, lambda: user)
return TestClient(app)
def _user_with_english_welcome(db) -> User:
"""An account whose welcome was written in English, then switched to Hungarian."""
user = User(email="welcome-hu@example.com", preferences={"language": "hu"})
db.add(user)
db.flush()
db.add(Message(kind="system", sender_id=None, recipient_id=user.id, body=WELCOME["en"]))
db.commit()
return user
def test_the_welcome_is_read_in_the_readers_language(db, monkeypatch, override_dep):
user = _user_with_english_welcome(db)
client = _client(db, user, monkeypatch, override_dep)
thread = client.get("/api/messages/conversations/0").json()["items"]
assert [m["body"] for m in thread] == [WELCOME["hu"]]
convs = client.get("/api/messages/conversations").json()["items"]
assert [c["last_message"]["body"] for c in convs if c["partner"]["id"] == 0] == [WELCOME["hu"]]
def test_another_system_message_is_served_as_stored(db, monkeypatch, override_dep):
user = _user_with_english_welcome(db)
db.add(
Message(kind="system", sender_id=None, recipient_id=user.id, body="Maintenance tonight.")
)
db.commit()
client = _client(db, user, monkeypatch, override_dep)
bodies = [m["body"] for m in client.get("/api/messages/conversations/0").json()["items"]]
assert bodies == [WELCOME["hu"], "Maintenance tonight."]