@code-by-dwayne/react-scorm-provider
Version:
Modern React Components to easily enable SCORM API communication in React projects. Updated fork of the original react-scorm-provider.
1 lines • 16 kB
Source Map (JSON)
{"version":3,"sources":["../src/lib/ScormProvider.tsx","../src/lib/withScorm.tsx"],"names":["React","createContext","useEffect","useState","useCallback","SCORM","debug","ScoContext","formatSessionTime","__name","milliseconds","totalSeconds","hours","minutes","seconds","ScormProvider","children","version","isDebug","apiConnected","setApiConnected","learnerName","setLearnerName","learnerID","setLearnerID","completionStatus","setCompletionStatus","suspendData","setSuspendDataState","scormVersion","setScormVersion","createScormAPIConnection","closeScormAPIConnection","learner","status","setSuspendData","commitData","exitMode","success","e","error","getSuspendData","data","parsedData","locationData","score","sessionTime","commitSuccess","savedData","clearSuspendData","setStatus","setScore","saveResult","set","key","value","get","useScorm","context","ScormProvider_default","withScorm","WrappedComponent","props","withScorm_default"],"mappings":"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","sourcesContent":["import React, { createContext, useEffect, useState, useCallback, ReactNode } from \"react\";\nimport { SCORM, debug } from \"pipwerks-scorm-api-wrapper\";\nimport { Score } from \".\";\nimport { IScormContextProps } from \"@/@types/scorm-context-props\";\n\nexport const ScoContext = createContext<IScormContextProps | undefined>(undefined);\n\ninterface ScormProviderProps {\n children: ReactNode;\n version?: \"1.2\" | \"2004\";\n debug?: boolean;\n}\n\nconst formatSessionTime = (milliseconds: number): string => {\n const totalSeconds = Math.floor(milliseconds / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n\n return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.00`;\n};\n\nconst ScormProvider: React.FC<ScormProviderProps> = ({ children, version, debug: isDebug }) => {\n const [apiConnected, setApiConnected] = useState(false);\n const [learnerName, setLearnerName] = useState(\"unknown\");\n const [learnerID, setLearnerID] = useState(\"unknown\");\n const [completionStatus, setCompletionStatus] = useState(\"unknown\");\n const [suspendData, setSuspendDataState] = useState<Record<string, any>>({});\n const [scormVersion, setScormVersion] = useState<string>(\"\");\n\n useEffect(() => {\n createScormAPIConnection();\n window.addEventListener(\"beforeunload\", closeScormAPIConnection);\n return () => {\n closeScormAPIConnection();\n window.removeEventListener(\"beforeunload\", closeScormAPIConnection);\n };\n }, []);\n\n const createScormAPIConnection = useCallback(() => {\n if (apiConnected) return;\n\n if (version) SCORM.version = version;\n if (typeof isDebug === \"boolean\") debug.isActive = isDebug;\n\n if (SCORM.init()) {\n const version = SCORM.version;\n const learner = version === \"1.2\" ? SCORM.get(\"cmi.core.student_name\") : SCORM.get(\"cmi.learner_name\");\n const learnerID = version === \"1.2\" ? SCORM.get(\"cmi.core.student_id\") : SCORM.get(\"cmi.learner_id\");\n const status = SCORM.status(\"get\");\n\n setApiConnected(true);\n setLearnerName(learner || \"unknown\");\n setLearnerID(learnerID || \"unknown\");\n setCompletionStatus(status || \"unknown\");\n setScormVersion(version);\n // getSuspendData();\n } else {\n console.error(\"ScormProvider init error: could not create the SCORM API connection\");\n }\n }, [apiConnected, version, isDebug]);\n\n const closeScormAPIConnection = useCallback(() => {\n if (!apiConnected) return;\n\n setSuspendData();\n SCORM.status(\"set\", completionStatus);\n SCORM.save();\n\n if (SCORM.quit()) {\n setApiConnected(false);\n setLearnerName(\"unknown\");\n setCompletionStatus(\"unknown\");\n setSuspendDataState({});\n setScormVersion(\"\");\n } else {\n console.error(\"ScormProvider error: could not close the API connection\");\n }\n }, [apiConnected, completionStatus]);\n\n /**\n * @description Saves all current student progress to the LMS without ending the session (LMSCommit)\n * @example\n * commitData();\n * @returns {boolean} A promise that resolves with the result of the save operation\n * @throws {Error} If the SCORM API is not connected\n */\n const commitData = useCallback((): boolean => {\n if (!apiConnected) {\n throw new Error(\"SCORM API não conectada\");\n }\n\n try {\n // 1. Garantir que dados críticos estejam definidos\n const exitMode = SCORM.get(\"cmi.core.exit\");\n if (!exitMode || exitMode !== \"suspend\") {\n SCORM.set(\"cmi.core.exit\", \"suspend\");\n }\n\n // 2. Tentar commit usando diferentes métodos\n let success = false;\n\n // Método 1: Função direta do provedor\n if (typeof SCORM.save === \"function\") {\n success = SCORM.save() !== \"false\";\n }\n\n // Método 2: LMSCommit direto (mais confiável em alguns LMS)\n if (!success && SCORM.version === \"1.2\") {\n try {\n // @ts-ignore - Acesso direto à API SCORM\n success = SCORM.API.LMSCommit(\"\") !== \"false\";\n } catch (e) {\n console.warn(\"Erro ao tentar LMSCommit direto:\", e);\n }\n }\n\n // 3. Verificar se o commit funcionou\n const suspendData = SCORM.get(\"cmi.suspend_data\");\n if (!suspendData || suspendData === \"{}\") {\n console.warn(\"Commit não persistiu dados em suspend_data\");\n }\n\n return success;\n } catch (error) {\n console.error(\"Erro no commit:\", error);\n return false;\n }\n }, [apiConnected]);\n\n const getSuspendData = useCallback(() => {\n if (!apiConnected) {\n throw new Error(\"SCORM API não conectada\");\n }\n\n try {\n const data = SCORM.get(\"cmi.suspend_data\");\n let parsedData = {};\n\n if (data && data !== \"{}\") {\n try {\n parsedData = JSON.parse(data);\n console.log(\"Dados recuperados de suspend_data com sucesso\");\n } catch (e) {\n console.warn(\"Erro ao processar dados de suspend_data:\", e);\n }\n } else {\n console.warn(\"Nenhum dado encontrado em suspend_data\");\n\n const locationData = SCORM.get(\"cmi.core.lesson_location\");\n if (locationData && locationData.includes(\"activityId=\")) {\n console.log(\"Encontrado identificador em lesson_location:\", locationData);\n }\n }\n\n const status = SCORM.get(\"cmi.core.lesson_status\");\n if (status === \"completed\" || status === \"passed\") {\n console.log(\"Atividade já marcada como concluída no LMS\");\n\n const score = SCORM.get(\"cmi.core.score.raw\");\n if (score) {\n console.log(\"Pontuação recuperada:\", score);\n }\n }\n\n setSuspendDataState(parsedData);\n return true;\n } catch (error) {\n console.error(\"Erro ao carregar suspend_data:\", error);\n return false;\n }\n }, [apiConnected]);\n\n const setSuspendData = useCallback(() => {\n if (!apiConnected) {\n console.warn(\"SCORM API não conectada, impossível salvar dados\");\n return false;\n }\n\n try {\n SCORM.set(\"cmi.suspend_data\", JSON.stringify(suspendData));\n\n const locationData = `activityId=${Object.keys(suspendData).join(',')}&ts=${Date.now()}`;\n SCORM.set(\"cmi.core.lesson_location\", locationData);\n\n SCORM.set(\"cmi.core.exit\", \"suspend\");\n\n const sessionTime = formatSessionTime(30 * 60 * 1000);\n SCORM.set(\"cmi.core.session_time\", sessionTime);\n\n const status = SCORM.get(\"cmi.core.lesson_status\");\n if (status !== \"completed\" && status !== \"passed\") {\n SCORM.set(\"cmi.core.lesson_status\", \"incomplete\");\n }\n\n const commitSuccess = commitData();\n\n const savedData = SCORM.get(\"cmi.suspend_data\");\n if (!savedData || savedData === \"{}\") {\n console.warn(\"Dados não foram persistidos corretamente no suspend_data\");\n return false;\n }\n\n return commitSuccess;\n } catch (error) {\n console.error(\"Erro ao salvar suspend_data:\", error);\n return false;\n }\n }, [apiConnected, suspendData, commitData]);\n\n const clearSuspendData = useCallback(() => setSuspendDataState({}), []);\n\n const setStatus = useCallback((status: string) => {\n if (!apiConnected) return;\n setCompletionStatus(status);\n SCORM.status(\"set\", status);\n }, [apiConnected]);\n\n const setScore = useCallback(async (score: Score): Promise<any> => {\n if (!apiConnected) {\n return Promise.reject(new Error(\"SCORM API not connected\"));\n }\n\n try {\n SCORM.set(\"cmi.score.raw\", score.value);\n\n if (scormVersion === \"2004\") {\n SCORM.set(\"cmi.score.min\", score.min);\n SCORM.set(\"cmi.score.max\", score.max);\n } else if (scormVersion === \"1.2\") {\n SCORM.set(\"cmi.core.score.min\", score.min);\n SCORM.set(\"cmi.core.score.max\", score.max);\n }\n\n if (score.status && score.status !== \"0\") {\n setStatus(score.status);\n }\n\n const saveResult = SCORM.save();\n\n return Promise.resolve({\n success: true,\n saveResult,\n score\n });\n } catch (error) {\n return Promise.reject(error);\n }\n }, [apiConnected, scormVersion, setStatus]);\n\n const set = useCallback((key: string, value: any) => {\n if (!apiConnected) return;\n SCORM.set(key, value);\n }, [apiConnected]);\n\n const get = useCallback((key: string) => {\n if (!apiConnected) return null;\n return SCORM.get(key);\n }, [apiConnected]);\n\n return (\n <ScoContext.Provider\n value={{\n apiConnected,\n learnerName,\n learnerID,\n completionStatus,\n suspendData,\n scormVersion,\n getSuspendData,\n\t\t\t\tsetSuspendData,\n clearSuspendData,\n setStatus,\n setScore,\n set,\n get,\n commitData,\n }}\n >\n {children}\n </ScoContext.Provider>\n );\n};\n\nexport const useScorm = (): IScormContextProps => {\n const context = React.useContext(ScoContext);\n if (!context) {\n throw new Error(\"useScorm must be used within a ScormProvider\");\n }\n return context;\n};\n\nexport default ScormProvider;\n","import React from 'react';\nimport { ScoContext } from './ScormProvider';\n\nfunction withScorm() {\n\n return function(WrappedComponent) {\n\n const WithScorm = function(props) {\n return (\n <ScoContext.Consumer>\n {value => <WrappedComponent {...props} sco={value} />}\n </ScoContext.Consumer>\n )\n }\n\n return WithScorm;\n }\n}\n\nexport default withScorm;\n"]}