feat(pwa): просмотр чужого экрана больше не выбрасывает из своего аккаунта

Сотрудник, вошедший водителем, при переключении на чужую карточку терял
собственный вход: токен один на приложение, и «посмотреть, как выглядит у
другого» означало заново входить через Telegram.

Теперь свой сеанс откладывается и возвращается кнопкой «Вернуться к своему
аккаунту». Откладывается ровно один раз — на переходе из обычного сеанса:
смена водителя внутри просмотра не должна подменять отложенное чужим токеном.

Заодно вход для сотрудника достижим изнутри приложения: длинный тап по названию
в шапке ведёт на /dev. Ссылкой на экране это не делаем — водителю она ни к чему,
а сотруднику иначе приходится набирать адрес руками.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 15:00:22 +10:00
co-authored by Claude Opus 5
parent ceec3db364
commit a0ada7de67
5 changed files with 116 additions and 8 deletions
@@ -1,7 +1,35 @@
import { useNavigate } from "react-router-dom";
import { useRef } from "react";
/** Долгое нажатие, после которого открывается вход для сотрудника. */
const LONG_PRESS_MS = 1500;
export function AppHeader() {
const nav = useNavigate();
const timer = useRef<number | null>(null);
// Ссылки «для сотрудника» на экранах водителя нет намеренно: водителю она
// ничего не даёт, а место занимает. Но сотруднику, уже вошедшему водителем,
// адрес /dev иначе приходится набирать руками — отсюда долгий тап по названию.
const start = () => {
timer.current = window.setTimeout(() => nav("/dev"), LONG_PRESS_MS);
};
const cancel = () => {
if (timer.current) window.clearTimeout(timer.current);
timer.current = null;
};
return (
<header className="mb-4">
<span className="text-sm font-bold">Премиум Водитель</span>
<span
className="text-sm font-bold select-none"
onPointerDown={start}
onPointerUp={cancel}
onPointerLeave={cancel}
onContextMenu={(e) => e.preventDefault()}
>
Премиум Водитель
</span>
</header>
);
}
@@ -11,7 +11,7 @@ import { Spinner } from "@/components/Spinner";
* первый вход сотрудника (/dev) и смена водителя на вкладке «Отладка».
*/
export function DriverPicker({ onPicked }: { onPicked: (d: DriverBrief) => void }) {
const setSession = useAuth((s) => s.setSession);
const startImpersonation = useAuth((s) => s.startImpersonation);
const [q, setQ] = useState("");
const [list, setList] = useState<DriverBrief[]>([]);
const [loading, setLoading] = useState(true);
@@ -35,7 +35,7 @@ export function DriverPicker({ onPicked }: { onPicked: (d: DriverBrief) => void
setBusyId(d.id);
try {
const r = await impersonate(d.id);
setSession(r.token, r.driver.id, { impersonated: true, driverName: r.driver.fio });
startImpersonation(r.token, r.driver.id, r.driver.fio);
onPicked(r.driver);
} catch (e: unknown) {
toast.error(await apiErrorText(e, "Не удалось открыть приложение за водителя"));