feat: test signal via avatar/indicator tap on main screen

Tapping user avatar or network indicator sends a test signal with
geo data. Backend formats it as "Тест от username" (🧪) instead of
"Сигнал" (🚨). Only active after login on main screen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Gros Frumos 2026-03-21 16:06:02 +02:00
parent 0562cb4e47
commit 268fb62bf3
3 changed files with 54 additions and 5 deletions

View file

@ -229,6 +229,13 @@ async def signal(
if geo
else "Гео нету"
)
if body.is_test:
text = (
f"🧪 Тест от {user_name}\n"
f"{ts.strftime('%H:%M:%S')} UTC\n"
f"{geo_info}"
)
else:
text = (
f"🚨 Сигнал от {user_name}\n"
f"{ts.strftime('%H:%M:%S')} UTC\n"

View file

@ -25,6 +25,7 @@ class SignalRequest(BaseModel):
user_id: Optional[str] = None # UUID for legacy api_key auth; omit for JWT auth
timestamp: int = Field(..., gt=0)
geo: Optional[GeoData] = None
is_test: bool = False
class SignalResponse(BaseModel):

View file

@ -203,6 +203,33 @@ function _setSosState(state) {
btn.disabled = state === 'sending';
}
async function _handleTestSignal() {
if (!navigator.onLine) {
_setStatus('Нет соединения.', 'error');
return;
}
const token = _getAuthToken();
if (!token) return;
_setStatus('', '');
try {
const geo = await _getGeo();
const body = { timestamp: Date.now(), is_test: true };
if (geo) body.geo = geo;
await _apiPost('/api/signal', body, { Authorization: 'Bearer ' + token });
_setStatus('Тест отправлен', 'success');
setTimeout(() => _setStatus('', ''), 1500);
} catch (err) {
if (err && err.status === 401) {
_clearAuth();
_setStatus('Сессия истекла. Войдите заново.', 'error');
setTimeout(() => _showOnboarding(), 1500);
} else {
_setStatus('Ошибка отправки.', 'error');
}
}
}
async function _handleSignal() {
if (!navigator.onLine) {
_setStatus('Нет соединения. Проверьте сеть и попробуйте снова.', 'error');
@ -297,6 +324,20 @@ function _showMain() {
btn.addEventListener('click', _handleSignal);
btn.dataset.listenerAttached = '1';
}
// Avatar and network indicator → test signal (only on main screen)
const avatar = document.getElementById('user-avatar');
if (avatar && !avatar.dataset.testAttached) {
avatar.addEventListener('click', _handleTestSignal);
avatar.dataset.testAttached = '1';
avatar.style.cursor = 'pointer';
}
const indicator = document.getElementById('indicator-network');
if (indicator && !indicator.dataset.testAttached) {
indicator.addEventListener('click', _handleTestSignal);
indicator.dataset.testAttached = '1';
indicator.style.cursor = 'pointer';
}
}
// ========== Service Worker ==========