Skip to content

Commit

Permalink
Kiosk: Support event-based navigation (#9700)
Browse files Browse the repository at this point in the history
* Kiosk: Support event-based navigation

* remove unused packages

* Add spacer to add game layout to keep it centered at wide resolutions

* Restore link border on scan qr page

* Shortened background transition time

* Better focus trapping. Fix tab nav on ScanQR page.

* pr feedback

* prettier
  • Loading branch information
eanders-ms committed Oct 24, 2023
1 parent 3747ea3 commit 17ec7dc
Show file tree
Hide file tree
Showing 61 changed files with 3,185 additions and 1,358 deletions.
24 changes: 18 additions & 6 deletions kiosk/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions kiosk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@types/node": "^16.11.33",
"bezier-easing": "^2.1.0",
"howler": "^2.2.3",
"html5-qrcode": "^2.2.6",
"nanoid": "^4.0.2",
Expand All @@ -12,6 +13,7 @@
"react-dom": "^18.1.0",
"react-scripts": "5.0.1",
"swiper": "^8.4.4",
"tslib": "^2.6.2",
"typescript": "^4.6.4"
},
"scripts": {
Expand Down
10 changes: 10 additions & 0 deletions kiosk/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import GameOver from "./Components/GameOver";
import PlayingGame from "./Components/PlayingGame";
import Footer from "./Components/Footer";
import Notifications from "./Components/Notifications";
import AppModal from "./Components/AppModal";
import { useLocationHash, usePromise } from "./Hooks";
import { launchGame } from "./Transforms/launchGame";
import { navigate } from "./Transforms/navigate";
Expand All @@ -20,6 +21,10 @@ import * as Actions from "./State/Actions";
import * as SimHost from "./Services/SimHostService";
import * as NotificationService from "./Services/NotificationService";
import * as AddingGames from "./Services/AddingGamesService";
import * as GamepadManager from "./Services/GamepadManager";
import * as NavGrid from "./Services/NavGrid";
import * as RectCache from "./Services/RectCache";
import Background from "./Components/Background";

function App() {
const { state, dispatch } = useContext(AppStateContext);
Expand Down Expand Up @@ -47,6 +52,9 @@ function App() {
SimHost.initialize();
NotificationService.initialize();
AddingGames.initialize();
GamepadManager.initialize();
NavGrid.initialize();
RectCache.initialize();
}
}, [ready]);

