diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 53ab7e85..c5469c0e 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -33,6 +33,8 @@ jobs: # 👇 Build steps - name: Set up Node.js uses: actions/setup-node@v4 + with: + node-version: "lts/*" - name: Use corepack run: corepack enable - name: Install dependencies diff --git a/.github/workflows/static_check.yml b/.github/workflows/static_check.yml index 6559d107..6ff2b398 100644 --- a/.github/workflows/static_check.yml +++ b/.github/workflows/static_check.yml @@ -7,8 +7,18 @@ jobs: steps: - name: checkout uses: actions/checkout@v4 + with: + submodules: "recursive" + - name: init and update submodules + run: git submodule update --init --recursive + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "lts/*" - name: Use corepack run: corepack enable + - name: Run prepare + run: scripts/prepare.sh - name: Install node dependencies run: yarn --immutable - name: Spellcheck @@ -17,7 +27,5 @@ jobs: run: yarn format:check - name: Check types run: yarn typecheck - - name: Fetch packages - run: yarn prepare - name: Check build run: yarn build diff --git a/docs/audio-calls.mdx b/docs/audio-calls.mdx index bdac4017..eb03f1c0 100644 --- a/docs/audio-calls.mdx +++ b/docs/audio-calls.mdx @@ -27,6 +27,12 @@ Using this feature is as easy as setting the `roomType` field to `audio_only` wh ```ts + import { FishjamClient } from '@fishjam-cloud/js-server-sdk'; + const fishjamClient = new FishjamClient({ + fishjamUrl: "aaa", + managementToken: "bbb", + }); + // ---cut--- const createdRoom = await fishjamClient.createRoom({ roomType: 'audio_only' }); ``` diff --git a/docs/livestreaming.mdx b/docs/livestreaming.mdx index fa185873..2281817f 100644 --- a/docs/livestreaming.mdx +++ b/docs/livestreaming.mdx @@ -29,6 +29,12 @@ https://fishjam.io/api/v1/connect/${ROOM_MANAGER_ID}/room-manager?roomName=foo&p ```ts + import { FishjamClient } from '@fishjam-cloud/js-server-sdk'; + const fishjamClient = new FishjamClient({ + fishjamUrl: "aaa", + managementToken: "bbb", + }); + // ---cut--- const createdRoom = await fishjamClient.createRoom({ roomType: 'livestream' }); const peer = await fishjamClient.createPeer(createdRoom.id) @@ -71,6 +77,14 @@ https://fishjam.io/api/v1/connect/${ROOM_MANAGER_ID}/room-manager//li ```ts + import { FishjamClient } from '@fishjam-cloud/js-server-sdk'; + const fishjamClient = new FishjamClient({ + fishjamUrl: "aaa", + managementToken: "bbb", + }); + const room = await fishjamClient.createRoom({ roomType: 'livestream' }); + // ---cut--- + const viewerToken = await fishjamClient.createLivestreamViewerToken(room.id) ``` diff --git a/docs/production/examples/fastify.mdx b/docs/production/examples/fastify.mdx index 11cb8859..7f4d9604 100644 --- a/docs/production/examples/fastify.mdx +++ b/docs/production/examples/fastify.mdx @@ -16,7 +16,7 @@ If you wish to see more general info, visit [**set up your server**](/production Use [`@fastify/env` package](https://github.com/fastify/fastify-env) to load and set environment variables in your Fastify instance. ```ts title='main.ts' -import { Fastify } from "fastify"; +import Fastify from "fastify"; import fastifyEnv from "@fastify/env"; const fastify = Fastify(); @@ -55,13 +55,17 @@ import { FishjamClient } from "@fishjam-cloud/js-server-sdk"; declare module "fastify" { interface FastifyInstance { fishjam: FishjamClient; + config: { + FISHJAM_URL: string; + FISHJAM_MANAGEMENT_TOKEN: string; + }; } } export const fishjamPlugin = fastifyPlugin((fastify) => { const fishjamClient = new FishjamClient({ fishjamUrl: fastify.config.FISHJAM_URL, - managementToken: fastify.config.FISHJAM_SERVER_TOKEN, + managementToken: fastify.config.FISHJAM_MANAGEMENT_TOKEN, }); fastify.decorate("fishjam", fishjamClient); @@ -71,13 +75,32 @@ export const fishjamPlugin = fastifyPlugin((fastify) => { Now, after registering the plugin, we will be able to use Fishjam client by accessing the `fastify.fishjam` property. ```ts title='main.ts' -import { Fastify } from "fastify"; -import fastifyEnv from "@fastify/env"; +import fastifyPlugin from "fastify-plugin"; +import { FishjamClient } from "@fishjam-cloud/js-server-sdk"; +import Fastify from "fastify"; -import { fishjamPlugin } from "./fishjamPlugin"; +declare module "fastify" { + interface FastifyInstance { + fishjam: FishjamClient; + config: { + FISHJAM_URL: string; + FISHJAM_MANAGEMENT_TOKEN: string; + }; + } +} + +export const fishjamPlugin = fastifyPlugin((fastify) => { + const fishjamClient = new FishjamClient({ + fishjamUrl: fastify.config.FISHJAM_URL, + managementToken: fastify.config.FISHJAM_MANAGEMENT_TOKEN, + }); + + fastify.decorate("fishjam", fishjamClient); +}); const fastify = Fastify(); -// ... skipping env loading code for convenience + +// ---cut--- await fastify.register(fishjamPlugin); fastify.get("/rooms", () => fastify.fishjam.getAllRooms()); @@ -97,7 +120,7 @@ To receive and parse the Fishjam protobuf messages, add a content type parser to Then, you will be able to access the parsed message at `request.Body`. ```ts title='main.ts' -import { Fastify } from "fastify"; +import Fastify, { FastifyRequest } from "fastify"; import { ServerMessage } from "@fishjam-cloud/js-server-sdk/proto"; const fastify = Fastify(); @@ -105,12 +128,13 @@ const fastify = Fastify(); fastify.addContentTypeParser( "application/x-protobuf", { parseAs: "buffer" }, - async (_: FastifyRequest, body: Buffer) => ServerMessage.decode(body), + async (_: FastifyRequest, body: Buffer) => + ServerMessage.decode(new Uint8Array(body)), ); fastify.post<{ Body: ServerMessage }>("/fishjam-webhook", (request) => { // handle the message - console.log(request.Body); + console.log(request.body); }); ``` @@ -122,6 +146,32 @@ Let's create another plugin in `fishjamNotifierPlugin.ts` file. In this case, we don't need to extend the Fastify instance type, because the plugin doesn't decorate the Fastify instance with any properties. It will work in the background. ```ts title='fishjamNotifierPlugin.ts' +import fastifyPlugin from "fastify-plugin"; +import { FishjamClient } from "@fishjam-cloud/js-server-sdk"; +import Fastify from "fastify"; + +declare module "fastify" { + interface FastifyInstance { + fishjam: FishjamClient; + config: { + FISHJAM_URL: string; + FISHJAM_MANAGEMENT_TOKEN: string; + }; + } +} + +export const fishjamPlugin = fastifyPlugin((fastify) => { + const fishjamClient = new FishjamClient({ + fishjamUrl: fastify.config.FISHJAM_URL, + managementToken: fastify.config.FISHJAM_MANAGEMENT_TOKEN, + }); + + fastify.decorate("fishjam", fishjamClient); +}); + +const fastify = Fastify(); + +// ---cut--- import { type FastifyInstance } from "fastify"; import fp from "fastify-plugin"; import { FishjamWSNotifier } from "@fishjam-cloud/js-server-sdk"; @@ -132,31 +182,59 @@ export const fishjamNotifierPlugin = fp((fastify) => { const fishjamNotifier = new FishjamWSNotifier( { fishjamUrl, managementToken }, - onError: (err) => fastify.log.error(err), - onClose: () => fastify.log.info("Websocket connection to Fishjam closed"), - onConnectionFailed: () => fastify.log.error("Failed to connect Fishjam notifier") + (err) => fastify.log.error(err), + () => fastify.log.info("Websocket connection to Fishjam closed"), + () => fastify.log.error("Failed to connect Fishjam notifier"), ); // handle the messages const handleRoomCreated = console.log; - const handlePeerCreated = console.log; + const handlePeerAdded = console.log; fishjamNotifier.on("roomCreated", handleRoomCreated); - fishjamNotifier.on("peerCreated", handlePeerCreated); + fishjamNotifier.on("peerAdded", handlePeerAdded); }); ``` Don't forget to register your plugin. ```ts title='main.ts' -import { Fastify } from "fastify"; -import fastifyEnv from "@fastify/env"; +import fastifyPlugin from "fastify-plugin"; +import { FishjamClient } from "@fishjam-cloud/js-server-sdk"; +import Fastify from "fastify"; +import { FishjamWSNotifier } from "@fishjam-cloud/js-server-sdk"; + +declare module "fastify" { + interface FastifyInstance { + fishjam: FishjamClient; + config: { + FISHJAM_URL: string; + FISHJAM_MANAGEMENT_TOKEN: string; + }; + } +} + +export const fishjamPlugin = fastifyPlugin((fastify) => { + const fishjamClient = new FishjamClient({ + fishjamUrl: "aaa", + managementToken: "bbb", + }); -import { fishjamPlugin } from "./fishjamPlugin"; -import { fishjamNotifierPlugin } from "./fishjamNotifierPlugin"; + fastify.decorate("fishjam", fishjamClient); +}); const fastify = Fastify(); -// ... + +const fishjamNotifierPlugin = fastifyPlugin((fastify) => { + const fishjamNotifier = new FishjamWSNotifier( + { fishjamUrl: "aaa", managementToken: "bbb" }, + (err) => fastify.log.error(err), + () => fastify.log.info("Websocket connection to Fishjam closed"), + () => fastify.log.error("Failed to connect Fishjam notifier"), + ); +}); +// ---cut--- + await fastify.register(fishjamPlugin); await fastify.register(fishjamNotifierPlugin); ``` diff --git a/docs/production/server.mdx b/docs/production/server.mdx index e0715e3f..5c3f89f9 100644 --- a/docs/production/server.mdx +++ b/docs/production/server.mdx @@ -56,6 +56,10 @@ They are required to proceed. Now, we are ready to dive into the code. ```ts + process.env.FISHJAM_URL = "https://fishjam.io"; + process.env.FISHJAM_MANAGEMENT_TOKEN = "bbb"; + // ---cut--- + import { FishjamClient } from '@fishjam-cloud/js-server-sdk'; const fishjamUrl = process.env.FISHJAM_URL; @@ -89,6 +93,13 @@ Create a room to get the `roomId` and be able to start adding peers. ```ts + import { FishjamClient } from '@fishjam-cloud/js-server-sdk'; + const fishjamClient = new FishjamClient({ + + fishjamUrl: "https://fishjam.io", + managementToken: "bbb", + }); + // ---cut--- const createdRoom = await fishjamClient.createRoom(); const theSameRoom = await fishjamClient.getRoom(createdRoom.id); @@ -125,9 +136,16 @@ At any time you can terminate user's access by deleting the peer. ```ts - const { peer, peerToken } = await fishjamClient.createPeer(roomId); + import { FishjamClient } from '@fishjam-cloud/js-server-sdk'; + const fishjamClient = new FishjamClient({ + fishjamUrl: "https://fishjam.io", + managementToken: "bbb", + }); + const created_room = await fishjamClient.createRoom(); + // ---cut--- + const { peer, peerToken } = await fishjamClient.createPeer(created_room.id); - await fishjamClient.deletePeer(roomId, peer.id); + await fishjamClient.deletePeer(created_room.id, peer.id); ``` @@ -153,7 +171,14 @@ or [web SDK](/react/metadata). This metadata can be only set when creating the p ```ts - const { peer, peerToken } = await fishjamClient.createPeer(roomId, { + import { FishjamClient } from '@fishjam-cloud/js-server-sdk'; + const fishjamClient = new FishjamClient({ + fishjamUrl: "https://fishjam.io", + managementToken: "bbb", + }); + const created_room = await fishjamClient.createRoom(); + // ---cut--- + const { peer, peerToken } = await fishjamClient.createPeer(created_room.id, { metadata: { realName: 'Keanu Reeves' }, }); ``` @@ -186,6 +211,13 @@ Simply pass the your webhook url as a `webhookUrl` parameter when creating a roo ```ts + import { FishjamClient } from '@fishjam-cloud/js-server-sdk'; + + const fishjamClient = new FishjamClient({ + fishjamUrl: "https://fishjam.io", + managementToken: "bbb", + }); + // ---cut--- const webhookUrl = "https://example.com/"; await fishjamClient.createRoom({ webhookUrl }); ``` @@ -215,6 +247,9 @@ It sets up a websocket connection with a Fishjam instance and provides a simple ```ts + const fishjamUrl = "https://fishjam.io"; + const managementToken = "bbb"; + // ---cut--- import { FishjamWSNotifier } from '@fishjam-cloud/js-server-sdk'; const onClose = console.log; diff --git a/docs/react-native/background.mdx b/docs/react-native/background.mdx index 75640809..129addfc 100644 --- a/docs/react-native/background.mdx +++ b/docs/react-native/background.mdx @@ -61,17 +61,26 @@ You need to modify `AndroidManifest.xml` file and add below service: -You can use [`useForegroundService`](/api/mobile/functions/useForegroundService) hook to handle how foreground service behaves on Android. +You can use [`useForegroundService`](/api/mobile/variables/useForegroundService) hook to handle how foreground service behaves on Android. :::important[Permissions] If you want to use [`enableCamera`](/api/mobile/type-aliases/ForegroundServiceConfig#enablecamera) or [`enableMicrophone`](/api/mobile/type-aliases/ForegroundServiceConfig#enablemicrophone), -user must first grant permission for this resource. [`useForegroundService`](/api/mobile/functions/useForegroundService) will check if permission is +user must first grant permission for this resource. [`useForegroundService`](/api/mobile/variables/useForegroundService) will check if permission is granted and only then allow to start a service. ::: ```tsx +import { + useForegroundService, + useCamera, + useMicrophone, +} from "@fishjam-cloud/react-native-client"; + +const { isCameraOn } = useCamera(); +const { isMicrophoneOn } = useMicrophone(); + useForegroundService({ channelId: "io.fishjam.example.fishjamchat.foregroundservice.channel", channelName: "Fishjam Chat Notifications", diff --git a/docs/react-native/connecting.mdx b/docs/react-native/connecting.mdx index 23d531e9..626b0515 100644 --- a/docs/react-native/connecting.mdx +++ b/docs/react-native/connecting.mdx @@ -25,6 +25,10 @@ the Room's Peer, and return the token required to use that Room. To use that, simply call `fetch`: ```ts +const ROOM_MANAGER_ID = "..."; // Check https://fishjam.io/app/ for your Room Manager ID +const roomName = "room"; +const peerName = "user"; +// ---cut--- const response = await fetch( `https://fishjam.io/api/v1/connect/${ROOM_MANAGER_ID}room-manager/?roomName=${roomName}&peerName=${peerName}`, ); @@ -46,7 +50,7 @@ follow our [server setup instructions](/production/server). In order to connect, call [`joinRoom`](/api/mobile/functions/useConnection#joinroom) method with data from the previous step: ```tsx -import { useCallback } from "react"; +import React, { useCallback } from "react"; import { Button } from "react-native"; import { useConnection } from "@fishjam-cloud/react-native-client"; @@ -54,14 +58,12 @@ import { useConnection } from "@fishjam-cloud/react-native-client"; const ROOM_MANAGER_ID = "..."; export function JoinRoomButton() { - // highlight-next-line - const { joinRoom } = useConnection(); + const { joinRoom } = useConnection(); // [!code highlight] const onPressJoin = useCallback(async () => { const { url, peerToken } = await getRoomDetails("Room", "User"); - // highlight-next-line - await joinRoom({ url, peerToken }); + await joinRoom({ url, peerToken }); // [!code highlight] }, [joinRoom]); return ; } - ``` diff --git a/docs/react/custom-sources.mdx b/docs/react/custom-sources.mdx index 7302e74b..fa95a5d1 100644 --- a/docs/react/custom-sources.mdx +++ b/docs/react/custom-sources.mdx @@ -27,18 +27,15 @@ To create a custom source, you only need to do two things: #### Usage Example ```tsx +import React, { useEffect } from "react"; import { useCustomSource } from "@fishjam-cloud/react-client"; -export function CameraControl() { - const stream: MediaStream = ... - const { setStream } = useCustomSource("my-custom-source"); - - useEffect(() => { - setStream(stream); - }, [stream, setStream]); +const stream: MediaStream | null = null; // Replace with your MediaStream object +const { setStream } = useCustomSource("my-custom-source"); - ... -} +useEffect(() => { + setStream(stream); +}, [stream, setStream]); ``` ### Using a created custom source @@ -53,8 +50,10 @@ If you wish to remove a custom source, then you should call the [`setStream`](/a #### Usage Example ```tsx +import { useCustomSource } from "@fishjam-cloud/react-client"; +// ---cut--- const { setStream } = useCustomSource("my-custom-source"); -... +// ... await setStream(null); ``` @@ -71,7 +70,7 @@ This is particularly useful if you are using [Three.js](https://threejs.org/) or #### Usage Example ```tsx -import { useCallback, useState } from "react"; +import React, { useCallback, useState } from "react"; export function CanvasExample() { const [canvasStream, setCanvasStream] = useState(); @@ -100,12 +99,22 @@ If you want to see a full example React app which uses Fishjam with Smelter, the ::: ```tsx -const { stream } = await smelter.registerOutput("example-output", , { - type: "stream", - video: { - resolution: { width: 1920, height: 1080 }, - }, -}); +import React from "react"; +import Smelter, { setWasmBundleUrl } from "@swmansion/smelter-web-wasm"; +import { View } from "@swmansion/smelter"; + +setWasmBundleUrl("/assets/smelter.wasm"); + +async function run() { + const smelter = new Smelter({ framerate: 30 }); + const { stream } = await smelter.registerOutput("example-output", , { + type: "stream", + video: { + resolution: { width: 1920, height: 1080 }, + }, + }); + await smelter.init(); +} ``` ### Video/Audio HTML tag @@ -118,19 +127,26 @@ If you have a `