feat: profile/settings (rest timer toggle)

Settings infrastructure + one working preference:

- New user_settings table (JSON blob per user, so adding
  future keys needs no migration).
- db.get_settings / update_settings helpers (merge semantics).
- GET/PUT /api/settings endpoints.
- New Settings tab in the Mini App with a rest-timer on/off
  toggle. Setting is loaded on init and written through on
  change; the rest-timer display now respects it.

Units (kg/lb) and language are intentionally left unwired for
now — each needs end-to-end display/input changes and deserve
focused passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Danny 2026-04-19 15:36:06 +02:00
parent 9636d6870e
commit 6d1de53b2e
6 changed files with 181 additions and 2 deletions

38
db.py
View file

@ -80,6 +80,12 @@ def init_db():
ON events(user_id, created_at);
CREATE INDEX IF NOT EXISTS idx_events_kind_created
ON events(kind, created_at);
CREATE TABLE IF NOT EXISTS user_settings (
user_id INTEGER PRIMARY KEY,
data TEXT NOT NULL DEFAULT '{}',
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
""")
# Migrations
@ -373,6 +379,38 @@ def get_events(
return out
def get_settings(user_id: int) -> dict:
"""Return the user's settings dict (empty dict if none stored)."""
with get_db() as conn:
row = conn.execute(
"SELECT data FROM user_settings WHERE user_id = ?", (user_id,)
).fetchone()
if not row:
return {}
try:
return json.loads(row["data"]) or {}
except json.JSONDecodeError:
return {}
def update_settings(user_id: int, patch: dict) -> dict:
"""Merge `patch` into the user's settings and return the new full dict."""
if not isinstance(patch, dict):
raise TypeError("patch must be a dict")
current = get_settings(user_id)
current.update(patch)
with get_db() as conn:
conn.execute(
"""INSERT INTO user_settings (user_id, data, updated_at)
VALUES (?, ?, datetime('now'))
ON CONFLICT(user_id) DO UPDATE SET
data = excluded.data,
updated_at = excluded.updated_at""",
(user_id, json.dumps(current)),
)
return current
def save_feedback(user_id: int, text: str) -> int:
"""Save user feedback. Returns the feedback id."""
with get_db() as conn: