User avatar (Google picture), avatar in mobile You tab, /account page

- Capture the Google profile picture (picture claim) into users.avatar_url; an
  Avatar component shows it, falling back to the initial. Used in the desktop
  header and the mobile "You" tab (which now shows the user when signed in).
- Move account/settings to its own route /account (robust + scrolls to top),
  reached by the desktop avatar and the mobile You tab; drop the inline "You"
  sheet. AccountPanel gains a Sign out action; the page links to Saved/History/
  Boundaries via home intent params (?view= / ?open=).
- db: users.avatar_url (schema + idempotent migration). 118 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-03 14:41:43 +00:00
parent bb008cfaa5
commit 15728c3bcb
11 changed files with 168 additions and 87 deletions
+6
View File
@@ -136,6 +136,7 @@ CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE,
display_name TEXT,
avatar_url TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
@@ -231,6 +232,11 @@ def _migrate(conn: sqlite3.Connection) -> None:
if column not in score_cols:
conn.execute(f"ALTER TABLE article_scores ADD COLUMN {column} TEXT")
# users.avatar_url added for Google profile pictures.
user_tbl = {row["name"] for row in conn.execute("PRAGMA table_info(users)")}
if user_tbl and "avatar_url" not in user_tbl:
conn.execute("ALTER TABLE users ADD COLUMN avatar_url TEXT")
article_cols = {row["name"] for row in conn.execute("PRAGMA table_info(articles)")}
if "duplicate_of" not in article_cols:
conn.execute(