mirror of
https://github.com/Vikeo/LifeTrinket.git
synced 2025-11-11 21:56:25 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e692454b96 | ||
|
|
71321e98dc | ||
|
|
c381b87c48 | ||
|
|
aa479e67bf | ||
|
|
97f9bb75e5 | ||
|
|
341cb3cde0 | ||
|
|
ce9c9ca997 | ||
|
|
ad485f059d | ||
|
|
92f954130f | ||
|
|
112023bdd5 | ||
|
|
4e6dc56d99 | ||
|
|
e427bfd0cf | ||
|
|
ed10edc6d2 | ||
|
|
7696b357b4 | ||
|
|
a7b78b8e7a | ||
|
|
fa95d171b7 | ||
|
|
00a556be0e |
@@ -1,12 +1,12 @@
|
|||||||
index.html,1711189442688,fa2549e32940c356ac5cee88c8db61076ad62fb4e599858c8e45cfc68cd901c4
|
index.html,1711905710499,4b604b23faec8d63a58e07b96d724a1aea56a7c86d57c9af791832ce87a552e7
|
||||||
manifest.json,1711189442512,7ff5111aa04a42adff3b38924ec467b6f345ed0309dba1486dc9b783b60c2a9d
|
manifest.webmanifest,1711905710499,f2bf253209f6e292a6b0dbfa06fb4ac188eb5f2dba568c3ad5511b9ed52c1f51
|
||||||
registerSW.js,1711189442688,5b6445b5215737c53ef0d379c151d57de165a056de2d1c5812ed614f158ebcbd
|
manifest.json,1711905710294,7ff5111aa04a42adff3b38924ec467b6f345ed0309dba1486dc9b783b60c2a9d
|
||||||
sw.js,1711189443521,9c09d33ea573bb818864bfad526fa911839637171773eca8e31905458679846d
|
sw.js,1711905711506,1ef2f4f40ec8dca15cc42d547462ade1e84314ea9722276f5994ccee7bbdf80f
|
||||||
robots.txt,1711189442512,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2
|
robots.txt,1711905710294,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2
|
||||||
manifest.webmanifest,1711189442688,f2bf253209f6e292a6b0dbfa06fb4ac188eb5f2dba568c3ad5511b9ed52c1f51
|
registerSW.js,1711905710499,5b6445b5215737c53ef0d379c151d57de165a056de2d1c5812ed614f158ebcbd
|
||||||
workbox-3e911b1d.js,1711189443521,d5dbc868a5c07af633d29de7ba3ffe37542aaaabdf33713b4298df31f92f11ff
|
workbox-3e911b1d.js,1711905711507,d5dbc868a5c07af633d29de7ba3ffe37542aaaabdf33713b4298df31f92f11ff
|
||||||
assets/index-WLCHZTqE.css,1711189442688,877e5ea9bfd3a1ca0e6449e8213da8a3c7717e530370f12669bb5c70dd21e700
|
assets/index-7m_Zw4yH.css,1711905710499,37997d06b32b3d0c724c054913e3c0583b86f786358121cb1615e6646ff46b56
|
||||||
favicon.ico,1711189442511,8cefe5adbf00d337d8633fb744b9f2c4914f769b319be4bb7e184b7a4aa17160
|
favicon.ico,1711905710294,8cefe5adbf00d337d8633fb744b9f2c4914f769b319be4bb7e184b7a4aa17160
|
||||||
logo192.png,1711189442511,3d1e2e6f064d4fd325828f21bb6493ff0dbf2390b0e7d2aba9f2b6def4829799
|
logo192.png,1711905710294,3d1e2e6f064d4fd325828f21bb6493ff0dbf2390b0e7d2aba9f2b6def4829799
|
||||||
logo512.png,1711189442511,892a4da1cc5434929a83a71fcbcb0d0d80aa82f44e3c21e9b20ffe9267197133
|
logo512.png,1711905710294,892a4da1cc5434929a83a71fcbcb0d0d80aa82f44e3c21e9b20ffe9267197133
|
||||||
assets/index-OHs0lOr7.js,1711189442688,aa0dca732cd5b6f621ecb7c6dbcbfdbccde78941cfad954f6626d4ff83040c7f
|
assets/index-CLJVONOc.js,1711905710499,22f3835412f82bb3f8a62e74a7f4602a9c43b447224790365dbcc6cbffb4e665
|
||||||
|
|||||||
1
.github/workflows/firebase-release.yml
vendored
1
.github/workflows/firebase-release.yml
vendored
@@ -8,6 +8,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
VITE_REPO_READ_ACCESS_TOKEN: ${{ secrets.REPO_READ_ACCESS_TOKEN }}
|
VITE_REPO_READ_ACCESS_TOKEN: ${{ secrets.REPO_READ_ACCESS_TOKEN }}
|
||||||
|
VITE_FIREBASE_ANALYTICS_API_KEY: ${{ secrets.FIREBASE_ANALYTICS_API_KEY }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "life-trinket",
|
"name": "life-trinket",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.9.2",
|
"version": "0.9.5",
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18",
|
"node": ">=18",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Cross } from '../../Icons/generated';
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useAnalytics } from '../../Hooks/useAnalytics';
|
import { useAnalytics } from '../../Hooks/useAnalytics';
|
||||||
|
|
||||||
export const ModalWrapper = twc.div`absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 h-[85vh] bg-background-default p-4 overflow-scroll rounded-2xl border-none text-text-primary w-[95vw] max-w-[548px]`;
|
export const ModalWrapper = twc.div`absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-[47.5%] h-[95%] bg-background-default p-4 overflow-scroll rounded-2xl border-none text-text-primary w-[95vw] max-w-[548px]`;
|
||||||
|
|
||||||
type InfoModalProps = {
|
type InfoModalProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ export const SettingsModal = ({ isOpen, closeModal }: SettingsModalProps) => {
|
|||||||
<option value={PreStartMode.FingerGame}>
|
<option value={PreStartMode.FingerGame}>
|
||||||
Touch Roulette
|
Touch Roulette
|
||||||
</option>
|
</option>
|
||||||
|
<option value={PreStartMode.Trivia}>Group Trivia</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-left text-text-secondary">
|
<div className="text-xs text-left text-text-secondary">
|
||||||
@@ -215,6 +216,14 @@ export const SettingsModal = ({ isOpen, closeModal }: SettingsModalProps) => {
|
|||||||
random.
|
random.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{settings.preStartMode === PreStartMode.Trivia && (
|
||||||
|
<div className="text-xs text-left text-text-secondary mt-1">
|
||||||
|
<span className="text-text-primary">Group Trivia:</span> A
|
||||||
|
random "who is the most ..." type question will be shown, the
|
||||||
|
group decides which player fits the question best.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</SettingContainer>
|
</SettingContainer>
|
||||||
<SettingContainer>
|
<SettingContainer>
|
||||||
<ToggleContainer>
|
<ToggleContainer>
|
||||||
|
|||||||
@@ -110,11 +110,13 @@ const PlayerMenu = ({
|
|||||||
settings,
|
settings,
|
||||||
setPlaying,
|
setPlaying,
|
||||||
setRandomizingPlayer,
|
setRandomizingPlayer,
|
||||||
|
saveCurrentGame,
|
||||||
|
initialGameSettings,
|
||||||
} = useGlobalSettings();
|
} = useGlobalSettings();
|
||||||
|
|
||||||
const analytics = useAnalytics();
|
const analytics = useAnalytics();
|
||||||
|
|
||||||
const { updatePlayer, resetCurrentGame } = usePlayers();
|
const { updatePlayer, resetCurrentGame, players } = usePlayers();
|
||||||
|
|
||||||
const handleColorChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleColorChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const updatedPlayer = { ...player, color: event.target.value };
|
const updatedPlayer = { ...player, color: event.target.value };
|
||||||
@@ -137,6 +139,7 @@ const PlayerMenu = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleGoToStart = () => {
|
const handleGoToStart = () => {
|
||||||
|
saveCurrentGame({ players, initialGameSettings });
|
||||||
goToStart();
|
goToStart();
|
||||||
setRandomizingPlayer(true);
|
setRandomizingPlayer(true);
|
||||||
};
|
};
|
||||||
@@ -443,8 +446,14 @@ const PlayerMenu = ({
|
|||||||
className="text-center text-text-primary"
|
className="text-center text-text-primary"
|
||||||
style={{ fontSize: extraCountersSize }}
|
style={{ fontSize: extraCountersSize }}
|
||||||
>
|
>
|
||||||
End Game?
|
Go to start?
|
||||||
</h1>
|
</h1>
|
||||||
|
<div
|
||||||
|
style={{ fontSize: iconSize }}
|
||||||
|
className="text-center text-text-primary"
|
||||||
|
>
|
||||||
|
(Game will be saved)
|
||||||
|
</div>
|
||||||
<div className="flex justify-evenly gap-2">
|
<div className="flex justify-evenly gap-2">
|
||||||
<button
|
<button
|
||||||
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
|
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
|
||||||
|
|||||||
160
src/Components/PreStartGame/Games/Trivia.tsx
Normal file
160
src/Components/PreStartGame/Games/Trivia.tsx
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { useGlobalSettings } from '../../../Hooks/useGlobalSettings';
|
||||||
|
|
||||||
|
const questions = [
|
||||||
|
'Who has the most siblings?',
|
||||||
|
'Who has the most pets?',
|
||||||
|
'Who has the most tattoos?',
|
||||||
|
'Who has the most piercings?',
|
||||||
|
'Who has the most expensive shoes?',
|
||||||
|
'Who has the most most amount of teeth?',
|
||||||
|
'Who has the most least amount of teeth?',
|
||||||
|
'Who has the most least amount of teeth?',
|
||||||
|
'Who lives closest to the equator?',
|
||||||
|
'Who is the tallest person in the group?',
|
||||||
|
'Who is the shortest person in the group?',
|
||||||
|
'Who speaks the most languages?',
|
||||||
|
'Who has traveled to the most countries?',
|
||||||
|
'Who has the earliest birthday in the year?',
|
||||||
|
'Who has won the most awards or trophies?',
|
||||||
|
'Who is the best cook among you?',
|
||||||
|
'Who is the fastest runner?',
|
||||||
|
'Who has the most unique hobby?',
|
||||||
|
'Who is the biggest movie buff?',
|
||||||
|
'Who is the most tech-savvy?',
|
||||||
|
'Who is the best at solving puzzles?',
|
||||||
|
'Who has the most extensive music collection?',
|
||||||
|
'Who has the most impressive collection of books?',
|
||||||
|
'Who has the most experience in a particular sport or activity?',
|
||||||
|
'Who has the most interesting job or profession?',
|
||||||
|
'Who has the most artistic talent?',
|
||||||
|
'Who is the most organized person?',
|
||||||
|
'Who is the best at keeping secrets?',
|
||||||
|
'Who has the most fascinating family history?',
|
||||||
|
'Who has the most embarrassing childhood nickname?',
|
||||||
|
'Who has the most unusual talent or skill?',
|
||||||
|
'Who has the most interesting family tradition?',
|
||||||
|
'Who has the most impressive celebrity encounter?',
|
||||||
|
'Who has the most unusual phobia?',
|
||||||
|
'Who has the most adventurous spirit?',
|
||||||
|
'Who has the most unique item in their wallet/purse?',
|
||||||
|
'Who has the most daring fashion sense?',
|
||||||
|
'Who has the most impressive party trick?',
|
||||||
|
'Who has the most memorable encounter with a wild animal?',
|
||||||
|
'Who has the most adventurous palate?',
|
||||||
|
'Who has the most unusual collection?',
|
||||||
|
'Who has the most unique bucket list item?',
|
||||||
|
'Who has the most inspiring life motto or mantra?',
|
||||||
|
'Who is the most likely to break out into song or dance in public?',
|
||||||
|
'Who is the most likely to be found binge-watching TV shows?',
|
||||||
|
'Who is the biggest procrastinator?',
|
||||||
|
'Who is the most likely to cry during a movie?',
|
||||||
|
'Who is the most adventurous when it comes to trying new foods?',
|
||||||
|
"Who is the most likely to forget someone's birthday?",
|
||||||
|
'Who is the best at giving advice?',
|
||||||
|
'Who is the worst at giving advice?',
|
||||||
|
'Who is the most likely to be found reading a book at a party?',
|
||||||
|
'Who is the most likely to win in a game of charades?',
|
||||||
|
'Who is the most likely to get lost in their own neighborhood?',
|
||||||
|
'Who is the most sentimental?',
|
||||||
|
'Who is the most likely to become famous?',
|
||||||
|
'Who is the most likely to become a millionaire?',
|
||||||
|
'Who is the most likely to start their own business?',
|
||||||
|
'Who is the most likely to become president?',
|
||||||
|
'Who is the most likely to go viral on social media?',
|
||||||
|
'Who is the most likely to win a Nobel Prize?',
|
||||||
|
'Who is the most likely to be a superhero in disguise?',
|
||||||
|
'Who is the most likely to survive a zombie apocalypse?',
|
||||||
|
'Who is the most likely to believe in aliens?',
|
||||||
|
'Who is the most likely to spend all their money on something silly?',
|
||||||
|
'Who is the most likely to write a bestselling novel?',
|
||||||
|
'Who is the most likely to be a secret agent?',
|
||||||
|
'Who is the most likely to be a professional athlete?',
|
||||||
|
'Who is the most likely to win a game of trivia?',
|
||||||
|
|
||||||
|
'Who is the most likely to win the upcoming game?',
|
||||||
|
'Who is the most likely to win at a game of Pokémon TCG?',
|
||||||
|
'Who has the most valuable card in their collection?',
|
||||||
|
'Who is the best at building decks?',
|
||||||
|
'Who has won the most games?',
|
||||||
|
'Who has the largest collection of cards?',
|
||||||
|
'Who is the most knowledgeable about Magic the Gathering lore?',
|
||||||
|
'Who is the most strategic?',
|
||||||
|
'Who is the most likely to trade away their most valuable card for something silly?',
|
||||||
|
'Who is the most competitive?',
|
||||||
|
'Who would be the most creative when it comes to making up new Magic the Gathering rules?',
|
||||||
|
'Who is the most likely to organize a Magic the Gathering draft tournament?',
|
||||||
|
'Who is the most enthusiastic about opening booster packs?',
|
||||||
|
'Who has the most unique and unusual Magic the Gathering deck?',
|
||||||
|
'Who is the most likely to cosplay as their favorite Magic the Gathering character?',
|
||||||
|
'Who is the most likely to forget to bring their Magic the Gathering deck to a game night?',
|
||||||
|
'Who is the most generous when it comes to lending out their decks?',
|
||||||
|
'Who is the most likely to start their own Magic the Gathering YouTube channel?',
|
||||||
|
'Who is the most skilled at bluffing during a game of Magic the Gathering?',
|
||||||
|
'Who is the most likely to spend all their money on Magic the Gathering cards?',
|
||||||
|
'Who is the most likely to rage quit during a game of Magic the Gathering?',
|
||||||
|
'Who is the most likely to win in a Magic the Gathering trivia contest?',
|
||||||
|
'Who is the most likely to build a themed Magic the Gathering deck?',
|
||||||
|
'Who is the most likely to organize a Magic the Gathering cube draft?',
|
||||||
|
'Who is the most likely to teach new players how to play Magic the Gathering?',
|
||||||
|
'Who is the most likely to build a commander deck with a ridiculous theme?',
|
||||||
|
'Who is the most likely to collect foreign-language Magic the Gathering cards?',
|
||||||
|
'Who is the most likely to participate in a Magic the Gathering charity event?',
|
||||||
|
'Who is the most likely to cosplay as their Magic the Gathering commander?',
|
||||||
|
'Who is the most likely to organize a Magic the Gathering charity tournament?',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const Trivia = () => {
|
||||||
|
const { setPlaying, goToStart } = useGlobalSettings();
|
||||||
|
|
||||||
|
const [randomQuestion, setRandomQuestion] = useState(
|
||||||
|
questions[Math.floor(Math.random() * questions.length)]
|
||||||
|
);
|
||||||
|
|
||||||
|
const setUniqueRandomQuestion = () => {
|
||||||
|
let newRandomQuestion =
|
||||||
|
questions[Math.floor(Math.random() * questions.length)];
|
||||||
|
while (newRandomQuestion === randomQuestion) {
|
||||||
|
newRandomQuestion =
|
||||||
|
questions[Math.floor(Math.random() * questions.length)];
|
||||||
|
}
|
||||||
|
setRandomQuestion(newRandomQuestion);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="absolute flex justify-center items-center w-full h-full portrait:h-[100dvw] portrait:w-[100dvh] z-50 bg-secondary-main overflow-hidden"
|
||||||
|
onClick={() => setPlaying(true)}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="absolute flex top-4 left-4 rounded-lg px-2 py-1 justify-center bg-primary-main text-text-primary text-xs"
|
||||||
|
onClick={goToStart}
|
||||||
|
>
|
||||||
|
<div className="text-xl leading-[0.80rem]">{'<'} </div>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="absolute flex top-4 right-4 rounded-lg px-2 py-1 justify-center bg-primary-main text-text-primary text-xs"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setUniqueRandomQuestion();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reroll
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="size-full flex flex-col justify-between items-center whitespace-nowrap pointer-events-none webkit-user-select-none text-wrap text-center py-[10vmin] px-[10vmax]">
|
||||||
|
<div className="text-[6vmin]">Decide who starts by answering:</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className="text-[8vmin] rotate-180 text-text-primary opacity-40">
|
||||||
|
{randomQuestion}
|
||||||
|
</div>
|
||||||
|
<div className="text-[8vmin]">{randomQuestion}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-[6vmin]">(Tap the screen to dismiss)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@ import { GridLayout } from '../Views/Play';
|
|||||||
import { FingerGame } from './Games/FingerGame';
|
import { FingerGame } from './Games/FingerGame';
|
||||||
import { RandomKingPlayers } from './Games/RandomKing/RandomKingPlayers';
|
import { RandomKingPlayers } from './Games/RandomKing/RandomKingPlayers';
|
||||||
import { RandomKingSelectWrapper } from './Games/RandomKing/RandomKingSelectWrapper';
|
import { RandomKingSelectWrapper } from './Games/RandomKing/RandomKingSelectWrapper';
|
||||||
|
import { Trivia } from './Games/Trivia';
|
||||||
|
|
||||||
export const PreStart = ({ gridLayout }: { gridLayout: GridLayout }) => {
|
export const PreStart = ({ gridLayout }: { gridLayout: GridLayout }) => {
|
||||||
const { settings, randomizingPlayer, goToStart } = useGlobalSettings();
|
const { settings, randomizingPlayer, goToStart } = useGlobalSettings();
|
||||||
@@ -25,6 +26,10 @@ export const PreStart = ({ gridLayout }: { gridLayout: GridLayout }) => {
|
|||||||
return <FingerGame />;
|
return <FingerGame />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings.preStartMode === PreStartMode.Trivia) {
|
||||||
|
return <Trivia />;
|
||||||
|
}
|
||||||
|
|
||||||
goToStart();
|
goToStart();
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ import { SettingsModal } from '../../Misc/SettingsModal';
|
|||||||
import { SupportMe } from '../../Misc/SupportMe';
|
import { SupportMe } from '../../Misc/SupportMe';
|
||||||
import { LayoutOptions } from './LayoutOptions';
|
import { LayoutOptions } from './LayoutOptions';
|
||||||
|
|
||||||
const MainWrapper = twc.div`w-[100dvw] h-fit pb-14 overflow-hidden items-center flex flex-col`;
|
const MainWrapper = twc.div`w-[100dvw] h-fit pb-24 overflow-hidden items-center flex flex-col min-[349px]:pb-10`;
|
||||||
|
|
||||||
const StartButtonFooter = twc.div`w-full max-w-[548px] fixed bottom-4 z-1 items-center flex flex-col px-4 z-10`;
|
const StartButtonFooter = twc.div`w-full max-w-[548px] fixed bottom-4 z-1 items-center flex flex-row flex-wrap px-4 z-10 gap-4`;
|
||||||
|
|
||||||
const SliderWrapper = twc.div`mx-8`;
|
const SliderWrapper = twc.div`mx-8`;
|
||||||
|
|
||||||
@@ -92,6 +92,9 @@ const Start = () => {
|
|||||||
isPWA,
|
isPWA,
|
||||||
setRandomizingPlayer,
|
setRandomizingPlayer,
|
||||||
version,
|
version,
|
||||||
|
setPlaying,
|
||||||
|
savedGame,
|
||||||
|
saveCurrentGame,
|
||||||
} = useGlobalSettings();
|
} = useGlobalSettings();
|
||||||
|
|
||||||
const [openInfoModal, setOpenInfoModal] = useState(false);
|
const [openInfoModal, setOpenInfoModal] = useState(false);
|
||||||
@@ -124,12 +127,16 @@ const Start = () => {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [playerOptions.numberOfPlayers]);
|
}, [playerOptions.numberOfPlayers]);
|
||||||
|
|
||||||
const doStartGame = () => {
|
const doStartNewGame = () => {
|
||||||
if (!initialGameSettings) {
|
if (!initialGameSettings) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
analytics.trackEvent('game_started', { ...initialGameSettings });
|
analytics.trackEvent('game_started', {
|
||||||
|
...initialGameSettings,
|
||||||
|
...settings,
|
||||||
|
isPWA,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (settings.goFullscreenOnStart) {
|
if (settings.goFullscreenOnStart) {
|
||||||
@@ -145,10 +152,40 @@ const Start = () => {
|
|||||||
|
|
||||||
setInitialGameSettings(initialGameSettings);
|
setInitialGameSettings(initialGameSettings);
|
||||||
setPlayers(createInitialPlayers(initialGameSettings));
|
setPlayers(createInitialPlayers(initialGameSettings));
|
||||||
setShowPlay(true);
|
|
||||||
setRandomizingPlayer(settings.preStartMode === PreStartMode.RandomKing);
|
setRandomizingPlayer(settings.preStartMode === PreStartMode.RandomKing);
|
||||||
localStorage.setItem('playing', 'false');
|
setShowPlay(true);
|
||||||
localStorage.setItem('showPlay', 'true');
|
setPlaying(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const doResumeGame = () => {
|
||||||
|
if (!savedGame) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
analytics.trackEvent('game_resumed', {
|
||||||
|
...initialGameSettings,
|
||||||
|
...settings,
|
||||||
|
isPWA,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (settings.goFullscreenOnStart) {
|
||||||
|
fullscreen.enableFullscreen();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.keepAwake && !wakeLock.active) {
|
||||||
|
wakeLock.request();
|
||||||
|
}
|
||||||
|
|
||||||
|
setInitialGameSettings(savedGame.initialGameSettings);
|
||||||
|
setPlayers(savedGame.players);
|
||||||
|
saveCurrentGame(null);
|
||||||
|
setRandomizingPlayer(false);
|
||||||
|
setShowPlay(true);
|
||||||
|
setPlaying(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const valueText = (value: number) => {
|
const valueText = (value: number) => {
|
||||||
@@ -192,48 +229,6 @@ const Start = () => {
|
|||||||
|
|
||||||
<div className="overflow-hidden items-center flex flex-col max-w-[548px] w-full mb-8 px-4">
|
<div className="overflow-hidden items-center flex flex-col max-w-[548px] w-full mb-8 px-4">
|
||||||
<FormControl focused={false} style={{ width: '100%' }}>
|
<FormControl focused={false} style={{ width: '100%' }}>
|
||||||
<FormLabel>Number of Players</FormLabel>
|
|
||||||
<SliderWrapper>
|
|
||||||
<Slider
|
|
||||||
title="Number of Players"
|
|
||||||
max={6}
|
|
||||||
min={1}
|
|
||||||
aria-label="Custom marks"
|
|
||||||
value={playerOptions?.numberOfPlayers ?? 4}
|
|
||||||
getAriaValueText={valueText}
|
|
||||||
step={null}
|
|
||||||
marks={playerMarks}
|
|
||||||
onChange={(_e, value) => {
|
|
||||||
setPlayerOptions({
|
|
||||||
...playerOptions,
|
|
||||||
numberOfPlayers: value as number,
|
|
||||||
orientation: Orientation.Landscape,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</SliderWrapper>
|
|
||||||
|
|
||||||
<FormLabel className="mt-[0.7rem]">Starting Health</FormLabel>
|
|
||||||
<SliderWrapper>
|
|
||||||
<Slider
|
|
||||||
title="Starting Health"
|
|
||||||
max={60}
|
|
||||||
min={20}
|
|
||||||
aria-label="Custom marks"
|
|
||||||
value={playerOptions?.startingLifeTotal ?? 40}
|
|
||||||
getAriaValueText={valueText}
|
|
||||||
step={10}
|
|
||||||
marks={healthMarks}
|
|
||||||
onChange={(_e, value) =>
|
|
||||||
setPlayerOptions({
|
|
||||||
...playerOptions,
|
|
||||||
startingLifeTotal: value as number,
|
|
||||||
orientation: Orientation.Landscape,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</SliderWrapper>
|
|
||||||
|
|
||||||
<ToggleButtonsWrapper className="mt-4">
|
<ToggleButtonsWrapper className="mt-4">
|
||||||
<ToggleContainer>
|
<ToggleContainer>
|
||||||
<FormLabel>Commander</FormLabel>
|
<FormLabel>Commander</FormLabel>
|
||||||
@@ -291,6 +286,47 @@ const Start = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ToggleButtonsWrapper>
|
</ToggleButtonsWrapper>
|
||||||
|
<FormLabel>Number of Players</FormLabel>
|
||||||
|
<SliderWrapper>
|
||||||
|
<Slider
|
||||||
|
title="Number of Players"
|
||||||
|
max={6}
|
||||||
|
min={1}
|
||||||
|
aria-label="Custom marks"
|
||||||
|
value={playerOptions?.numberOfPlayers ?? 4}
|
||||||
|
getAriaValueText={valueText}
|
||||||
|
step={null}
|
||||||
|
marks={playerMarks}
|
||||||
|
onChange={(_e, value) => {
|
||||||
|
setPlayerOptions({
|
||||||
|
...playerOptions,
|
||||||
|
numberOfPlayers: value as number,
|
||||||
|
orientation: Orientation.Landscape,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SliderWrapper>
|
||||||
|
|
||||||
|
<FormLabel className="mt-[0.7rem]">Starting Health</FormLabel>
|
||||||
|
<SliderWrapper>
|
||||||
|
<Slider
|
||||||
|
title="Starting Health"
|
||||||
|
max={60}
|
||||||
|
min={20}
|
||||||
|
aria-label="Custom marks"
|
||||||
|
value={playerOptions?.startingLifeTotal ?? 40}
|
||||||
|
getAriaValueText={valueText}
|
||||||
|
step={10}
|
||||||
|
marks={healthMarks}
|
||||||
|
onChange={(_e, value) =>
|
||||||
|
setPlayerOptions({
|
||||||
|
...playerOptions,
|
||||||
|
startingLifeTotal: value as number,
|
||||||
|
orientation: Orientation.Landscape,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SliderWrapper>
|
||||||
|
|
||||||
<FormLabel>Layout</FormLabel>
|
<FormLabel>Layout</FormLabel>
|
||||||
<LayoutOptions
|
<LayoutOptions
|
||||||
@@ -314,14 +350,25 @@ const Start = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<StartButtonFooter>
|
<StartButtonFooter>
|
||||||
<Button
|
<button
|
||||||
size="large"
|
className="flex flex-grow basis-0 justify-center self-center items-center bg-primary-main px-3 py-2 rounded-md text-text-primary min-w-[150px]"
|
||||||
variant="contained"
|
onClick={doStartNewGame}
|
||||||
onClick={doStartGame}
|
|
||||||
fullWidth
|
|
||||||
>
|
>
|
||||||
START GAME
|
NEW GAME
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
|
{savedGame && (
|
||||||
|
<button
|
||||||
|
className="flex flex-grow basis-0 justify-center self-center items-center bg-primary-dark px-3 py-2 rounded-md text-text-primary min-w-[150px]"
|
||||||
|
onClick={doResumeGame}
|
||||||
|
>
|
||||||
|
RESUME
|
||||||
|
<span className="text-xs">
|
||||||
|
({savedGame.players.length}
|
||||||
|
{savedGame.players.length > 1 ? 'players' : 'player'})
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</StartButtonFooter>
|
</StartButtonFooter>
|
||||||
</MainWrapper>
|
</MainWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
import { InitialGameSettings, Settings } from '../Types/Settings';
|
import { InitialGameSettings, Settings } from '../Types/Settings';
|
||||||
|
import { Player } from '../Types/Player';
|
||||||
|
|
||||||
type Version = {
|
type Version = {
|
||||||
installedVersion: string;
|
installedVersion: string;
|
||||||
@@ -8,6 +9,11 @@ type Version = {
|
|||||||
remoteVersion?: string;
|
remoteVersion?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SavedGame = {
|
||||||
|
initialGameSettings: InitialGameSettings;
|
||||||
|
players: Player[];
|
||||||
|
} | null;
|
||||||
|
|
||||||
export type GlobalSettingsContextType = {
|
export type GlobalSettingsContextType = {
|
||||||
fullscreen: {
|
fullscreen: {
|
||||||
isFullscreen: boolean;
|
isFullscreen: boolean;
|
||||||
@@ -25,7 +31,7 @@ export type GlobalSettingsContextType = {
|
|||||||
goToStart: () => void;
|
goToStart: () => void;
|
||||||
showPlay: boolean;
|
showPlay: boolean;
|
||||||
setShowPlay: (showPlay: boolean) => void;
|
setShowPlay: (showPlay: boolean) => void;
|
||||||
initialGameSettings: InitialGameSettings | null;
|
initialGameSettings: InitialGameSettings;
|
||||||
setInitialGameSettings: (initialGameSettings: InitialGameSettings) => void;
|
setInitialGameSettings: (initialGameSettings: InitialGameSettings) => void;
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
setSettings: (settings: Settings) => void;
|
setSettings: (settings: Settings) => void;
|
||||||
@@ -36,8 +42,9 @@ export type GlobalSettingsContextType = {
|
|||||||
isPWA: boolean;
|
isPWA: boolean;
|
||||||
preStartCompleted: boolean;
|
preStartCompleted: boolean;
|
||||||
setPreStartCompleted: (completed: boolean) => void;
|
setPreStartCompleted: (completed: boolean) => void;
|
||||||
|
|
||||||
version: Version;
|
version: Version;
|
||||||
|
savedGame: SavedGame;
|
||||||
|
saveCurrentGame: (currentGame: SavedGame) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GlobalSettingsContext =
|
export const GlobalSettingsContext =
|
||||||
|
|||||||
@@ -23,7 +23,12 @@ export const useAnalytics = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logEvent(analytics, eventName, eventParams);
|
const paramsWithVersion = {
|
||||||
|
...eventParams,
|
||||||
|
app_version: import.meta.env.VITE_APP_VERSION,
|
||||||
|
};
|
||||||
|
|
||||||
|
logEvent(analytics, eventName, paramsWithVersion);
|
||||||
};
|
};
|
||||||
|
|
||||||
return { trackEvent };
|
return { trackEvent };
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useWakeLock } from 'react-screen-wake-lock';
|
|||||||
import {
|
import {
|
||||||
GlobalSettingsContext,
|
GlobalSettingsContext,
|
||||||
GlobalSettingsContextType,
|
GlobalSettingsContextType,
|
||||||
|
SavedGame,
|
||||||
} from '../Contexts/GlobalSettingsContext';
|
} from '../Contexts/GlobalSettingsContext';
|
||||||
import { useAnalytics } from '../Hooks/useAnalytics';
|
import { useAnalytics } from '../Hooks/useAnalytics';
|
||||||
import {
|
import {
|
||||||
@@ -21,12 +22,21 @@ export const GlobalSettingsProvider = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const analytics = useAnalytics();
|
const analytics = useAnalytics();
|
||||||
|
|
||||||
const savedShowPlay = localStorage.getItem('showPlay');
|
const localSavedGame = localStorage.getItem('savedGame');
|
||||||
const savedGameSettings = localStorage.getItem('initialGameSettings');
|
const [savedGame, setCurrentGame] = useState<SavedGame>(
|
||||||
const savedSettings = localStorage.getItem('settings');
|
localSavedGame ? JSON.parse(localSavedGame) : null
|
||||||
const savedPlaying = localStorage.getItem('playing');
|
);
|
||||||
const savedPreStartComplete = localStorage.getItem('preStartComplete');
|
const setCurrentGameAndLocalStorage = (savedGame: SavedGame) => {
|
||||||
|
if (!savedGame) {
|
||||||
|
setCurrentGame(savedGame);
|
||||||
|
localStorage.removeItem('savedGame');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setCurrentGame(savedGame);
|
||||||
|
localStorage.setItem('savedGame', JSON.stringify(savedGame));
|
||||||
|
};
|
||||||
|
|
||||||
|
const savedPlaying = localStorage.getItem('playing');
|
||||||
const [playing, setPlaying] = useState<boolean>(
|
const [playing, setPlaying] = useState<boolean>(
|
||||||
savedPlaying ? savedPlaying === 'true' : false
|
savedPlaying ? savedPlaying === 'true' : false
|
||||||
);
|
);
|
||||||
@@ -35,23 +45,42 @@ export const GlobalSettingsProvider = ({
|
|||||||
localStorage.setItem('playing', String(playing));
|
localStorage.setItem('playing', String(playing));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const savedPreStartComplete = localStorage.getItem('preStartComplete');
|
||||||
const [preStartCompleted, setPreStartCompleted] = useState<boolean>(
|
const [preStartCompleted, setPreStartCompleted] = useState<boolean>(
|
||||||
savedPreStartComplete ? savedPreStartComplete === 'true' : false
|
savedPreStartComplete ? savedPreStartComplete === 'true' : false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const savedShowPlay = localStorage.getItem('showPlay');
|
||||||
const [showPlay, setShowPlay] = useState<boolean>(
|
const [showPlay, setShowPlay] = useState<boolean>(
|
||||||
savedShowPlay ? savedShowPlay === 'true' : false
|
savedShowPlay ? savedShowPlay === 'true' : false
|
||||||
);
|
);
|
||||||
|
const setShowPlayAndLocalStorage = (showPlay: boolean) => {
|
||||||
|
setShowPlay(showPlay);
|
||||||
|
localStorage.setItem('showPlay', String(showPlay));
|
||||||
|
};
|
||||||
|
|
||||||
|
const savedSettings = localStorage.getItem('settings');
|
||||||
const [randomizingPlayer, setRandomizingPlayer] = useState<boolean>(
|
const [randomizingPlayer, setRandomizingPlayer] = useState<boolean>(
|
||||||
savedSettings
|
savedSettings
|
||||||
? Boolean(JSON.parse(savedSettings).preStartMode === 'random-king')
|
? Boolean(JSON.parse(savedSettings).preStartMode === 'random-king')
|
||||||
: true
|
: true
|
||||||
);
|
);
|
||||||
|
const [settings, setSettings] = useState<Settings>(
|
||||||
|
savedSettings ? JSON.parse(savedSettings) : defaultSettings
|
||||||
|
);
|
||||||
|
|
||||||
|
const setSettingsAndLocalStorage = (settings: Settings) => {
|
||||||
|
setSettings(settings);
|
||||||
|
localStorage.setItem('settings', JSON.stringify(settings));
|
||||||
|
};
|
||||||
|
|
||||||
|
const savedGameSettings = localStorage.getItem('initialGameSettings');
|
||||||
|
|
||||||
const [initialGameSettings, setInitialGameSettings] =
|
const [initialGameSettings, setInitialGameSettings] =
|
||||||
useState<InitialGameSettings | null>(
|
useState<InitialGameSettings>(
|
||||||
savedGameSettings ? JSON.parse(savedGameSettings) : null
|
savedGameSettings
|
||||||
|
? JSON.parse(savedGameSettings)
|
||||||
|
: defaultInitialGameSettings
|
||||||
);
|
);
|
||||||
|
|
||||||
const setInitialGameSettingsAndLocalStorage = (
|
const setInitialGameSettingsAndLocalStorage = (
|
||||||
@@ -64,15 +93,6 @@ export const GlobalSettingsProvider = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const [settings, setSettings] = useState<Settings>(
|
|
||||||
savedSettings ? JSON.parse(savedSettings) : defaultSettings
|
|
||||||
);
|
|
||||||
|
|
||||||
const setSettingsAndLocalStorage = (settings: Settings) => {
|
|
||||||
setSettings(settings);
|
|
||||||
localStorage.setItem('settings', JSON.stringify(settings));
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeLocalStorage = async () => {
|
const removeLocalStorage = async () => {
|
||||||
localStorage.removeItem('initialGameSettings');
|
localStorage.removeItem('initialGameSettings');
|
||||||
localStorage.removeItem('players');
|
localStorage.removeItem('players');
|
||||||
@@ -252,7 +272,7 @@ export const GlobalSettingsProvider = ({
|
|||||||
},
|
},
|
||||||
goToStart,
|
goToStart,
|
||||||
showPlay,
|
showPlay,
|
||||||
setShowPlay,
|
setShowPlay: setShowPlayAndLocalStorage,
|
||||||
playing,
|
playing,
|
||||||
setPlaying: setPlayingAndLocalStorage,
|
setPlaying: setPlayingAndLocalStorage,
|
||||||
initialGameSettings,
|
initialGameSettings,
|
||||||
@@ -264,6 +284,8 @@ export const GlobalSettingsProvider = ({
|
|||||||
isPWA: window?.matchMedia('(display-mode: standalone)').matches,
|
isPWA: window?.matchMedia('(display-mode: standalone)').matches,
|
||||||
preStartCompleted,
|
preStartCompleted,
|
||||||
setPreStartCompleted: setPreStartCompletedAndLocalStorage,
|
setPreStartCompleted: setPreStartCompletedAndLocalStorage,
|
||||||
|
savedGame,
|
||||||
|
saveCurrentGame: setCurrentGameAndLocalStorage,
|
||||||
|
|
||||||
version: {
|
version: {
|
||||||
installedVersion: import.meta.env.VITE_APP_VERSION,
|
installedVersion: import.meta.env.VITE_APP_VERSION,
|
||||||
@@ -285,6 +307,7 @@ export const GlobalSettingsProvider = ({
|
|||||||
settings,
|
settings,
|
||||||
randomizingPlayer,
|
randomizingPlayer,
|
||||||
preStartCompleted,
|
preStartCompleted,
|
||||||
|
savedGame,
|
||||||
remoteVersion,
|
remoteVersion,
|
||||||
isLatestVersion,
|
isLatestVersion,
|
||||||
analytics,
|
analytics,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export enum PreStartMode {
|
|||||||
None = 'none',
|
None = 'none',
|
||||||
RandomKing = 'random-king',
|
RandomKing = 'random-king',
|
||||||
FingerGame = 'finger-game',
|
FingerGame = 'finger-game',
|
||||||
|
Trivia = 'trivia',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Settings = {
|
export type Settings = {
|
||||||
|
|||||||
@@ -31,6 +31,16 @@ code {
|
|||||||
monospace;
|
monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hide scrollbar globally
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
.pointer-events-all {
|
.pointer-events-all {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
|
|||||||
1
src/vite-env.d.ts
vendored
1
src/vite-env.d.ts
vendored
@@ -3,6 +3,7 @@
|
|||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
readonly VITE_APP_VERSION: string;
|
readonly VITE_APP_VERSION: string;
|
||||||
readonly VITE_REPO_READ_ACCESS_TOKEN: string;
|
readonly VITE_REPO_READ_ACCESS_TOKEN: string;
|
||||||
|
readonly VITE_FIREBASE_ANALYTICS_API_KEY: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
|
|||||||
@@ -27,5 +27,8 @@ export default defineConfig({
|
|||||||
VITE_REPO_READ_ACCESS_TOKEN: JSON.stringify(
|
VITE_REPO_READ_ACCESS_TOKEN: JSON.stringify(
|
||||||
process.env.VITE_REPO_READ_ACCESS_TOKEN
|
process.env.VITE_REPO_READ_ACCESS_TOKEN
|
||||||
),
|
),
|
||||||
|
VITE_FIREBASE_ANALYTICS_API_KEY: JSON.stringify(
|
||||||
|
process.env.VITE_FIREBASE_ANALYTICS_API_KEY
|
||||||
|
),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user