Expand Down Expand Up @@ -80,6 +88,8 @@ function App() {
{kioskState === KioskState.GameOver && <GameOver />}
{kioskState === KioskState.PlayingGame && <PlayingGame />}
<Notifications />
<AppModal />
<Background />
</>
) : (
<></>
Expand Down
21 changes: 0 additions & 21 deletions kiosk/src/Components/AddGameButton.tsx

This file was deleted.

59 changes: 21 additions & 38 deletions kiosk/src/Components/AddingGame.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import { useEffect, useState, useContext, useMemo } from "react";
import configData from "../config.json";
import "../Kiosk.css";
import AddGameButton from "./AddGameButton";
import GenericButton from "./GenericButton";
import { QRCodeSVG } from "qrcode.react";
import { playSoundEffect } from "../Services/SoundEffectService";
import { AppStateContext } from "../State/AppStateContext";
import { gamepadManager } from "../Services/GamepadManager";
import { showMainMenu } from "../Transforms/showMainMenu";
import { generateKioskCodeAsync } from "../Services/AddingGamesService";
import { useOnControlPress } from "../Hooks";
import * as GamepadManager from "../Services/GamepadManager";

interface IProps {}

const AddingGame: React.FC<IProps> = ({}) => {
const { state: kiosk } = useContext(AppStateContext);
const [menuButtonSelected, setMenuButtonState] = useState(true);
const [generatingKioskCode, setGeneratingKioskCode] = useState(false);

const kioskTimeOutInMinutes = useMemo(() => {
Expand All @@ -30,31 +27,18 @@ const AddingGame: React.FC<IProps> = ({}) => {
return true;
};

// Input loop
useEffect(() => {
const updateLoop = () => {
if (!menuButtonSelected && gamepadManager.isDownPressed()) {
setMenuButtonState(true);
playSoundEffect("switch");
}
if (
(menuButtonSelected && gamepadManager.isAButtonPressed()) ||
gamepadManager.isBButtonPressed()
) {
pxt.tickEvent("kiosk.toMainMenu");
showMainMenu();
playSoundEffect("select");
}
};

updateLoop();
const intervalId = setInterval(
updateLoop,
configData.GamepadPollLoopMilli
);
const gotoMainMenu = () => {
pxt.tickEvent("kiosk.toMainMenu");
showMainMenu();
};

return () => clearInterval(intervalId);
}, [menuButtonSelected]);
// Handle Escape button press
useOnControlPress(
[],
gotoMainMenu,
GamepadManager.GamepadControl.EscapeButton,
GamepadManager.GamepadControl.BButton
);

// Generate kiosk code
useEffect(() => {
Expand All @@ -70,7 +54,7 @@ const AddingGame: React.FC<IProps> = ({}) => {

setGeneratingKioskCode(true);

generateKioskCodeAsync(generatedCodeDuration).then(newKioskCode => {
generateKioskCodeAsync(generatedCodeDuration).then(() => {
setGeneratingKioskCode(false);
});
};
Expand All @@ -95,7 +79,7 @@ const AddingGame: React.FC<IProps> = ({}) => {
onClick={kioskLinkClicked}
href={kioskUrl}
>
{kioskUrl}
<div className="kioskLinkContent">{kioskUrl}</div>
</a>
</div>
</div>
Expand Down Expand Up @@ -127,13 +111,12 @@ const AddingGame: React.FC<IProps> = ({}) => {
</li>
</ol>
</div>

<div className="QRCodeHolder">{qrDivContent()}</div>
<div style={{ flexGrow: 1 }} />
{qrDivContent()}
</div>
<AddGameButton
selected={menuButtonSelected}
content="Return to menu"
/>
<GenericButton autofocus={true} onClick={gotoMainMenu}>
{"Return to menu"}
</GenericButton>
</div>
);
};
Expand Down
71 changes: 71 additions & 0 deletions kiosk/src/Components/AppModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useContext } from "react";
import { AppStateContext } from "../State/AppStateContext";
import ConfirmModal from "./ConfirmModal";
import { hideModal, postNotification } from "../State/Actions";
import { playSoundEffect } from "../Services/SoundEffectService";
import * as Storage from "../Services/LocalStorage";
import { removeGame } from "../Transforms/removeGame";
import { makeNotification } from "../Utils";
import * as GamepadManager from "../Services/GamepadManager";

export default function AppModal() {
const { state, dispatch } = useContext(AppStateContext);

const hideAppModal = () => {
GamepadManager.lockControl(GamepadManager.GamepadControl.AButton);
GamepadManager.lockControl(GamepadManager.GamepadControl.EscapeButton);
dispatch(hideModal());
};

const cancelClicked = () => {
playSoundEffect("select");
hideAppModal();
};

const deleteConfirmClicked = () => {
pxt.tickEvent("kiosk.deleteGame.confirmed");
playSoundEffect("select");
deleteGame();
hideAppModal();
};

const selectedGame = state.allGames.find(
g => g.id === state.selectedGameId
);

const deleteGame = () => {
const userAddedGames = Storage.getUserAddedGames();
const gameId = state.selectedGameId;
if (gameId && userAddedGames.hasOwnProperty(gameId)) {
const name = userAddedGames[gameId].name;
userAddedGames[gameId].deleted = true;
Storage.setUserAddedGames(userAddedGames);
removeGame(gameId);
dispatch(
postNotification(makeNotification(`${name} deleted`, 5000))
);
}
};

switch (state.modal?.id) {
case "delete-game-confirmation":
return (
<ConfirmModal
title={"Delete Game?"}
onCancel={cancelClicked}
onConfirm={deleteConfirmClicked}
>
<div className="common-modal-body">
<p>
<b>{`Delete "${selectedGame?.name}"? `}</b>
{
"The only way to get the game back is by re-uploading it."
}
</p>
</div>
</ConfirmModal>
);
default:
return null;
}
}
Loading

0 comments on commit 17ec7dc

Please sign in to comment.