Added language provider, fixed race condition on emulator
This commit is contained in:
@ -34,6 +34,10 @@
|
|||||||
<script type="text/javascript" language="javascript">
|
<script type="text/javascript" language="javascript">
|
||||||
let scriptElement = null;
|
let scriptElement = null;
|
||||||
|
|
||||||
|
let initInterval = setInterval(() => {
|
||||||
|
send({ message: 'iframe_loaded' });
|
||||||
|
}, 500);
|
||||||
|
|
||||||
EJS_player = "#game";
|
EJS_player = "#game";
|
||||||
EJS_pathtodata = "https://cdn.emulatorjs.org/stable/data/";
|
EJS_pathtodata = "https://cdn.emulatorjs.org/stable/data/";
|
||||||
EJS_language = "en-US";
|
EJS_language = "en-US";
|
||||||
@ -77,6 +81,8 @@
|
|||||||
scriptElement = document.createElement('script');
|
scriptElement = document.createElement('script');
|
||||||
scriptElement.src = 'https://cdn.emulatorjs.org/stable/data/loader.js';
|
scriptElement.src = 'https://cdn.emulatorjs.org/stable/data/loader.js';
|
||||||
document.body.appendChild(scriptElement);
|
document.body.appendChild(scriptElement);
|
||||||
|
|
||||||
|
clearInterval(initInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
const send = data => {
|
const send = data => {
|
||||||
@ -145,7 +151,7 @@
|
|||||||
|
|
||||||
EJS_onGameStart = () => {
|
EJS_onGameStart = () => {
|
||||||
send({ message: 'start' });
|
send({ message: 'start' });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import styles from './Emulator.module.scss';
|
import styles from './Emulator.module.scss';
|
||||||
import { GAME_SYSTEM_CORES, GameSystem, GameSystemCore } from '@/lib/game';
|
import { GAME_SYSTEM_CORES, GameSystem, GameSystemCore } from '@/lib/game';
|
||||||
|
import { useLanguage } from '@/providers/LanguageProvider';
|
||||||
|
|
||||||
type SendEmulatorMessageInit = {
|
type SendEmulatorMessageInit = {
|
||||||
message:'init';
|
message:'init';
|
||||||
@ -21,6 +22,7 @@ type SendEmulatorMessage = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
type ReceiveEmulatorMessage = (
|
type ReceiveEmulatorMessage = (
|
||||||
|
{ message: 'iframe_loaded' } |
|
||||||
{ message: 'start' } |
|
{ message: 'start' } |
|
||||||
{ message: 'ready' } |
|
{ message: 'ready' } |
|
||||||
{ message: 'save' } |
|
{ message: 'save' } |
|
||||||
@ -36,6 +38,7 @@ export type EmulatorProps = {
|
|||||||
export const Emulator:React.FC<EmulatorProps> = props => {
|
export const Emulator:React.FC<EmulatorProps> = props => {
|
||||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||||
const [ hasInit, setHasInit ] = useState(false);
|
const [ hasInit, setHasInit ] = useState(false);
|
||||||
|
const { t } = useLanguage();
|
||||||
|
|
||||||
const send = (msg:SendEmulatorMessage) => {
|
const send = (msg:SendEmulatorMessage) => {
|
||||||
iframeRef.current?.contentWindow?.postMessage(msg, '*');
|
iframeRef.current?.contentWindow?.postMessage(msg, '*');
|
||||||
@ -45,14 +48,6 @@ export const Emulator:React.FC<EmulatorProps> = props => {
|
|||||||
if(hasInit) return;
|
if(hasInit) return;
|
||||||
setHasInit(true);
|
setHasInit(true);
|
||||||
|
|
||||||
send({
|
|
||||||
message: 'init',
|
|
||||||
core: GAME_SYSTEM_CORES[props.system],
|
|
||||||
system: props.system,
|
|
||||||
gameId: props.gameId,
|
|
||||||
gameName: props.gameName
|
|
||||||
});
|
|
||||||
|
|
||||||
window.onmessage = e => {
|
window.onmessage = e => {
|
||||||
if(!e.data) {
|
if(!e.data) {
|
||||||
console.error('Invalid message received:', e.data);
|
console.error('Invalid message received:', e.data);
|
||||||
@ -61,6 +56,17 @@ export const Emulator:React.FC<EmulatorProps> = props => {
|
|||||||
|
|
||||||
const msg = e.data as ReceiveEmulatorMessage;
|
const msg = e.data as ReceiveEmulatorMessage;
|
||||||
switch(msg.message) {
|
switch(msg.message) {
|
||||||
|
case 'iframe_loaded':
|
||||||
|
if(hasInit) return;
|
||||||
|
send({
|
||||||
|
message: 'init',
|
||||||
|
core: GAME_SYSTEM_CORES[props.system],
|
||||||
|
system: props.system,
|
||||||
|
gameId: props.gameId,
|
||||||
|
gameName: props.gameName
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
case 'start':
|
case 'start':
|
||||||
case 'ready':
|
case 'ready':
|
||||||
case 'load':
|
case 'load':
|
||||||
@ -71,7 +77,17 @@ export const Emulator:React.FC<EmulatorProps> = props => {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [ iframeRef ]);
|
|
||||||
|
window.onbeforeunload = (e: BeforeUnloadEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
return t('emulator.exit_unsaved');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.onbeforeunload = null;
|
||||||
|
};
|
||||||
|
}, [ ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.emulator}>
|
<div className={styles.emulator}>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { GAME_SYSTEMS } from "@/lib/game";
|
|
||||||
import { ApolloError, gql } from "apollo-server-micro";
|
import { ApolloError, gql } from "apollo-server-micro";
|
||||||
import { pageTypeDefs } from "./page";
|
import { pageTypeDefs } from "./page";
|
||||||
|
import { GAME_SYSTEMS } from "@/lib/game";
|
||||||
|
|
||||||
export const gameTypeDefs = gql`
|
export const gameTypeDefs = gql`
|
||||||
${pageTypeDefs}
|
${pageTypeDefs}
|
||||||
|
@ -7,9 +7,12 @@ export const GAME_SYSTEM_CORES = <const>{
|
|||||||
'gba': 'mgba'
|
'gba': 'mgba'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type GameSystem = keyof typeof GAME_SYSTEM_CORES;
|
export type GameSystem = keyof typeof GAME_SYSTEM_CORES;
|
||||||
export type GameSystemCore = typeof GAME_SYSTEM_CORES[GameSystem];
|
export type GameSystemCore = typeof GAME_SYSTEM_CORES[GameSystem];
|
||||||
|
|
||||||
|
export const GAME_SYSTEMS = Object.keys(GAME_SYSTEM_CORES) as GameSystem[];
|
||||||
|
|
||||||
export type GameLight = {
|
export type GameLight = {
|
||||||
id:string;
|
id:string;
|
||||||
name:string;
|
name:string;
|
||||||
|
10
src/lib/language.ts
Normal file
10
src/lib/language.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export const LANGUAGE_ENGLISH = <const>{
|
||||||
|
'emulator.exit_unsaved': 'Your game may not be saved. Are you sure you want to exit?',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LANGUAGES = <const>{
|
||||||
|
'en': LANGUAGE_ENGLISH
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LanguageKey = keyof typeof LANGUAGE_ENGLISH;
|
||||||
|
export type Language = keyof typeof LANGUAGES;
|
@ -1,11 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { AppProps } from 'next/app';
|
import { AppProps } from 'next/app';
|
||||||
import './globals.scss';
|
import './globals.scss';
|
||||||
|
import { LanguageProvider } from '@/providers/LanguageProvider';
|
||||||
|
|
||||||
const RootLayout:React.FC<AppProps> = ({ Component, pageProps }) => {
|
const RootLayout:React.FC<AppProps> = ({ Component, pageProps }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Component {...pageProps} />
|
<LanguageProvider>
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</LanguageProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { LanguageProvider } from '@/providers/LanguageProvider';
|
||||||
import { Html, Head, Main, NextScript, DocumentProps } from 'next/document';
|
import { Html, Head, Main, NextScript, DocumentProps } from 'next/document';
|
||||||
|
|
||||||
const RootDocument:React.FC<DocumentProps> = props => {
|
const RootDocument:React.FC<DocumentProps> = props => {
|
||||||
|
47
src/providers/LanguageProvider.tsx
Normal file
47
src/providers/LanguageProvider.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Language, LanguageKey, LANGUAGES } from '@/lib/language';
|
||||||
|
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
||||||
|
|
||||||
|
type LanguageContextType = {
|
||||||
|
lang:Language;
|
||||||
|
t:(key:LanguageKey, p?:{ [key:string]:string }) => string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export const LanguageProvider: React.FC<{ children: ReactNode }> = ({
|
||||||
|
children
|
||||||
|
}) => {
|
||||||
|
const [ lang, setLang ] = useState<Language>('en');
|
||||||
|
|
||||||
|
const t:LanguageContextType['t'] = (key, p) => {
|
||||||
|
const str = LANGUAGES[lang][key];
|
||||||
|
|
||||||
|
if(!str) {
|
||||||
|
console.error(`Invalid language key: ${key}`);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!p) return str;
|
||||||
|
|
||||||
|
return Object.keys(p).reduce((acc, k, v) => {
|
||||||
|
return acc.replace(`{{${k}}}`, p[k]);
|
||||||
|
}, str);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LanguageContext.Provider value={{
|
||||||
|
lang,
|
||||||
|
t
|
||||||
|
}}>
|
||||||
|
{children}
|
||||||
|
</LanguageContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useLanguage = (): LanguageContextType => {
|
||||||
|
const context = useContext(LanguageContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useLanguage must be used within a LanguageProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
Reference in New Issue
Block a user