Clean up instructions
Test / test (push) Failing after 43s Details

This commit is contained in:
Jeff 2024-09-24 23:38:30 -04:00
parent a62fd5ffdb
commit 987b419018
11 changed files with 239 additions and 76 deletions

View File

@ -3,6 +3,7 @@
"tabWidth": 2,
"semi": false,
"singleQuote": false,
"maxLineLength": 120,
"overrides": [
{
"files": [

View File

@ -0,0 +1,61 @@
import React from "react"
import { useEffect } from "react"
import { usePush } from "remix-pwa-monorepo/packages/push/client/hook"
export default function EnableNotifications({
onSubscribe,
}: {
onSubscribe: () => void
}) {
return (
<div>
<h1>Allow Notifications</h1>
<div>
Tack Up Now requires your permission to send notifications in order to
function properly
</div>
<EnableButton onSubscribe={onSubscribe} />
</div>
)
}
function EnableButton({ onSubscribe }: { onSubscribe: () => void }) {
const { subscribeToPush, requestPermission, canSendPush } = usePush()
function subscribe() {
requestPermission()
}
useEffect(() => {
if (!canSendPush) return
subscribeToPush(
urlB64ToUint8Array(applicationServerPublicKey) as any,
(subscription) => {
fetch("/api/subscribe", {
method: "POST",
body: JSON.stringify(subscription),
})
onSubscribe()
}
)
}, [canSendPush])
return <button onClick={subscribe}>Enable Notifications</button>
}
const applicationServerPublicKey =
"BDTbzdtzJxwV0sscdsXla-GKvlcxqQr7edEfkX8-papwvvV1UVc3IMyRacl1BbgTi31nWPji2wKCZkjf1l5iX7Y"
function urlB64ToUint8Array(base64String: string) {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4)
const base64 = (base64String + padding).replace(/\-/g, "+").replace(/_/g, "/")
const rawData = window.atob(base64)
const outputArray = new Uint8Array(rawData.length)
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i)
}
return outputArray
}

View File

@ -0,0 +1,32 @@
import React from "react"
import shareIcon from "../../public/images/safari-share-icon.png"
export default function InstallPWA() {
return (
<div>
<h1>Install Tack Up Now!</h1>
<div>
Install Tack Up Now on your device to get notified when it's time to
tack up
</div>
<div>
<ul>
<span>Tap </span>
<img style={{ width: "2rem" }} src={shareIcon} />
<span> and choose </span>
<span className="bold">Add to Home Screen</span>
</ul>
<ul>
<span>On the next screen, tap </span>
<span className="bold">Add</span>
</ul>
<ul>
<span>Then open </span>
<span className="bold">Tack Up Now</span>
<span> from your home screen</span>
</ul>
</div>
</div>
)
}

View File

@ -0,0 +1,32 @@
import React from "react"
import useInstallState from "../useInstallState"
import EnableNotifications from "./EnableNotifications"
import OpenSafari from "./OpenSafari"
import InstallPWA from "./InstallPWA"
import Unsupported from "./Unsupported"
interface InstallPromptsProps {
isMobileSafari: boolean
isSupported: boolean
notificationsEnabled: boolean
onInstallComplete: () => void
}
export default function InstallPrompts({
isSupported,
isMobileSafari,
onInstallComplete,
}: InstallPromptsProps) {
const { step } = useInstallState({ isSupported, isMobileSafari })
const steps = {
"open safari": <OpenSafari />,
install: <InstallPWA />,
"enable notifications": (
<EnableNotifications onSubscribe={onInstallComplete} />
),
unsupported: <Unsupported />,
}
return steps[step as keyof typeof steps] ?? null
}

View File

@ -0,0 +1,11 @@
import React from "react"
export default function OpenSafari() {
return (
<div>
<div>This device requires Tack Up Now to be installed using Safari</div>
<br />
<div>Open tackupnow.com in Safari to continue!</div>
</div>
)
}

View File

@ -0,0 +1,22 @@
import React, { useState } from "react"
export default function Unsupported() {
const [showWhy, setShowWhy] = useState(false)
return (
<div>
<h1>{"Sorry :("}</h1>
<br />
<div>Your device doesn't support Tack Up Now!</div>
{showWhy ? (
<div>
iOS 16.3 and under does not support notification delivery through web
apps, so Tack Up Now can't send notifications to your device.
</div>
) : (
<button onClick={() => setShowWhy(true)}>Why not?</button>
)}
</div>
)
}

View File

@ -7,6 +7,10 @@ import {
} from "@remix-run/react"
import React from "react"
import { ManifestLink } from "@remix-pwa/sw"
import { LinksFunction } from "@remix-run/node"
import styles from "./styles/styles.css?url"
export const links: LinksFunction = () => [{ rel: "stylesheet", href: styles }]
export function Layout({ children }: { children: React.ReactNode }) {
return (

View File

@ -8,7 +8,8 @@ import React, { Suspense, useEffect, useState } from "react"
import coerceSemver from "semver/functions/coerce"
import versionAtLeast from "semver/functions/gte"
import UAParser from "ua-parser-js"
import { usePush } from "remix-pwa-monorepo/packages/push/client/hook"
import InstallPrompts from "../install/InstallPrompts"
import useInstallState from "../useInstallState"
export const meta: MetaFunction = () => {
return [
@ -21,25 +22,24 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
const userAgent = request.headers.get("user-agent")
const parsedUserAgent = new UAParser(userAgent ?? "")
const os = parsedUserAgent.getOS()
const isNonSafari = parsedUserAgent.getBrowser().name !== "Mobile Safari"
const browser = parsedUserAgent.getBrowser().name
const isMobileSafari = parsedUserAgent.getBrowser().name !== "Mobile Safari"
const isSupported =
os.name !== "iOS" ||
versionAtLeast(coerceSemver(os.version) ?? "0.0.0", "16.4.0")
console.log("Wut", isNonSafari)
return json({ isSupported, isNonSafari, browser })
return json({ isSupported, isMobileSafari })
}
export default function Index() {
const { isSupported, isNonSafari, browser } = useLoaderData<typeof loader>()
const { isSupported, isMobileSafari } = useLoaderData<typeof loader>()
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
<Suspense>
<LandingMessage isSupported={isSupported} isNonSafari={isNonSafari} />
{browser}
<LandingMessage
isSupported={isSupported}
isMobileSafari={isMobileSafari}
/>
</Suspense>
</div>
)
@ -47,86 +47,30 @@ export default function Index() {
function LandingMessage({
isSupported,
isNonSafari,
isMobileSafari,
}: {
isSupported: boolean
isNonSafari: boolean
isMobileSafari: boolean
}) {
const isClient = typeof window !== "undefined"
const notificationsEnabled =
isClient &&
"Notification" in window &&
window.Notification.permission === "granted"
const isRunningPWA =
isClient &&
(("standalone" in navigator && (navigator.standalone as boolean)) ||
matchMedia("(dislay-mode: standalone)").matches)
const { installed } = useInstallState({ isSupported, isMobileSafari })
const [rendered, setRendered] = useState(false)
const [isInstalled, setIsInstalled] = useState(notificationsEnabled)
const [isInstalled, setIsInstalled] = useState(installed)
useEffect(() => {
setRendered(true)
}, [])
return !isClient || !rendered ? (
return !rendered ? (
<div>Loading</div>
) : isInstalled ? (
<div>Your Notifications</div>
) : isNonSafari ? (
<div>Safari</div>
) : isRunningPWA && !notificationsEnabled ? (
<EnableButton onSubscribe={() => setIsInstalled(true)} />
) : isSupported ? (
<div>Install Tack Up Now!</div>
) : (
<div>{"Sorry, your device doesn't support Tack Up Now! :("}</div>
<InstallPrompts
isMobileSafari={isMobileSafari}
isSupported={isSupported}
notificationsEnabled={false}
onInstallComplete={() => setIsInstalled(true)}
/>
)
}
function EnableButton({ onSubscribe }: { onSubscribe: () => void }) {
const { subscribeToPush, requestPermission, canSendPush } = usePush()
function subscribe() {
requestPermission()
}
useEffect(() => {
if (!canSendPush) return
subscribeToPush(
urlB64ToUint8Array(applicationServerPublicKey) as any,
(subscription) => {
fetch("/api/subscribe", {
method: "POST",
body: JSON.stringify(subscription),
})
onSubscribe()
},
(error) => {
const errorDiv = document.createElement("div")
errorDiv.innerText = "Error thingy" + JSON.stringify(error)
document.appendChild(errorDiv)
}
)
}, [canSendPush])
return <button onClick={subscribe}>Enable notifications</button>
}
const applicationServerPublicKey =
"BDTbzdtzJxwV0sscdsXla-GKvlcxqQr7edEfkX8-papwvvV1UVc3IMyRacl1BbgTi31nWPji2wKCZkjf1l5iX7Y"
function urlB64ToUint8Array(base64String: string) {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4)
const base64 = (base64String + padding).replace(/\-/g, "+").replace(/_/g, "/")
const rawData = window.atob(base64)
const outputArray = new Uint8Array(rawData.length)
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i)
}
return outputArray
}

10
app/styles/styles.css Normal file
View File

@ -0,0 +1,10 @@
.bold {
font-weight: bold;
}
ul {
display: flex;
flex-direction: row;
align-items: center;
white-space: pre-wrap;
}

46
app/useInstallState.ts Normal file
View File

@ -0,0 +1,46 @@
import { usePush } from "remix-pwa-monorepo/packages/push/client/hook"
type IOSInstallStep =
| "loading"
| "install"
| "open safari"
| "enable notifications"
| "unsupported"
export default function useInstallState({
isSupported,
isMobileSafari,
}: {
isSupported: boolean
isMobileSafari: boolean
}) {
const isClient = typeof window !== undefined
if (!isClient)
return {
step: isSupported ? "loading" : ("unsupported" as IOSInstallStep),
installed: false,
}
const { canSendPush } = usePush()
const notificationsEnabled =
("Notification" in window &&
window.Notification.permission === "granted") ||
canSendPush
const isRunningPWA =
("standalone" in navigator && (navigator.standalone as boolean)) ||
matchMedia("(dislay-mode: standalone)").matches
return {
step: !isMobileSafari
? "open safari"
: !isRunningPWA && isMobileSafari
? "install"
: !notificationsEnabled
? "enable notifications"
: (null as IOSInstallStep | null),
installed: notificationsEnabled,
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB