desenvolvimento pre-lancamento
Commit inicial - add do repo privado para o repo NT style: changes header's logo and colors style: changes home page first session layout feat: creates about us home page section chore: creates home page section for whom chore: creates student materails home page section chore: creates teachers materials home page section chore: creates teacher materials home page section style: changes primary color style: changes color at activities page style: changes about page color style: changes name to Decoda fix: changes route to about page at footer fix: changes background color style: changes game page header colors style: changes footer colors chore: adds home page sections title style: changes main font family to Lato style: adds title font fix: changes sizes to be more responsive for mobile ajuste no build vercel atualiza regras envio homol Adiciona instrucoes de uso add JupyterLite fix solucao turtle Add Mole Mash e Modal de Falhas Add Progress Bar na pagina de Atividades fix game name chore: atualiza lockfile removendo vercel analytics inclusão de efeito ao mudar de fase add mecanismo de solução de fases em debug vite config test add BaseGame e refator do MoleMash refatoração turtle refatoração automato refatoração automato add tag bug 1 e 2 automato mostrar apenas games em homologação na pagina de atividades aumentar timeout das fases finais do Turtle fix bug scroll add video refactor semaforo arrumar ordem das cores add build docs update vercel update vercel update vercel update vercel update vercel add vercel jupyter add vercel jupyter fix deploy Vercel fix deploy Vercel fix deploy Vercel add cripto add cripto refatoração fix tour Mole Mash . remover arquivos de controle chore: adds development tag for activity card remover arquivos de status indevidamente versionados atualizar cores nas atividades add Quebra Cabeças add Quebra Cabeças add iniciativas add Iniciativas alteração de fotos pesadas fix menu mobile fix menu mobile fix menu mobile add Aspirador update icons update identidade visual documentação update jupyter add kernel python local add kernel python local add kernel python local feat: add health check feat: add primeiros passos add letramento mover letramento de lugar update path games update path games fix: ajuste clique rapido no botão executar remover dead code fix: refactor: extract shared utilities for storage, phase unlock and mobile detection stabilize context references and fix stale closure extrair GameProgressContext do GameStateContext (SRP) refactor(game): extrair usePhaser e useGameModals de GameBase + corrigir bugs descobertos refactor(game): remove todos os aliases PT/EN duplicados Remover aliases PT/EN da camada de modais refactor + tests security: add CodeSanitizer and integrate into GameInterpreter - CodeSanitizer.js: 4 built-in rules (max_length, infinite_while, infinite_for, excessive_nesting) with pluggable extra rules - GameInterpreter.executeCode: calls sanitizeCode() before js-interpreter, differentiates CodeSanitizationError (warn) from other errors (error) - 21 unit tests for CodeSanitizer (100% coverage) - 4 integration tests in GameInterpreter for sanitization paths add CodeSanitizer fix: fase 10 aspirador fix: bug semaforo teste feat: add version Ajusta a landing page para ficar mais próxima ao protótipo ajusta raio da borda do botão de Acesse nosso Laboratório pequenos ajustes de layout na página de iniciativas atualiza tabela de jogos educativos com os jogos disponíveis atualmente ajustados pequenos detalhes e informações do jogos na seção de guias pedagógicos troca nome playground para laboratório e adiciona imagens do lab adiciona documentação de conceitos básicos de programação ajustado pequenos erros de digitação adiciona tooltip com conceitos escondidos em hover na tag +N de conceitos update docs dev desativar tour setup matriz MoleMash setup matriz MoleMash fix: link update version update docs update docs mudou o layout de quem somos mudei as imgs dos icons e baixei o botao centraliza titulo com imagem e ajusta sessão com gradiente vermelho-rosa adiciona responsividade para a pagina quem somos ajusta botão de conheça nossa história ajustes ajustes na home + add. teclado update version security security feat: add tapume para telas pequenas v1.1.0 feat: decoda offline feat: doc offline offline fix: ajustes para release fix: navbar; config ordenação; versão fix: rotas docs e jupyter para pwa delete private files Co-authored-by: Indra Araujo <indra.araujo.santos@gmail.com> Co-authored-by: solange dos santos <sollangelive71@gmail.com>
This commit is contained in:
372
app/src/contexts/GameStateContext.jsx
Normal file
372
app/src/contexts/GameStateContext.jsx
Normal file
@@ -0,0 +1,372 @@
|
||||
/**
|
||||
* @fileoverview React component for GameStateContext.jsx
|
||||
*
|
||||
* @module contexts.GameStateContext
|
||||
*/
|
||||
|
||||
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
useCallback,
|
||||
useRef,
|
||||
} from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { gameEventBus } from "../utils/gameEvents";
|
||||
import { GameProgressProvider, useGameProgress } from "./GameProgressContext";
|
||||
|
||||
export const GAME_STATES = {
|
||||
PARADO: "parado",
|
||||
EXECUTANDO: "executando",
|
||||
SUCESSO: "sucesso",
|
||||
FALHA: "falha",
|
||||
};
|
||||
|
||||
const GameStateContext = createContext();
|
||||
|
||||
/**
|
||||
* Provedor de contexto global para o estado de execução do jogo.
|
||||
* Gerencia: código gerado, estado de execução, fases completadas, mensagens de erro.
|
||||
*
|
||||
* @component
|
||||
* @param {Object} props - Componente provider props
|
||||
* @param {React.ReactNode} props.children - Componentes filhos
|
||||
* @param {Object} props.gameConfig - Configuração do jogo (gameId, fases, etc)
|
||||
* @param {string} props.gameConfig.gameId - ID único do jogo
|
||||
* @param {Array} props.gameConfig.fases - Array de fases do jogo
|
||||
*
|
||||
* @returns {React.Context} BaseExecution context with executor methods and state
|
||||
*
|
||||
* @example
|
||||
* <GameStateProvider gameConfig={config}>
|
||||
* <GameArea />
|
||||
* </GameStateProvider>
|
||||
*
|
||||
* @context
|
||||
* - {@link useGameState} - Para consumir este contexto
|
||||
*/
|
||||
export function GameStateProvider({ children, gameConfig }) {
|
||||
return (
|
||||
<GameProgressProvider gameConfig={gameConfig}>
|
||||
<GameStateInnerProvider gameConfig={gameConfig}>
|
||||
{children}
|
||||
</GameStateInnerProvider>
|
||||
</GameProgressProvider>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider interno que consome GameProgressContext e gerencia estado de execução.
|
||||
* Expõe via GameStateContext todos os campos (progresso + execução) para
|
||||
* preservar compatibilidade com os consumidores existentes de useGameState().
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function GameStateInnerProvider({ children, gameConfig }) {
|
||||
const {
|
||||
currentPhase,
|
||||
setCurrentPhase,
|
||||
completedPhases,
|
||||
setCompletedPhases,
|
||||
changePhase: progressChangePhase,
|
||||
resetProgress: progressResetProgress,
|
||||
} = useGameProgress();
|
||||
|
||||
const [executionState, setExecutionState] = useState(GAME_STATES.PARADO);
|
||||
const [generatedCode, setGeneratedCode] = useState("");
|
||||
const [currentBlockCount, setCurrentBlockCount] = useState(0);
|
||||
const [onWorkspaceChangeCallback, setOnWorkspaceChangeCallback] =
|
||||
useState(null);
|
||||
const [editorType, setEditorType] = useState("blockly"); // 'blockly' ou 'code'
|
||||
const [codeEditorContent, setCodeEditorContent] = useState("");
|
||||
const [failureMessage, setFailureMessage] = useState("");
|
||||
const [isDebugMode, setIsDebugMode] = useState(() => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const debugKey = Array.from(urlParams.keys()).find(
|
||||
(k) => k.toLowerCase() === "debug",
|
||||
);
|
||||
return urlParams.get(debugKey)?.toLowerCase() === "true";
|
||||
});
|
||||
|
||||
const getCodeFromWorkspace = useRef(null);
|
||||
const getCodeFromEditor = useRef(null);
|
||||
|
||||
/**
|
||||
* Executa o código/workspace do editor de forma síncrona.
|
||||
* Registra o código gerado e muda estado para EXECUTANDO.
|
||||
* Valida se o editor registrou a função de execução antes de processar.
|
||||
*
|
||||
* @function execute
|
||||
* @returns {void}
|
||||
* @throws {console.error} Se editor não registrou a função de execução
|
||||
*/
|
||||
const execute = () => {
|
||||
if (editorType === "code") {
|
||||
if (getCodeFromEditor.current) {
|
||||
const codigo = getCodeFromEditor.current();
|
||||
|
||||
if (codigo && codigo.trim()) {
|
||||
setGeneratedCode(codigo);
|
||||
setExecutionState(GAME_STATES.EXECUTANDO);
|
||||
} else {
|
||||
console.error(
|
||||
"CodeEditor ainda não registrou sua função de execução.",
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (getCodeFromWorkspace.current) {
|
||||
const { codigo, workspace } = getCodeFromWorkspace.current();
|
||||
if (codigo && workspace) {
|
||||
setGeneratedCode({ codigo, workspace });
|
||||
setExecutionState(GAME_STATES.EXECUTANDO);
|
||||
}
|
||||
} else {
|
||||
console.error(
|
||||
"BlocklyEditor ainda não registrou sua função de execução.",
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Marca a execução como bem-sucedida e registra a fase como completada.
|
||||
* Adiciona a fase ao array de fases concluídas se ainda não estiver incluída.
|
||||
* Persiste as mudanças no localStorage.
|
||||
*
|
||||
* @function finalizeWithSuccess
|
||||
* @returns {void}
|
||||
*/
|
||||
const finalizeWithSuccess = () => {
|
||||
setExecutionState(GAME_STATES.SUCESSO);
|
||||
|
||||
if (!completedPhases.includes(currentPhase)) {
|
||||
setCompletedPhases([...completedPhases, currentPhase]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Marca a execução como falhada.
|
||||
* Atualiza o estado para FALHA sem limpar o código gerado.
|
||||
* Permite que o jogador veja o código e os erros para corrigir.
|
||||
*
|
||||
* @function finalizeWithFailure
|
||||
* @returns {void}
|
||||
*/
|
||||
const finalizeWithFailure = () => {
|
||||
setExecutionState(GAME_STATES.FALHA);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reinicia o estado de execução e limpa o código gerado.
|
||||
* Usado para permitir nova execução após sucesso/falha sem mudar de fase.
|
||||
* Mantém a fase atual e fases completadas intactas.
|
||||
*
|
||||
* @function restart
|
||||
* @returns {void}
|
||||
*/
|
||||
const restart = () => {
|
||||
setExecutionState(GAME_STATES.PARADO);
|
||||
setGeneratedCode("");
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove todo o progresso salvo e reseta fases para a primeira.
|
||||
* Persiste a remoção no `localStorage` usando a chave do jogo.
|
||||
*
|
||||
* @function resetProgress
|
||||
* @returns {void}
|
||||
*/
|
||||
const resetProgress = () => {
|
||||
progressResetProgress();
|
||||
};
|
||||
|
||||
/**
|
||||
* Navega para uma fase específica do jogo.
|
||||
* Reseta o estado de execução, limpa o código gerado e conta de blocos.
|
||||
* Usado quando o jogador seleciona uma nova fase no seletor.
|
||||
*
|
||||
* @function changePhase
|
||||
* @param {number} numeroFase - Número da fase para navegar (1-indexed)
|
||||
* @returns {void}
|
||||
*/
|
||||
const changePhase = (numeroFase) => {
|
||||
progressChangePhase(numeroFase);
|
||||
setExecutionState(GAME_STATES.PARADO);
|
||||
setGeneratedCode("");
|
||||
setCurrentBlockCount(0);
|
||||
setCodeEditorContent("");
|
||||
};
|
||||
|
||||
/**
|
||||
* Para a execução atual e limpa o código gerado sem alterar a fase.
|
||||
* Usado para interromper execuções em andamento pelo usuário ou pela UI.
|
||||
*
|
||||
* @function stop
|
||||
* @returns {void}
|
||||
*/
|
||||
const stop = () => {
|
||||
// Avisar Phaser para parar antes de mudar estado React
|
||||
gameEventBus.stopExecution();
|
||||
setExecutionState(GAME_STATES.PARADO);
|
||||
setGeneratedCode("");
|
||||
};
|
||||
|
||||
/**
|
||||
* Registra a função que extrai código/workspace do editor Blockly.
|
||||
* A função registrada deve retornar um objeto `{ codigo, workspace }`.
|
||||
*
|
||||
* @function registerExecutionFunction
|
||||
* @param {Function} func - Função que retorna { codigo, workspace }
|
||||
* @returns {void}
|
||||
*/
|
||||
const registerExecutionFunction = useCallback((func) => {
|
||||
getCodeFromWorkspace.current = func;
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Registra a função que retorna o código do editor de texto (code editor).
|
||||
* Usado quando `editorType` é `code` para obter o código atual.
|
||||
*
|
||||
* @function registerCodeEditorFunction
|
||||
* @param {Function} func - Função que retorna uma string com o código
|
||||
* @returns {void}
|
||||
*/
|
||||
const registerCodeEditorFunction = useCallback((func) => {
|
||||
getCodeFromEditor.current = func;
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Callback disparado quando a workspace do Blockly sofre alterações.
|
||||
* Atualiza contador de blocos e repassa para callback externo se fornecido.
|
||||
*
|
||||
* @function onWorkspaceChange
|
||||
* @param {number} blockCount - Quantidade atual de blocos na workspace
|
||||
* @returns {void}
|
||||
*/
|
||||
const onWorkspaceChange = useCallback(
|
||||
(blockCount) => {
|
||||
setCurrentBlockCount(blockCount);
|
||||
if (onWorkspaceChangeCallback) {
|
||||
onWorkspaceChangeCallback(blockCount);
|
||||
}
|
||||
},
|
||||
[onWorkspaceChangeCallback],
|
||||
);
|
||||
|
||||
/**
|
||||
* Callback para mudanças no editor de código (texto).
|
||||
* Atualiza o conteúdo e ajusta o contador de blocos (1 se houver código, 0 caso contrário).
|
||||
*
|
||||
* @function onCodeEditorChange
|
||||
* @param {string} content - Conteúdo atual do editor de código
|
||||
* @returns {void}
|
||||
*/
|
||||
const onCodeEditorChange = useCallback((content) => {
|
||||
setCodeEditorContent(content);
|
||||
setCurrentBlockCount(content.trim() ? 1 : 0);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (editorType === "code" && getCodeFromEditor.current) {
|
||||
setCurrentBlockCount(
|
||||
codeEditorContent && codeEditorContent.trim() ? 1 : 0,
|
||||
);
|
||||
} else {
|
||||
setCodeEditorContent(0);
|
||||
}
|
||||
}, [editorType, codeEditorContent]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<GameStateContext.Provider
|
||||
value={{
|
||||
// English API
|
||||
executionState,
|
||||
setExecutionState,
|
||||
generatedCode,
|
||||
setGeneratedCode,
|
||||
currentBlockCount,
|
||||
execute,
|
||||
finalizeWithSuccess,
|
||||
finalizeWithFailure,
|
||||
restart,
|
||||
stop,
|
||||
currentPhase,
|
||||
// public: change phase using `changePhase` behavior
|
||||
setCurrentPhase: changePhase,
|
||||
completedPhases,
|
||||
setCompletedPhases,
|
||||
resetProgress,
|
||||
gameConfig,
|
||||
registerExecutionFunction,
|
||||
registerCodeEditorFunction,
|
||||
onWorkspaceChange,
|
||||
onCodeEditorChange,
|
||||
// Wrapping fn in arrow prevents React from treating it as a state updater.
|
||||
// useState(fn) calls fn(prevState) — storing a callback directly would cause
|
||||
// handleWorkspaceChange(prevCallback) to fire during render (illegal side-effect).
|
||||
setOnWorkspaceChange: (fn) => setOnWorkspaceChangeCallback(() => fn),
|
||||
editorType,
|
||||
setEditorType,
|
||||
codeEditorContent,
|
||||
failureMessage,
|
||||
setFailureMessage,
|
||||
isDebugMode,
|
||||
setIsDebugMode,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</GameStateContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook customizado para consumir o contexto global de estado do jogo.
|
||||
* Fornece acesso aos estados de execução, progresso e métodos de controle do jogo.
|
||||
*
|
||||
* @hook
|
||||
* @returns {Object} Objeto com estados e métodos do jogo:
|
||||
* - Execução: executionState, execute(), finalizeWithSuccess(), finalizeWithFailure(), restart(), stop()
|
||||
* - Progresso: currentPhase, changePhase(fase), completedPhases, resetProgress()
|
||||
* - Código: generatedCode, codeEditorContent
|
||||
* - Registro: registerExecutionFunction(), registerCodeEditorFunction()
|
||||
* - Callbacks: onWorkspaceChange(), onCodeEditorChange()
|
||||
* - Mensagens: failureMessage, setFailureMessage()
|
||||
* - Debug: isDebugMode, setIsDebugMode()
|
||||
* - Compatibilidade: aliases em português (estadoExecucao, codigoGerado, etc)
|
||||
*
|
||||
* @throws {Error} Se usado fora de GameStateProvider
|
||||
*
|
||||
* @example
|
||||
* function MyComponent() {
|
||||
* const { executionState, execute, currentPhase } = useGameState();
|
||||
* return <button onClick={execute}>Executar</button>;
|
||||
* }
|
||||
*/
|
||||
export function useGameState() {
|
||||
const context = useContext(GameStateContext);
|
||||
if (!context) {
|
||||
throw new Error("useGameState deve ser usado dentro de GameStateProvider");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
GameStateProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
gameConfig: PropTypes.shape({
|
||||
gameId: PropTypes.string.isRequired,
|
||||
fases: PropTypes.array.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
GameStateInnerProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
gameConfig: PropTypes.shape({
|
||||
gameId: PropTypes.string.isRequired,
|
||||
fases: PropTypes.array.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
Reference in New Issue
Block a user