Skip to content

Commit 03b39d8

Browse files
Add browser compatibility check
1 parent b84476c commit 03b39d8

File tree

9 files changed

+570
-7
lines changed

9 files changed

+570
-7
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"dependencies": {
1515
"@fishjam-cloud/react-client": "^0.17.0",
1616
"@mediapipe/tasks-vision": "^0.10.22-rc.20250304",
17+
"@radix-ui/react-alert-dialog": "^1.1.14",
1718
"@radix-ui/react-separator": "^1.1.7",
1819
"@radix-ui/react-slot": "^1.2.3",
1920
"@swmansion/smelter": "^0.2.1",
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { ScreenShareOff } from "lucide-react";
2+
import {
3+
AlertDialog,
4+
AlertDialogAction,
5+
AlertDialogContent,
6+
AlertDialogDescription,
7+
AlertDialogFooter,
8+
AlertDialogHeader,
9+
AlertDialogTitle,
10+
} from "./ui/alert-dialog";
11+
12+
export default function BrowserSupportAlert() {
13+
return (
14+
<AlertDialog defaultOpen={true}>
15+
<AlertDialogContent className="font-june">
16+
<AlertDialogHeader>
17+
<AlertDialogTitle asChild>
18+
<div className="inline-flex gap-2">
19+
<ScreenShareOff /> Unsupported browser
20+
</div>
21+
</AlertDialogTitle>
22+
<AlertDialogDescription>
23+
Gesture recognition won't work on your browser, but is coming soon.
24+
You will still see gestures made by other people.
25+
</AlertDialogDescription>
26+
</AlertDialogHeader>
27+
<AlertDialogFooter>
28+
<AlertDialogAction>OK</AlertDialogAction>
29+
</AlertDialogFooter>
30+
</AlertDialogContent>
31+
</AlertDialog>
32+
);
33+
}

src/components/PeerTile.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Loader2 } from "lucide-react";
22
import { FC, useEffect, useRef } from "react";
33

