Bootstrap dashboard totals in Web App links

This commit is contained in:
Codex 2026-09-07 21:16:48 +00:00
parent e1b91a2bed
commit 912c41c3bb
4 changed files with 60 additions and 5 deletions

View file

@ -7,6 +7,7 @@ from datetime import UTC, datetime
from pathlib import Path
from threading import Thread
from typing import Any
from urllib.parse import urlencode
import telebot
from telebot import types
@ -19,8 +20,20 @@ logger = logging.getLogger(__name__)
CATEGORY_PATTERN = re.compile(r"^[^\x00-\x1f]{1,40}$")
def webapp_url_for_user(settings: Settings, user_id: int) -> str:
return f"{settings.webapp_url}?user_id={user_id}&mode=keyboard"
def webapp_url_for_user(
settings: Settings,
user_id: int,
summary: dict[str, Any] | None = None,
) -> str:
params: dict[str, str | int] = {"user_id": user_id, "mode": "keyboard"}
if summary:
params.update(
nickname=str(summary["nickname"]),
balance=int(summary["balance_cents"]),
income=int(summary["income_cents"]),
expense=int(summary["expense_cents"]),
)
return f"{settings.webapp_url}?{urlencode(params)}"
def decode_payload(raw: str) -> dict[str, Any]:
@ -107,7 +120,11 @@ class TelegramBotService:
types.KeyboardButton(
text="Открыть Пожитки",
web_app=types.WebAppInfo(
url=webapp_url_for_user(self.settings, user_id)
url=webapp_url_for_user(
self.settings,
user_id,
self.database.summary(user_id),
)
),
)
)

View file

@ -29,6 +29,22 @@
const $ = (selector) => document.querySelector(selector);
const money = new Intl.NumberFormat("ru-RU", { style: "currency", currency: "RUB", maximumFractionDigits: 2 });
function bootstrapSummaryFromUrl() {
const nickname = query.get("nickname")?.trim();
const values = ["balance", "income", "expense"].map((key) => {
const raw = query.get(key);
return raw !== null && /^-?\d+$/.test(raw) ? Number(raw) : NaN;
});
if (!nickname || values.some((value) => !Number.isSafeInteger(value))) return null;
return {
nickname,
balance_cents: values[0],
income_cents: values[1],
expense_cents: values[2],
categories: [],
};
}
function safeTelegram(action) {
try { action(); } catch (error) { console.warn("Telegram WebApp method failed", error); }
}
@ -460,7 +476,14 @@
}
initTelegram();
setIdentity();
const bootstrapSummary = bootstrapSummaryFromUrl();
if (bootstrapSummary) {
state.summary = bootstrapSummary;
setIdentity(bootstrapSummary);
renderSummary(bootstrapSummary);
} else {
setIdentity();
}
bindNavigation();
setupNativeControls();
bindForm();

View file

@ -8,7 +8,7 @@
<title>Пожитки</title>
<link rel="stylesheet" href="./styles.css?v=16" />
<script src="./telegram-web-app.js"></script>
<script src="./app.js?v=15" defer></script>
<script src="./app.js?v=17" defer></script>
</head>
<body>
<main class="shell">

View file

@ -108,6 +108,21 @@ def test_personalized_keyboard_webapp_url(tmp_path: Path) -> None:
assert webapp_url_for_user(settings, 123456) == (
"https://example.com/app/?user_id=123456&mode=keyboard"
)
url = webapp_url_for_user(
settings,
123456,
{
"nickname": "Иван Петров",
"balance_cents": 2500,
"income_cents": 10000,
"expense_cents": 7500,
},
)
assert url == (
"https://example.com/app/?user_id=123456&mode=keyboard"
"&nickname=%D0%98%D0%B2%D0%B0%D0%BD+%D0%9F%D0%B5%D1%82%D1%80%D0%BE%D0%B2"
"&balance=2500&income=10000&expense=7500"
)
def test_custom_category_and_cancellation(tmp_path: Path) -> None: