Skip to content
This repository has been archived by the owner on Nov 8, 2024. It is now read-only.

iOS PWA improvments #89

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
<meta name="theme-color" content="#120f1d" />

<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

<link rel="apple-touch-startup-image"
media="screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
href="/splash_screens/iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_landscape.png">
Expand Down
4 changes: 4 additions & 0 deletions src/assets/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ html[data-no-scroll], html[data-no-scroll] body {
overflow: hidden;
}

.top-content {
padding-top: calc(env(safe-area-inset-top) - 20px);
}

.roll {
animation: roll 1s;
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export enum Icons {
BRUSH = "brush",
UPLOAD = "upload",
WEB = "web",
SHRINK = "shrink",
STRETCH = "stretch",
}

export interface IconProps {
Expand Down Expand Up @@ -153,6 +155,8 @@ const iconList: Record<Icons, string> = {
<path d="M22.0182 15.0781C20.9582 15.403 18.7915 16.0311 16.4781 16.4781C16.0311 18.7915 15.403 20.9581 15.0781 22.0182L15.0702 22.044C18.4002 21.0274 21.0274 18.4002 22.044 15.0702L22.0182 15.0781Z" fill="currentColor"/>
<path d="M1.6103 13.323C1.64665 13.3277 1.67628 13.3327 1.68611 13.3349C1.69472 13.337 1.70821 13.3406 1.7131 13.3419L1.72391 13.345L1.72973 13.3468L1.73585 13.3487L1.74098 13.3503C1.7381 13.3494 1.67976 13.3348 1.6103 13.323Z" fill="currentColor"/>
</svg>`,
shrink: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512" fill="currentColor"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M439 7c9.4-9.4 24.6-9.4 33.9 0l32 32c9.4 9.4 9.4 24.6 0 33.9l-87 87 39 39c6.9 6.9 8.9 17.2 5.2 26.2s-12.5 14.8-22.2 14.8l-144 0c-13.3 0-24-10.7-24-24l0-144c0-9.7 5.8-18.5 14.8-22.2s19.3-1.7 26.2 5.2l39 39L439 7zM72 272l144 0c13.3 0 24 10.7 24 24l0 144c0 9.7-5.8 18.5-14.8 22.2s-19.3 1.7-26.2-5.2l-39-39L73 505c-9.4 9.4-24.6 9.4-33.9 0L7 473c-9.4-9.4-9.4-24.6 0-33.9l87-87L55 313c-6.9-6.9-8.9-17.2-5.2-26.2s12.5-14.8 22.2-14.8z"/></svg>`,
stretch: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512" fill="currentColor"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M344 0L488 0c13.3 0 24 10.7 24 24l0 144c0 9.7-5.8 18.5-14.8 22.2s-19.3 1.7-26.2-5.2l-39-39-87 87c-9.4 9.4-24.6 9.4-33.9 0l-32-32c-9.4-9.4-9.4-24.6 0-33.9l87-87L327 41c-6.9-6.9-8.9-17.2-5.2-26.2S334.3 0 344 0zM168 512L24 512c-13.3 0-24-10.7-24-24L0 344c0-9.7 5.8-18.5 14.8-22.2s19.3-1.7 26.2 5.2l39 39 87-87c9.4-9.4 24.6-9.4 33.9 0l32 32c9.4 9.4 9.4 24.6 0 33.9l-87 87 39 39c6.9 6.9 8.9 17.2 5.2 26.2s-12.5 14.8-22.2 14.8z"/></svg>`,
};

function ChromeCastButton() {
Expand Down
23 changes: 23 additions & 0 deletions src/components/buttons/IosPwaLimitations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-disable no-alert */
import { Icon, Icons } from "../Icon";

function IosPwaLimitations() {
const showAlert = () => {
alert(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't be using this
please do smth better like a modal that also is themed properly

"Due to Apple’s limitations, Picture-in-Picture (PiP) and Fullscreen are disabled on iOS PWAs. Use the browser vertion to re-enable these features.\n" +
"Tip: To hide the iOS home indicator, use guided access within the PWA!",
);
};

return (
<button
type="button"
onClick={showAlert}
className="tabbable p-2 rounded-full hover:bg-video-buttonBackground hover:bg-opacity-50 transition-transform duration-100 flex items-center gap-3 active:scale-110 active:bg-opacity-75 active:text-white"
>
<Icon className="text-2xl" icon={Icons.CIRCLE_QUESTION} />
</button>
);
}

export default IosPwaLimitations;
6 changes: 3 additions & 3 deletions src/components/layout/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ export function Navigation(props: NavigationProps) {

{/* backgrounds - these are seperate because of z-index issues */}
<div
className="fixed z-[20] pointer-events-none left-0 right-0 top-0 min-h-[150px]"
className="top-content fixed z-[20] pointer-events-none left-0 right-0 top-0 min-h-[150px]"
style={{
top: `${bannerHeight}px`,
}}
>
<div
className={classNames(
"fixed left-0 right-0 h-20 flex items-center",
"fixed left-0 right-0 top-0 flex items-center",
props.doBackground
? "bg-background-main border-b border-utils-divider border-opacity-50"
: null,
Expand All @@ -78,7 +78,7 @@ export function Navigation(props: NavigationProps) {

{/* content */}
<div
className="fixed pointer-events-none left-0 right-0 z-[60] top-0 min-h-[150px]"
className="top-content fixed pointer-events-none left-0 right-0 z-[60] top-0 min-h-[150px]"
style={{
top: `${bannerHeight}px`,
}}
Expand Down
23 changes: 23 additions & 0 deletions src/components/player/atoms/Widescreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useState } from "react";

import { Icons } from "@/components/Icon";
import { VideoPlayerButton } from "@/components/player/internals/Button";

export function Widescreen() {
// Add widescreen status
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Widescreen isn't a replacement/alternative to fullscreen
This also seems very unfinished
so we look into how add widescreen properly later

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now please remove this

const [isWideScreen, setIsWideScreen] = useState(false);

return (
<VideoPlayerButton
icon={isWideScreen ? Icons.SHRINK : Icons.STRETCH}
className="text-white"
onClick={() => {
const videoElement = document.getElementById("video-element");
if (videoElement) {
videoElement.classList.toggle("object-cover");
setIsWideScreen(!isWideScreen);
}
}}
/>
);
}
2 changes: 1 addition & 1 deletion src/components/player/base/TopControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function TopControls(props: {
<Transition
animation="slide-down"
show={props.show}
className="text-white"
className="top-content text-white"
>
{props.children}
</Transition>
Expand Down
1 change: 1 addition & 0 deletions src/components/player/internals/VideoContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ function VideoElement() {

return (
<video
id="video-element"
className="absolute inset-0 w-full h-screen bg-black"
autoPlay
playsInline
Expand Down
20 changes: 15 additions & 5 deletions src/pages/parts/home/HeroPart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,30 @@ export function HeroPart({ setIsSticky, searchParams }: HeroPartProps) {
},
[setShowBg, setIsSticky],
);
const { width: windowWidth, height: windowHeight } = useWindowSize();

const { width: windowWidth } = useWindowSize();
// Detect if running as a PWA on iOS
const isIOSPWA =
/iPad|iPhone|iPod/i.test(navigator.userAgent) &&
window.matchMedia("(display-mode: standalone)").matches;

const topSpacing = 16;
const topSpacing = isIOSPWA ? 60 : 16;
const [stickyOffset, setStickyOffset] = useState(topSpacing);

const isLandscape = windowHeight < windowWidth && isIOSPWA;
const adjustedOffset = isLandscape
? -40 // landscape
: 0; // portrait

useEffect(() => {
if (windowWidth > 1200) {
if (windowWidth > 1280) {
// On large screens the bar goes inline with the nav elements
setStickyOffset(topSpacing);
} else {
// On smaller screens the bar goes below the nav elements
setStickyOffset(topSpacing + 60);
setStickyOffset(topSpacing + 60 + adjustedOffset);
}
}, [windowWidth]);
}, [adjustedOffset, topSpacing, windowWidth]);

const time = getTimeOfDay(new Date());
const title = randomT(`home.titles.${time}`);
Expand Down
53 changes: 49 additions & 4 deletions src/pages/parts/player/PlayerPart.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { ReactNode } from "react";
import { ReactNode, useState } from "react";

import IosPwaLimitations from "@/components/buttons/IosPwaLimitations";
import { BrandPill } from "@/components/layout/BrandPill";
import { Player } from "@/components/player";
import { Widescreen } from "@/components/player/atoms/Widescreen";
import { useShouldShowControls } from "@/components/player/hooks/useShouldShowControls";
import { useIsMobile } from "@/hooks/useIsMobile";
import { PlayerMeta, playerStatus } from "@/stores/player/slices/source";
Expand All @@ -20,6 +22,26 @@ export function PlayerPart(props: PlayerPartProps) {
const { isMobile } = useIsMobile();
const isLoading = usePlayerStore((s) => s.mediaPlaying.isLoading);

// Detect if running as a PWA on iOS
const isIOSPWA =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just make this a separate hook in /hooks

/iPad|iPhone|iPod/i.test(navigator.userAgent) &&
window.matchMedia("(display-mode: standalone)").matches;

// Detect if Shift key is being held
const [isShifting, setIsShifting] = useState(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this has to go since we aren't adding widescreen for now


document.addEventListener("keydown", (event) => {
if (event.key === "Shift") {
setIsShifting(true);
}
});

document.addEventListener("keyup", (event) => {
if (event.key === "Shift") {
setIsShifting(false);
}
});

return (
<Player.Container onLoad={props.onLoad} showingControls={showTargets}>
{props.children}
Expand Down Expand Up @@ -116,18 +138,41 @@ export function PlayerPart(props: PlayerPartProps) {
<Player.Settings />
</>
) : null}
<Player.Fullscreen />
{/* Fullscreen on when not shifting */}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, all this has to go

{!isShifting && <Player.Fullscreen />}

{/* Expand button visible when shifting */}
{isShifting && (
<div>
<Widescreen />
</div>
)}
</div>
</div>
<div className="grid grid-cols-[2.5rem,1fr,2.5rem] gap-3 lg:hidden">
<div />
<div className="flex justify-center space-x-3">
{status === playerStatus.PLAYING ? <Player.Pip /> : null}
{/* Disable PiP for iOS PWA */}
{!isIOSPWA &&
(status === playerStatus.PLAYING ? <Player.Pip /> : null)}
<Player.Episodes />
{status === playerStatus.PLAYING ? <Player.Settings /> : null}
{/* Expand button for iOS PWA only */}
{isIOSPWA && status === playerStatus.PLAYING && <Widescreen />}
</div>
<div>
<Player.Fullscreen />
{/* Disable for iOS PWA */}
{!isIOSPWA && (
<div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just do

{!isIOSPWA && (<Player.Fullscreen />)}

<Player.Fullscreen />
</div>
)}
{/* Add info for iOS PWA */}
{isIOSPWA && (
<div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

<IosPwaLimitations />
</div>
)}
</div>
</div>
</Player.BottomControls>
Expand Down