44
export type PeerTileProps = {
5-
stream: MediaStream | null;
5+
stream?: MediaStream | null;
66
name: string;
77
showHelp?: boolean;
88
};
@@ -12,7 +12,7 @@ export const PeerTile: FC<PeerTileProps> = ({ stream, name }) => {
1212

1313
useEffect(() => {
1414
if (!videoRef.current) return;
15-
videoRef.current.srcObject = stream;
15+
videoRef.current.srcObject = stream ?? null;
1616
}, [stream]);
1717

1818
return (

src/components/SmelterProvider.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import { FC, PropsWithChildren, useEffect, useState } from "react";
22
import { SmelterContext } from "../contexts/SmelterContext";
33
import Smelter from "@swmansion/smelter-web-wasm";
4+
import BrowserSupportAlert from "./BrowserSupportAlert";
5+
import { browserSupported } from "@/lib/utils";
46

57
export const SmelterProvider: FC<PropsWithChildren> = ({ children }) => {
68
const [smelter, setSmelter] = useState<Smelter | null>(null);
79

810
useEffect(() => {
11+
if (!browserSupported) return;
12+
913
const smelter = new Smelter();
1014

1115
let cancel = false;
@@ -28,6 +32,7 @@ export const SmelterProvider: FC<PropsWithChildren> = ({ children }) => {
2832

2933
return (
3034
<SmelterContext.Provider value={smelter}>
35+
{!browserSupported && <BrowserSupportAlert />}
3136
{children}
3237
</SmelterContext.Provider>
3338
);

src/components/ui/alert-dialog.tsx

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import * as React from "react";
2+
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
3+
4+
import { cn } from "@/lib/utils";
5+
import { buttonVariants } from "@/components/ui/button";
6+
7+
function AlertDialog({
8+
...props
9+
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
10+
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
11+
}
12+
13+
function AlertDialogTrigger({
14+
...props
15+
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
16+
return (
17+
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
18+
);
19+
}
20+
21+
function AlertDialogPortal({
22+
...props
23+
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
24+
return (
25+
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
26+
);
27+
}
28+
29+
const AlertDialogOverlay = React.forwardRef<
30+
HTMLDivElement,
31+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
32+
>(({ className, ...props }, ref) => {
33+
return (
34+
<AlertDialogPrimitive.Overlay
35+
data-slot="alert-dialog-overlay"
36+
className={cn(
37+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
38+
className,
39+
)}
40+
ref={ref}
41+
{...props}
42+
/>
43+
);
44+
});
45+
46+
function AlertDialogContent({
47+
className,
48+
...props
49+
}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
50+
return (
51+
<AlertDialogPortal>
52+
<AlertDialogOverlay />
53+
<AlertDialogPrimitive.Content
54+
data-slot="alert-dialog-content"
55+
className={cn(
56+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-stone-200 bg-white p-6 shadow-lg duration-200 sm:max-w-lg dark:border-stone-800 dark:bg-stone-950",
57+
className,
58+
)}
59+
{...props}
60+
/>
61+
</AlertDialogPortal>
62+
);
63+
}
64+
65+
function AlertDialogHeader({
66+
className,
67+
...props
68+
}: React.ComponentProps<"div">) {
69+
return (
70+
<div
71+
data-slot="alert-dialog-header"
72+
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
73+
{...props}
74+
/>
75+
);
76+
}
77+
78+
function AlertDialogFooter({
79+
className,
80+
...props
81+
}: React.ComponentProps<"div">) {
82+
return (
83+
<div
84+
data-slot="alert-dialog-footer"
85+
className={cn(
86+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
87+
className,
88+
)}
89+
{...props}
90+
/>
91+
);
92+
}
93+
94+
function AlertDialogTitle({
95+
className,
96+
...props
97+
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
98+
return (
99+
<AlertDialogPrimitive.Title
100+
data-slot="alert-dialog-title"
101+
className={cn("text-lg font-semibold", className)}
102+
{...props}
103+
/>
104+
);
105+
}
106+
107+
function AlertDialogDescription({
108+
className,
109+
...props
110+
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
111+
return (
112+
<AlertDialogPrimitive.Description
113+
data-slot="alert-dialog-description"
114+
className={cn("text-sm text-stone-500 dark:text-stone-400", className)}
115+
{...props}
116+
/>
117+
);
118+
}
119+
120+
function AlertDialogAction({
121+
className,
122+
...props
123+
}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
124+
return (
125+
<AlertDialogPrimitive.Action
126+
className={cn(buttonVariants(), className)}
127+
{...props}
128+
/>
129+
);
130+
}
131+
132+
function AlertDialogCancel({
133+
className,
134+
...props
135+
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
136+
return (
137+
<AlertDialogPrimitive.Cancel
138+
className={cn(buttonVariants({ variant: "outline" }), className)}
139+
{...props}
140+
/>
141+
);
142+
}
143+
144+
export {
145+
AlertDialog,
146+
AlertDialogPortal,
147+
AlertDialogOverlay,
148+
AlertDialogTrigger,
149+
AlertDialogContent,
150+
AlertDialogHeader,
151+
AlertDialogFooter,
152+
AlertDialogTitle,
153+
AlertDialogDescription,
154+
AlertDialogAction,
155+
AlertDialogCancel,
156+
};

src/components/ui/button.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
55
import { cn } from "@/lib/utils";
66

77
const buttonVariants = cva(
8-
"inline-flex gap-2 items-center justify-center whitespace-nowrap rounded-full text-xs sm:text-sm md:text-base font-june transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
8+
"inline-flex gap-2 items-center justify-center whitespace-nowrap rounded-full font-june transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
99
{
1010
variants: {
1111
variant: {
@@ -15,7 +15,7 @@ const buttonVariants = cva(
1515
},
1616
size: {
1717
default: "px-6 h-12",
18-
responsive: "px-2 h-8 sm:px-6 sm:h-12",
18+
responsive: "px-2 h-8 sm:px-6 sm:h-12 text-xs sm:text-sm md:text-base ",
1919
},
2020
},
2121
defaultVariants: {

src/lib/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ import { twMerge } from "tailwind-merge";
44
export function cn(...inputs: ClassValue[]) {
55
return twMerge(clsx(inputs));
66
}
7+
8+
export const browserSupported = "MediaStreamTrackProcessor" in window;

src/views/RoomView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ export default function RoomView() {
2626
<section
2727
className={`align-center grid w-full flex-1 grid-flow-row grid-cols-1 justify-center md:grid-cols-${cols} gap-4 lg:gap-8`}
2828
>
29-
<PeerTile name="You" stream={stream ?? null} showHelp />
29+
<PeerTile name="You" stream={stream ?? cameraStream} showHelp />
3030
{remotePeers.map((peer) => (
3131
<PeerTile
3232
name={peer.metadata?.peer?.name ?? peer.id}
3333
key={peer.id}
34-
stream={peer.customVideoTracks[0]?.stream}
34+
stream={peer.customVideoTracks[0]?.stream ?? peer.cameraTrack?.stream}
3535
/>
3636
))}
3737
</section>

0 commit comments

Comments
 (0)