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:
@@ -94,13 +94,30 @@ def _system_partner() -> dict:
|
|||||||
return {"id": SYSTEM_PARTNER_ID, "name": SYSTEM_NAME, "avatar_url": None}
|
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 {
|
return {
|
||||||
"id": m.id,
|
"id": m.id,
|
||||||
"kind": m.kind,
|
"kind": m.kind,
|
||||||
"sender_id": m.sender_id,
|
"sender_id": m.sender_id,
|
||||||
"recipient_id": m.recipient_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,
|
"ciphertext": m.ciphertext,
|
||||||
"iv": m.iv,
|
"iv": m.iv,
|
||||||
"read_at": iso(m.read_at),
|
"read_at": iso(m.read_at),
|
||||||
@@ -124,7 +141,7 @@ def ensure_welcome(db: Session, user: User) -> None:
|
|||||||
db.add(m)
|
db.add(m)
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(m)
|
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 -----------------------------------------
|
# --- E2EE key directory / blob store -----------------------------------------
|
||||||
@@ -313,7 +330,7 @@ def list_conversations(user: User = Depends(require_human), db: Session = Depend
|
|||||||
items.append(
|
items.append(
|
||||||
{
|
{
|
||||||
"partner": partner,
|
"partner": partner,
|
||||||
"last_message": _serialize_msg(m),
|
"last_message": _serialize_msg(m, lang_for_user(user)),
|
||||||
"unread": int(unread.get(pid, 0)),
|
"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
|
# 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.
|
# count on this signal, so no number is carried here.
|
||||||
manager.push(user.id, {"type": "unread"})
|
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("")
|
@router.post("")
|
||||||
|
|||||||
@@ -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."]
|
||||||
Reference in New Issue
Block a user