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:
parent
0562cb4e47
commit
268fb62bf3
3 changed files with 54 additions and 5 deletions
|
|
@ -229,11 +229,18 @@ async def signal(
|
||||||
if geo
|
if geo
|
||||||
else "Гео нету"
|
else "Гео нету"
|
||||||
)
|
)
|
||||||
text = (
|
if body.is_test:
|
||||||
f"🚨 Сигнал от {user_name}\n"
|
text = (
|
||||||
f"⏰ {ts.strftime('%H:%M:%S')} UTC\n"
|
f"🧪 Тест от {user_name}\n"
|
||||||
f"{geo_info}"
|
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"
|
||||||
|
f"{geo_info}"
|
||||||
|
)
|
||||||
asyncio.create_task(telegram.send_message(text))
|
asyncio.create_task(telegram.send_message(text))
|
||||||
|
|
||||||
return SignalResponse(status="ok", signal_id=signal_id)
|
return SignalResponse(status="ok", signal_id=signal_id)
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ class SignalRequest(BaseModel):
|
||||||
user_id: Optional[str] = None # UUID for legacy api_key auth; omit for JWT auth
|
user_id: Optional[str] = None # UUID for legacy api_key auth; omit for JWT auth
|
||||||
timestamp: int = Field(..., gt=0)
|
timestamp: int = Field(..., gt=0)
|
||||||
geo: Optional[GeoData] = None
|
geo: Optional[GeoData] = None
|
||||||
|
is_test: bool = False
|
||||||
|
|
||||||
|
|
||||||
class SignalResponse(BaseModel):
|
class SignalResponse(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -203,6 +203,33 @@ function _setSosState(state) {
|
||||||
btn.disabled = state === 'sending';
|
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() {
|
async function _handleSignal() {
|
||||||
if (!navigator.onLine) {
|
if (!navigator.onLine) {
|
||||||
_setStatus('Нет соединения. Проверьте сеть и попробуйте снова.', 'error');
|
_setStatus('Нет соединения. Проверьте сеть и попробуйте снова.', 'error');
|
||||||
|
|
@ -297,6 +324,20 @@ function _showMain() {
|
||||||
btn.addEventListener('click', _handleSignal);
|
btn.addEventListener('click', _handleSignal);
|
||||||
btn.dataset.listenerAttached = '1';
|
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 ==========
|
// ========== Service Worker ==========
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue