Browse Source

fix: Crash when navigator.serial is undefined (#670)

* Fix crash when navigator.serial is undefined

* Change value

---------

Co-authored-by: philon- <[email protected]>
pull/674/head
Jeremy Gallant 12 months ago
committed by GitHub
parent
commit
bb91350ef5
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 112
      src/components/Dialog/NewDeviceDialog.tsx
  2. 3
      src/i18n/locales/en/dialog.json

112
src/components/Dialog/NewDeviceDialog.tsx

@ -18,9 +18,7 @@ import {
TabsList, TabsList,
TabsTrigger, TabsTrigger,
} from "@components/UI/Tabs.tsx"; } from "@components/UI/Tabs.tsx";
import { Subtle } from "@components/UI/Typography/Subtle.tsx";
import { AlertCircle } from "lucide-react"; import { AlertCircle } from "lucide-react";
import { useMemo } from "react";
import { Trans, useTranslation } from "react-i18next"; import { Trans, useTranslation } from "react-i18next";
import { Link } from "../UI/Typography/Link.tsx"; import { Link } from "../UI/Typography/Link.tsx";
@ -29,6 +27,7 @@ export interface TabElementProps {
} }
export interface TabManifest { export interface TabManifest {
id: "HTTP" | "BLE" | "Serial";
label: string; label: string;
element: React.FC<TabElementProps>; element: React.FC<TabElementProps>;
isDisabled: boolean; isDisabled: boolean;
@ -41,29 +40,28 @@ export interface NewDeviceProps {
interface FeatureErrorProps { interface FeatureErrorProps {
missingFeatures: BrowserFeature[]; missingFeatures: BrowserFeature[];
tabId: "HTTP" | "BLE" | "Serial";
} }
const links: { [key: string]: string } = { const errors: Record<BrowserFeature, { href: string; i18nKey: string }> = {
"Web Bluetooth": "Web Bluetooth": {
"https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API#browser_compatibility", href:
"Web Serial": "https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API#browser_compatibility",
"https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API#browser_compatibility", i18nKey: "newDeviceDialog.validation.requiresWebBluetooth",
"Secure Context": },
"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts", "Web Serial": {
href:
"https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API#browser_compatibility",
i18nKey: "newDeviceDialog.validation.requiresWebSerial",
},
"Secure Context": {
href:
"https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts",
i18nKey: "newDeviceDialog.validation.requiresSecureContext",
},
}; };
const ErrorMessage = ({ missingFeatures }: FeatureErrorProps) => { const ErrorMessage = ({ missingFeatures, tabId }: FeatureErrorProps) => {
const { i18n } = useTranslation("dialog");
const listFormatter = useMemo(
() =>
new Intl.ListFormat(i18n.language, {
style: "long",
type: "disjunction",
}),
[i18n.language],
);
if (missingFeatures.length === 0) return null; if (missingFeatures.length === 0) return null;
const browserFeatures = missingFeatures.filter( const browserFeatures = missingFeatures.filter(
@ -71,37 +69,32 @@ const ErrorMessage = ({ missingFeatures }: FeatureErrorProps) => {
); );
const needsSecureContext = missingFeatures.includes("Secure Context"); const needsSecureContext = missingFeatures.includes("Secure Context");
const formatFeatureList = (features: string[]) => { const needsFeature =
const parts = listFormatter.formatToParts(features); (tabId === "BLE" && browserFeatures.includes("Web Bluetooth"))
return parts.map((part) => { ? "Web Bluetooth"
if (part.type === "element") { : (tabId === "Serial" && browserFeatures.includes("Web Serial"))
return ( ? "Web Serial"
<Link key={part.value} href={links[part.value]}> : undefined;
{part.value}
</Link>
);
}
return <span key={part.value}>{part.value}</span>;
});
};
const featureNodes = formatFeatureList(browserFeatures);
return ( return (
<Subtle className="flex flex-col items-start gap-2 bg-red-500 p-4 rounded-md"> <div className="flex flex-col items-start gap-2 bg-red-500 p-4 rounded-md text-sm text-slate-500 dark:text-slate-400">
<div className="flex items-center gap-2 w-full"> <div className="flex items-center gap-2 w-full">
<AlertCircle size={40} className="mr-2 shrink-0 text-white" /> <AlertCircle size={40} className="mr-2 shrink-0 text-white" />
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<p className="text-sm text-white"> <div className="text-sm text-white">
{browserFeatures.length > 0 && ( {needsFeature && (
<Trans <Trans
i18nKey="newDeviceDialog.validation.requiresFeatures" i18nKey={errors[needsFeature].i18nKey}
components={{ components={[
"0": <>{featureNodes}</>, <Link
}} key="0"
href={errors[needsFeature].href}
className="underline hover:text-slate-200 text-white dark:text-white dark:hover:text-slate-300"
/>,
]}
/> />
)} )}
{browserFeatures.length > 0 && needsSecureContext && " "} {needsFeature && needsSecureContext && " "}
{needsSecureContext && ( {needsSecureContext && (
<Trans <Trans
i18nKey={browserFeatures.length > 0 i18nKey={browserFeatures.length > 0
@ -110,17 +103,17 @@ const ErrorMessage = ({ missingFeatures }: FeatureErrorProps) => {
components={{ components={{
"0": ( "0": (
<Link <Link
href={links["Secure Context"]} href={errors["Secure Context"].href}
className="underline hover:text-slate-200" className="underline hover:text-slate-200"
/> />
), ),
}} }}
/> />
)} )}
</p> </div>
</div> </div>
</div> </div>
</Subtle> </div>
); );
}; };
@ -133,17 +126,20 @@ export const NewDeviceDialog = ({
const tabs: TabManifest[] = [ const tabs: TabManifest[] = [
{ {
id: "HTTP",
label: t("newDeviceDialog.tabHttp"), label: t("newDeviceDialog.tabHttp"),
element: HTTP, element: HTTP,
isDisabled: false, isDisabled: false,
}, },
{ {
id: "BLE",
label: t("newDeviceDialog.tabBluetooth"), label: t("newDeviceDialog.tabBluetooth"),
element: BLE, element: BLE,
isDisabled: unsupported.includes("Web Bluetooth") || isDisabled: unsupported.includes("Web Bluetooth") ||
unsupported.includes("Secure Context"), unsupported.includes("Secure Context"),
}, },
{ {
id: "Serial",
label: t("newDeviceDialog.tabSerial"), label: t("newDeviceDialog.tabSerial"),
element: Serial, element: Serial,
isDisabled: unsupported.includes("Web Serial") || isDisabled: unsupported.includes("Web Serial") ||
@ -161,21 +157,27 @@ export const NewDeviceDialog = ({
<Tabs defaultValue="HTTP"> <Tabs defaultValue="HTTP">
<TabsList> <TabsList>
{tabs.map((tab) => ( {tabs.map((tab) => (
<TabsTrigger key={tab.label} value={tab.label}> <TabsTrigger key={tab.id} value={tab.id}>
{tab.label} {tab.label}
</TabsTrigger> </TabsTrigger>
))} ))}
</TabsList> </TabsList>
{tabs.map((tab) => ( {tabs.map((tab) => (
<TabsContent key={tab.label} value={tab.label}> <TabsContent key={tab.id} value={tab.id}>
<fieldset disabled={tab.isDisabled}> <fieldset disabled={tab.isDisabled}>
{(tab.label !== "HTTP" && {(tab.id !== "HTTP" &&
tab.isDisabled) tab.isDisabled)
? <ErrorMessage missingFeatures={unsupported} /> ? (
: null} <ErrorMessage
<tab.element missingFeatures={unsupported}
closeDialog={() => onOpenChange(false)} tabId={tab.id}
/> />
)
: (
<tab.element
closeDialog={() => onOpenChange(false)}
/>
)}
</fieldset> </fieldset>
</TabsContent> </TabsContent>
))} ))}

3
src/i18n/locales/en/dialog.json

@ -64,7 +64,8 @@
"newDeviceButton": "New device" "newDeviceButton": "New device"
}, },
"validation": { "validation": {
"requiresFeatures": "This connection type requires <0></0>. Please use a supported browser, like Chrome or Edge.", "requiresWebBluetooth": "This connection type requires <0>Web Bluetooth</0>. Please use a supported browser, like Chrome or Edge.",
"requiresWebSerial": "This connection type requires <0>Web Serial</0>. Please use a supported browser, like Chrome or Edge.",
"requiresSecureContext": "This application requires a <0>secure context</0>. Please connect using HTTPS or localhost.", "requiresSecureContext": "This application requires a <0>secure context</0>. Please connect using HTTPS or localhost.",
"additionallyRequiresSecureContext": "Additionally, it requires a <0>secure context</0>. Please connect using HTTPS or localhost." "additionallyRequiresSecureContext": "Additionally, it requires a <0>secure context</0>. Please connect using HTTPS or localhost."
} }

Loading…
Cancel
Save