studio/src/auth/SanctionsContext.jsx
МИН 61fba4e174
Some checks failed
CI / Lint + Format (push) Failing after 32s
CI / Build (push) Failing after 37s
CI / Secret scan (push) Failing after 37s
CI / PR size check (push) Has been skipped
fix: починка билда + studio.rublox.pro инфра
Большой консолидирующий коммит после поднятия studio.rublox.pro (28 мая 2026).
Содержит изменения которые делались в процессе подготовки прод-окружения:

Фиксы импортов после выноса из minecraftia:
- Массовая замена путей ../../components → ../components (40+ файлов в src/community/, src/admin-preview/)
- Замена ../KubikonEditor/ → ../editor/, ../KubikonStudio/ → ../community/, ../AdminPreview/ → ../admin-preview/
- API.js скопирован из минки целиком (было 8 экспортов, стало 312)
- Добавлены PLAYER_URL, MyButton_1, недостающие компоненты
- Заменены require() на статические ES-imports в BabylonScene, PrimitiveManager, GameRuntime (Vite не поддерживает CJS require)

Структура ассетов:
- public/kubikon-templates/ → public/assets/kubikon-templates/
- public/kubikon-learn/ → public/assets/kubikon-learn/
- (код искал в /assets/, файлы лежали без /assets/)

Навигация роутов внутри студии:
- /kubikon-studio/docs → /docs (90+ навигационных вызовов sed-replaced)
- /kubikon-editor/X → /edit/X, /kubikon/play/X → /play/X, /kubikon/gd/X → /gd/X

UI:
- Новый компонент StudioHeader (61px, как в минке) + копия favicon
- WithHeader wrapper в App.jsx для всех страниц кроме fullscreen-редактора/плеера
- SSO ticket-flow в AuthContext (auto-redeem #ticket= при загрузке)
- Тёмная тема карточек игр в ВИКИ (фон #1c2231 вместо #fff, картинка впритык)

Документация:
- docs/ONBOARDING.md — путь нового контрибьютора от нуля до PR
- docs/TUTORIAL_ADD_SCRIPT_API.md — как добавить game.* API
- API_USAGE.md — список эндпоинтов backend
- README в подпапках engine/, engine/terrain/, engine/voxel/, engine/robloxterrain/, engine/types/

.gitignore:
- public/wiki/ исключён (73МБ PNG, будут на CDN отдельной задачей)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 05:01:13 +03:00

85 lines
3.3 KiB
JavaScript

/**
* SanctionsContext — глобальный контекст активных санкций юзера.
*
* Дёргает /api-storys/api/v1/storys/me/sanctions ОДИН раз при монтировании
* и при изменении localStorage Authorization (логин/логаут). Возвращает:
*
* sanctions — массив { type, banned_until, reason, ... }
* isMuted — true если есть активный user_timed_ban или chat_ban
* или comment_ban (то что блокирует ввод текста)
* isCantPublish — true если есть user_timed_ban или publish_ban
* muteInfo — { type, banned_until, reason } первой найденной мут-санкции
* reload() — перезагрузить (например после успешной подачи жалобы)
*
* Подключи провайдер один раз в корне приложения (Menu.jsx). Дальше
* через useSanctions() в любом компоненте, без отдельных запросов.
*/
import React, { createContext, useContext, useEffect, useState, useCallback } from 'react';
import SanctionsService from '../api/SanctionsService';
const SanctionsContext = createContext({
sanctions: [], isMuted: false, isCantPublish: false,
muteInfo: null, loading: true, reload: () => {},
});
export function SanctionsProvider({ children }) {
const [sanctions, setSanctions] = useState([]);
const [loading, setLoading] = useState(true);
const reload = useCallback(async () => {
// Без JWT не дёргаем (вернёт пустоту, но всё равно лишний запрос).
if (!localStorage.getItem('Authorization')) {
setSanctions([]);
setLoading(false);
return;
}
try {
const r = await SanctionsService.getMySanctions();
setSanctions(Array.isArray(r?.sanctions) ? r.sanctions : []);
} catch (_) {
setSanctions([]);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
reload();
}, [reload]);
// Перезагрузка при изменении токена (login/logout)
useEffect(() => {
const onStorage = (e) => {
if (e.key === 'Authorization') reload();
};
window.addEventListener('storage', onStorage);
return () => window.removeEventListener('storage', onStorage);
}, [reload]);
// Мут на текст: user_timed_ban или chat_ban или comment_ban
const muteInfo = sanctions.find((s) =>
['user_timed_ban', 'chat_ban', 'comment_ban'].includes(s.type)
) || null;
const isMuted = !!muteInfo;
// Запрет публикации: user_timed_ban или publish_ban
const cantPublishInfo = sanctions.find((s) =>
['user_timed_ban', 'publish_ban'].includes(s.type)
) || null;
const isCantPublish = !!cantPublishInfo;
return (
<SanctionsContext.Provider value={{
sanctions, isMuted, isCantPublish,
muteInfo, cantPublishInfo,
loading, reload,
}}>
{children}
</SanctionsContext.Provider>
);
}
export function useSanctions() {
return useContext(SanctionsContext);
}