forked from external-repos/LifeTrinket
show and hide things based on ios, add button for installing PWA
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "life-trinket",
|
||||
"private": true,
|
||||
"version": "0.9.95",
|
||||
"version": "0.9.96",
|
||||
"type": "commonjs",
|
||||
"engines": {
|
||||
"node": ">=20",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { twc } from 'react-twc';
|
||||
import { useAnalytics } from '../../Hooks/useAnalytics';
|
||||
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
||||
import { PreStartMode } from '../../Types/Settings';
|
||||
import { InstallPWA } from '../Misc/InstallPWAButton';
|
||||
import { Separator } from '../Misc/Separator';
|
||||
import { Paragraph } from '../Misc/TextComponents';
|
||||
import { ToggleButton } from '../Misc/ToggleButton';
|
||||
@@ -201,25 +202,25 @@ export const SettingsDialog = ({
|
||||
is enabled.
|
||||
</Description>
|
||||
</SettingContainer>
|
||||
<SettingContainer>
|
||||
<ToggleContainer>
|
||||
<label>
|
||||
Fullscreen on start <span className="text-xs">(Android only)</span>
|
||||
</label>
|
||||
<ToggleButton
|
||||
checked={settings.goFullscreenOnStart}
|
||||
onChange={() => {
|
||||
setSettings({
|
||||
...settings,
|
||||
goFullscreenOnStart: !settings.goFullscreenOnStart,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</ToggleContainer>
|
||||
<Description>
|
||||
Will enter fullscreen mode when starting a game if this is enabled.
|
||||
</Description>
|
||||
</SettingContainer>
|
||||
{(!window.isIOS || window.isIPad) && (
|
||||
<SettingContainer>
|
||||
<ToggleContainer>
|
||||
<label>Fullscreen on start</label>
|
||||
<ToggleButton
|
||||
checked={settings.goFullscreenOnStart}
|
||||
onChange={() => {
|
||||
setSettings({
|
||||
...settings,
|
||||
goFullscreenOnStart: !settings.goFullscreenOnStart,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</ToggleContainer>
|
||||
<Description>
|
||||
Will enter fullscreen mode when starting a game if this is enabled.
|
||||
</Description>
|
||||
</SettingContainer>
|
||||
)}
|
||||
|
||||
<SettingContainer>
|
||||
<ToggleContainer>
|
||||
@@ -255,21 +256,45 @@ export const SettingsDialog = ({
|
||||
</div>
|
||||
{!isPWA && (
|
||||
<>
|
||||
<Separator height="1px" />
|
||||
<SettingContainer>
|
||||
<ToggleContainer>
|
||||
<Paragraph>
|
||||
<b>Tip:</b> You can{' '}
|
||||
<b>add this webapp to your home page on iOS</b> or{' '}
|
||||
<b>install it on Android</b> to have it act just like a normal
|
||||
app!
|
||||
</Paragraph>
|
||||
</ToggleContainer>
|
||||
<Description className="mt-1">
|
||||
If you do, this app will work offline and the toolbar will be
|
||||
automatically hidden.
|
||||
</Description>
|
||||
</SettingContainer>
|
||||
{window.isIOS && (
|
||||
<>
|
||||
<Separator height="1px" />
|
||||
<SettingContainer>
|
||||
<ToggleContainer>
|
||||
<Paragraph>
|
||||
<b>Tip:</b> You can <b>add this webapp to your home page</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.
|
||||
</Description>
|
||||
</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" />
|
||||
|
||||
45
src/Components/Misc/InstallPWAButton.tsx
Normal file
45
src/Components/Misc/InstallPWAButton.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@@ -347,30 +347,35 @@ const PlayerMenu = ({
|
||||
>
|
||||
<Exit size={iconSize} style={{ rotate: '180deg' }} />
|
||||
</button>
|
||||
<div
|
||||
data-fullscreen={document.fullscreenElement ? true : false}
|
||||
className="flex
|
||||
{(!window.isIOS || window.isIPad) && (
|
||||
<div
|
||||
data-fullscreen={document.fullscreenElement ? true : false}
|
||||
className="flex
|
||||
data-[fullscreen=true]:bg-secondary-dark rounded-lg border border-transparent
|
||||
data-[fullscreen=true]:border-primary-main"
|
||||
>
|
||||
<IconCheckbox
|
||||
className="p-1"
|
||||
name="fullscreen"
|
||||
checked={document.fullscreenElement ? true : false}
|
||||
icon={
|
||||
<FullscreenOff
|
||||
size={iconSize}
|
||||
className="text-primary-main"
|
||||
/>
|
||||
}
|
||||
checkedIcon={
|
||||
<FullscreenOn size={iconSize} className="text-primary-main" />
|
||||
}
|
||||
onChange={toggleFullscreen}
|
||||
aria-checked={document.fullscreenElement ? true : false}
|
||||
aria-label="Fullscreen"
|
||||
/>
|
||||
</div>
|
||||
>
|
||||
<IconCheckbox
|
||||
className="p-1"
|
||||
name="fullscreen"
|
||||
checked={document.fullscreenElement ? true : false}
|
||||
icon={
|
||||
<FullscreenOff
|
||||
size={iconSize}
|
||||
className="text-primary-main"
|
||||
/>
|
||||
}
|
||||
checkedIcon={
|
||||
<FullscreenOn
|
||||
size={iconSize}
|
||||
className="text-primary-main"
|
||||
/>
|
||||
}
|
||||
onChange={toggleFullscreen}
|
||||
aria-checked={document.fullscreenElement ? true : false}
|
||||
aria-label="Fullscreen"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
data-wake-lock-active={settings.keepAwake}
|
||||
|
||||
21
src/global.d.ts
vendored
Normal file
21
src/global.d.ts
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,10 @@ import ReactDOM from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
import './index.css';
|
||||
|
||||
window.isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
||||
|
||||
window.isIPad = /iPad/.test(navigator.userAgent);
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user