show and hide things based on ios, add button for installing PWA

This commit is contained in:
Vikeo
2024-05-27 10:47:59 +02:00
parent 4b7b06ded3
commit 2ec9998a2a
6 changed files with 157 additions and 57 deletions

View File

@@ -1,7 +1,7 @@
{ {
"name": "life-trinket", "name": "life-trinket",
"private": true, "private": true,
"version": "0.9.95", "version": "0.9.96",
"type": "commonjs", "type": "commonjs",
"engines": { "engines": {
"node": ">=20", "node": ">=20",

View File

@@ -2,6 +2,7 @@ import { twc } from 'react-twc';
import { useAnalytics } from '../../Hooks/useAnalytics'; import { useAnalytics } from '../../Hooks/useAnalytics';
import { useGlobalSettings } from '../../Hooks/useGlobalSettings'; import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
import { PreStartMode } from '../../Types/Settings'; import { PreStartMode } from '../../Types/Settings';
import { InstallPWA } from '../Misc/InstallPWAButton';
import { Separator } from '../Misc/Separator'; import { Separator } from '../Misc/Separator';
import { Paragraph } from '../Misc/TextComponents'; import { Paragraph } from '../Misc/TextComponents';
import { ToggleButton } from '../Misc/ToggleButton'; import { ToggleButton } from '../Misc/ToggleButton';
@@ -201,25 +202,25 @@ export const SettingsDialog = ({
is enabled. is enabled.
</Description> </Description>
</SettingContainer> </SettingContainer>
<SettingContainer> {(!window.isIOS || window.isIPad) && (
<ToggleContainer> <SettingContainer>
<label> <ToggleContainer>
Fullscreen on start <span className="text-xs">(Android only)</span> <label>Fullscreen on start</label>
</label> <ToggleButton
<ToggleButton checked={settings.goFullscreenOnStart}
checked={settings.goFullscreenOnStart} onChange={() => {
onChange={() => { setSettings({
setSettings({ ...settings,
...settings, goFullscreenOnStart: !settings.goFullscreenOnStart,
goFullscreenOnStart: !settings.goFullscreenOnStart, });
}); }}
}} />
/> </ToggleContainer>
</ToggleContainer> <Description>
<Description> Will enter fullscreen mode when starting a game if this is enabled.
Will enter fullscreen mode when starting a game if this is enabled. </Description>
</Description> </SettingContainer>
</SettingContainer> )}
<SettingContainer> <SettingContainer>
<ToggleContainer> <ToggleContainer>
@@ -255,21 +256,45 @@ export const SettingsDialog = ({
</div> </div>
{!isPWA && ( {!isPWA && (
<> <>
<Separator height="1px" /> {window.isIOS && (
<SettingContainer> <>
<ToggleContainer> <Separator height="1px" />
<Paragraph> <SettingContainer>
<b>Tip:</b> You can{' '} <ToggleContainer>
<b>add this webapp to your home page on iOS</b> or{' '} <Paragraph>
<b>install it on Android</b> to have it act just like a normal <b>Tip:</b> You can <b>add this webapp to your home page</b>{' '}
app! to have it act just like a normal app!
</Paragraph> </Paragraph>
</ToggleContainer> </ToggleContainer>
<Description className="mt-1"> <Description className="mt-1">
If you do, this app will work offline and the toolbar will be If you do, this web app will work offline and the toolbar will
automatically hidden. be automatically hidden.
</Description> </Description>
</SettingContainer> </SettingContainer>
</>
)}
{!window.isIOS && (
<>
<Separator height="1px" />
<SettingContainer>
<ToggleContainer>
<Paragraph>
<b>Tip:</b> You can <b>install this page as a PWA</b> to
have it act just like a normal app!
</Paragraph>
</ToggleContainer>
<Description className="mt-1">
If you do, this web app will work offline and the toolbar will
be automatically hidden. PWA stands for Progressive Web
Application
</Description>
</SettingContainer>
<div className="flex w-full justify-center">
<InstallPWA />
</div>
</>
)}
</> </>
)} )}
<Separator height="1px" /> <Separator height="1px" />

View File

@@ -0,0 +1,45 @@
import { useEffect, useRef, useState } from 'react';
import { BeforeInstallPromptEvent } from '../../global';
import { useAnalytics } from '../../Hooks/useAnalytics';
export const InstallPWA = () => {
const supportsPWARef = useRef<boolean>(false);
const [promptInstall, setPromptInstall] =
useState<BeforeInstallPromptEvent | null>(null);
const analytics = useAnalytics();
const handler = (e: BeforeInstallPromptEvent) => {
e.preventDefault();
supportsPWARef.current = true;
setPromptInstall(e);
};
useEffect(() => {
window.addEventListener('beforeinstallprompt', handler);
return () => window.removeEventListener('transitionend', handler);
}, []);
if (!supportsPWARef.current) {
return 'lull';
}
return (
<button
className="mt-1 mb-1 bg-primary-main px-3 py-1 rounded-md duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark font-bold"
aria-label="Install app"
title="Install app"
onClick={(e) => {
e.preventDefault();
if (!promptInstall) {
return;
}
analytics.trackEvent('install_pwa_prompt_shown');
promptInstall.prompt();
}}
>
Install as a PWA
</button>
);
};

View File

@@ -347,30 +347,35 @@ const PlayerMenu = ({
> >
<Exit size={iconSize} style={{ rotate: '180deg' }} /> <Exit size={iconSize} style={{ rotate: '180deg' }} />
</button> </button>
<div {(!window.isIOS || window.isIPad) && (
data-fullscreen={document.fullscreenElement ? true : false} <div
className="flex data-fullscreen={document.fullscreenElement ? true : false}
className="flex
data-[fullscreen=true]:bg-secondary-dark rounded-lg border border-transparent data-[fullscreen=true]:bg-secondary-dark rounded-lg border border-transparent
data-[fullscreen=true]:border-primary-main" data-[fullscreen=true]:border-primary-main"
> >
<IconCheckbox <IconCheckbox
className="p-1" className="p-1"
name="fullscreen" name="fullscreen"
checked={document.fullscreenElement ? true : false} checked={document.fullscreenElement ? true : false}
icon={ icon={
<FullscreenOff <FullscreenOff
size={iconSize} size={iconSize}
className="text-primary-main" className="text-primary-main"
/> />
} }
checkedIcon={ checkedIcon={
<FullscreenOn size={iconSize} className="text-primary-main" /> <FullscreenOn
} size={iconSize}
onChange={toggleFullscreen} className="text-primary-main"
aria-checked={document.fullscreenElement ? true : false} />
aria-label="Fullscreen" }
/> onChange={toggleFullscreen}
</div> aria-checked={document.fullscreenElement ? true : false}
aria-label="Fullscreen"
/>
</div>
)}
<button <button
data-wake-lock-active={settings.keepAwake} data-wake-lock-active={settings.keepAwake}

21
src/global.d.ts vendored Normal file
View File

@@ -0,0 +1,21 @@
export {};
export interface BeforeInstallPromptEvent extends Event {
readonly platforms: string[];
readonly userChoice: Promise<{
outcome: 'accepted' | 'dismissed';
platform: string;
}>;
prompt(): Promise<void>;
}
declare global {
interface Window {
isIOS: boolean;
isIPad: boolean;
}
interface WindowEventMap {
beforeinstallprompt: BeforeInstallPromptEvent;
transitionend: BeforeInstallPromptEvent;
}
}

View File

@@ -3,6 +3,10 @@ import ReactDOM from 'react-dom/client';
import App from './App.tsx'; import App from './App.tsx';
import './index.css'; import './index.css';
window.isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
window.isIPad = /iPad/.test(navigator.userAgent);
const root = ReactDOM.createRoot( const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement document.getElementById('root') as HTMLElement
); );