Тосты: убрать лишние, остальные показывать как уведомления сверху
Многие тосты повторяли то, что пользователь и так видит сразу после действия: новую фракцию или аватар в профиле, переход на страницу партии, исчезнувшую карточку приглашения, закрывшийся редактор. Такой шум приучает не читать всплывашки, в том числе ошибки. Убраны 19 таких тостов в профиле, группах, партиях и админке. Остались ошибки, предупреждения (лимит фото, повторы фракций, конфликт правок, «вы не в группе») и сообщения о действиях без видимого следа: приглашение отправлено, дополнения сохранены, вступление в группу, пароль задан. Сохранение текстового поля, где заметна только погасшая кнопка, тоже осталось. По просьбе владельца оставшиеся тосты показываются не нижней «таблеткой», а всплывашкой сверху, как входящие уведомления. ToastProvider держит одну стопку на всё приложение: show — обычное сообщение, error — ошибка с красной полосой (держится 5 с), notify — уведомление с переходом по клику. Одинаковое сообщение не копится, а заменяет прежнее; клик по карточке закрывает её. NotificationToaster больше не рисует свою стопку, а отдаёт новые уведомления в общую, поэтому они не наезжают на сообщения. Все вызовы с ApiError и отказы (лимит фото, партия изменилась на другом устройстве) переведены на toast.error. Нижний класс .toast удалён. #41 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
This commit is contained in:
@@ -65,9 +65,8 @@ export function AccountPage() {
|
||||
const saveFavorite = async (factionId: number | null) => {
|
||||
try {
|
||||
await updateFavorite.mutateAsync(factionId);
|
||||
toast.show(factionId == null ? "Любимая фракция убрана" : "Любимая фракция сохранена");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,7 +74,7 @@ export function AccountPage() {
|
||||
try {
|
||||
await updateHistory.mutateAsync(prefs);
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -85,9 +84,8 @@ export function AccountPage() {
|
||||
try {
|
||||
await updateNick.mutateAsync(nick);
|
||||
setNickname("");
|
||||
toast.show("Никнейм обновлён");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -96,7 +94,7 @@ export function AccountPage() {
|
||||
await updateBio.mutateAsync(bioValue.trim());
|
||||
toast.show("Сохранено");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -106,18 +104,16 @@ export function AccountPage() {
|
||||
if (!file) return;
|
||||
try {
|
||||
await uploadAvatar.mutateAsync(file);
|
||||
toast.show("Аватар обновлён");
|
||||
} catch (err) {
|
||||
toast.show(err instanceof ApiError ? err.message : "Не удалось загрузить");
|
||||
toast.error(err instanceof ApiError ? err.message : "Не удалось загрузить");
|
||||
}
|
||||
};
|
||||
|
||||
const removeAvatar = async () => {
|
||||
try {
|
||||
await deleteAvatar.mutateAsync();
|
||||
toast.show("Аватар удалён");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -307,9 +303,8 @@ function LoginSettingsCard({ me }: { me: Me }) {
|
||||
const onTelegram = async (payload: Record<string, unknown>) => {
|
||||
try {
|
||||
await linkTelegram.mutateAsync(payload);
|
||||
toast.show("Telegram привязан");
|
||||
} catch (err) {
|
||||
toast.show(err instanceof ApiError ? err.message : "Не удалось привязать Telegram");
|
||||
toast.error(err instanceof ApiError ? err.message : "Не удалось привязать Telegram");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ export function CreateMatchPage() {
|
||||
} catch (e) {
|
||||
if (reelTimers.current[id]) clearTimeout(reelTimers.current[id]);
|
||||
setSpin((s) => ({ ...s, [id]: null }));
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка рандома");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка рандома");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -176,7 +176,6 @@ export function CreateMatchPage() {
|
||||
was_random: r.was_random,
|
||||
})),
|
||||
});
|
||||
toast.show("Партия начата");
|
||||
navigate(`/match/${match.id}`, { replace: true });
|
||||
} catch (e) {
|
||||
setError(e instanceof ApiError ? e.message : "Не удалось начать партию");
|
||||
|
||||
@@ -53,7 +53,7 @@ export function GroupPage() {
|
||||
setNewNick("");
|
||||
toast.show("Приглашение отправлено");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ export function GroupSettingsPage() {
|
||||
setName(null);
|
||||
toast.show("Название сохранено");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -68,7 +68,7 @@ export function GroupSettingsPage() {
|
||||
await setExpansions.mutateAsync([...selected]);
|
||||
toast.show("Дополнения сохранены");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -76,7 +76,7 @@ export function GroupSettingsPage() {
|
||||
try {
|
||||
await removeMember.mutateAsync(userId);
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -165,10 +165,9 @@ export function MatchDetailPage() {
|
||||
expected_version: match.version,
|
||||
},
|
||||
});
|
||||
toast.show("Партия завершена");
|
||||
} catch (e) {
|
||||
if (isStale(e)) {
|
||||
toast.show("Партия изменилась на другом устройстве — обновлено");
|
||||
toast.error("Партия изменилась на другом устройстве — обновлено");
|
||||
refetch();
|
||||
return;
|
||||
}
|
||||
@@ -234,11 +233,10 @@ export function MatchDetailPage() {
|
||||
expected_version: match.version,
|
||||
},
|
||||
});
|
||||
toast.show("Партия обновлена");
|
||||
cancelEditing();
|
||||
} catch (e) {
|
||||
if (isStale(e)) {
|
||||
toast.show("Партия изменилась на другом устройстве — обновлено");
|
||||
toast.error("Партия изменилась на другом устройстве — обновлено");
|
||||
refetch();
|
||||
cancelEditing();
|
||||
return;
|
||||
@@ -257,11 +255,11 @@ export function MatchDetailPage() {
|
||||
navigate("/group", { replace: true });
|
||||
} catch (e) {
|
||||
if (isStale(e)) {
|
||||
toast.show("Партия изменилась на другом устройстве — обновлено");
|
||||
toast.error("Партия изменилась на другом устройстве — обновлено");
|
||||
refetch();
|
||||
return;
|
||||
}
|
||||
toast.show(e instanceof ApiError ? e.message : "Не удалось выполнить");
|
||||
toast.error(e instanceof ApiError ? e.message : "Не удалось выполнить");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ export function PublicProfilePage() {
|
||||
await invite.mutateAsync({ groupId, nickname: data.nickname });
|
||||
toast.show("Приглашение отправлено");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Не удалось пригласить");
|
||||
toast.error(e instanceof ApiError ? e.message : "Не удалось пригласить");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export function AdminAccountsPage() {
|
||||
setPasswordId(null);
|
||||
setNewPassword("");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Не удалось задать пароль");
|
||||
toast.error(e instanceof ApiError ? e.message : "Не удалось задать пароль");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -51,19 +51,17 @@ export function AdminAccountsPage() {
|
||||
if (editId == null || !name) return;
|
||||
try {
|
||||
await update.mutateAsync({ userId: editId, nickname: name });
|
||||
toast.show("Ник обновлён");
|
||||
setEditId(null);
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Не удалось переименовать");
|
||||
toast.error(e instanceof ApiError ? e.message : "Не удалось переименовать");
|
||||
}
|
||||
};
|
||||
|
||||
const toggleActive = async (id: number, isActive: boolean) => {
|
||||
try {
|
||||
await update.mutateAsync({ userId: id, is_active: !isActive });
|
||||
toast.show("Сохранено");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Не удалось сохранить");
|
||||
toast.error(e instanceof ApiError ? e.message : "Не удалось сохранить");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -35,9 +35,8 @@ export function AdminAchievementsPage() {
|
||||
setName("");
|
||||
setDescription("");
|
||||
setCondition("");
|
||||
toast.show("Ачивка создана");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -100,9 +99,8 @@ function AchievementCard({ ach }: { ach: AchievementRead }) {
|
||||
body: { name: name.trim(), description, condition },
|
||||
});
|
||||
setEditing(false);
|
||||
toast.show("Сохранено");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -112,18 +110,16 @@ function AchievementCard({ ach }: { ach: AchievementRead }) {
|
||||
if (!file) return;
|
||||
try {
|
||||
await uploadIcon.mutateAsync({ slug: ach.slug, file });
|
||||
toast.show("Иконка обновлена");
|
||||
} catch (err) {
|
||||
toast.show(err instanceof ApiError ? err.message : "Не удалось загрузить");
|
||||
toast.error(err instanceof ApiError ? err.message : "Не удалось загрузить");
|
||||
}
|
||||
};
|
||||
|
||||
const remove = async () => {
|
||||
try {
|
||||
await del.mutateAsync(ach.slug);
|
||||
toast.show("Удалено");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export function AdminFactionsPage() {
|
||||
await rename.mutateAsync({ factionId: id, name_ru: names[id] });
|
||||
toast.show("Название обновлено во всей системе");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Ошибка");
|
||||
toast.error(e instanceof ApiError ? e.message : "Ошибка");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import { MatchMedia } from "../../components/MatchMedia";
|
||||
import { Spinner } from "../../components/Spinner";
|
||||
import { Switch } from "../../components/Switch";
|
||||
import { WIN_REASONS, type WinReason } from "../../domain/winReasons";
|
||||
import { useToast } from "../../context/ToastContext";
|
||||
import {
|
||||
useAdminDeleteAttachment,
|
||||
useAdminFactions,
|
||||
@@ -33,7 +32,6 @@ export function AdminMatchEdit({ matchId, onClose }: { matchId: number; onClose:
|
||||
const update = useAdminUpdateMatch();
|
||||
const uploadAtt = useAdminUploadAttachment(matchId);
|
||||
const deleteAtt = useAdminDeleteAttachment(matchId);
|
||||
const toast = useToast();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [rows, setRows] = useState<Row[]>([]);
|
||||
@@ -83,7 +81,6 @@ export function AdminMatchEdit({ matchId, onClose }: { matchId: number; onClose:
|
||||
})),
|
||||
},
|
||||
});
|
||||
toast.show("Партия обновлена");
|
||||
onClose();
|
||||
} catch (e) {
|
||||
setError(e instanceof ApiError ? e.message : "Не удалось сохранить");
|
||||
|
||||
@@ -149,9 +149,8 @@ export function AdminRecordsPage() {
|
||||
setDeleteTarget(null);
|
||||
try {
|
||||
await del.mutateAsync(deleteTarget.id);
|
||||
toast.show("Удалено");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Не удалось удалить");
|
||||
toast.error(e instanceof ApiError ? e.message : "Не удалось удалить");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -57,9 +57,8 @@ export function DevDeleteAccountButton({
|
||||
setConfirmOpen(false);
|
||||
try {
|
||||
await del.mutateAsync();
|
||||
toast.show("Аккаунт удалён");
|
||||
} catch (e) {
|
||||
toast.show(e instanceof ApiError ? e.message : "Не удалось удалить");
|
||||
toast.error(e instanceof ApiError ? e.message : "Не удалось удалить");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user