diff --git a/examples/mobile-client/fishjam-chat/.eslintrc.js b/examples/mobile-client/fishjam-chat/.eslintrc.js index 7f365dedc..9d8402ae0 100644 --- a/examples/mobile-client/fishjam-chat/.eslintrc.js +++ b/examples/mobile-client/fishjam-chat/.eslintrc.js @@ -1,4 +1,5 @@ module.exports = { + root: true, extends: ['expo'], ignorePatterns: [ 'dist/*', diff --git a/examples/mobile-client/fishjam-chat/app.json b/examples/mobile-client/fishjam-chat/app.json index a336812d3..b44374e14 100644 --- a/examples/mobile-client/fishjam-chat/app.json +++ b/examples/mobile-client/fishjam-chat/app.json @@ -71,9 +71,7 @@ } } ], - [ - "../common/plugins/build/withLocalWebrtcPaths.js" - ] + ["../common/plugins/build/withLocalWebrtcPaths.js"] ], "experiments": { "typedRoutes": true diff --git a/examples/mobile-client/fishjam-chat/utils/tracks.ts b/examples/mobile-client/fishjam-chat/utils/tracks.ts index 762da9270..f9b7b68ff 100644 --- a/examples/mobile-client/fishjam-chat/utils/tracks.ts +++ b/examples/mobile-client/fishjam-chat/utils/tracks.ts @@ -1,4 +1,8 @@ -import type { PeerId, PeerWithTracks, Track } from '@fishjam-cloud/react-native-client'; +import type { + PeerId, + PeerWithTracks, + Track, +} from '@fishjam-cloud/react-native-client'; export type GridTrack = { track: Track | null; diff --git a/packages/mobile-client/src/index.ts b/packages/mobile-client/src/index.ts index 470d74830..b63dbf526 100644 --- a/packages/mobile-client/src/index.ts +++ b/packages/mobile-client/src/index.ts @@ -13,7 +13,6 @@ import React from 'react'; import { FishjamProvider as ReactClientFishjamProvider, type FishjamProviderProps as ReactClientFishjamProviderProps, - useMicrophone as useMicrophoneReactClient, } from '@fishjam-cloud/react-client'; import { FishjamClient } from '@fishjam-cloud/ts-client'; @@ -42,44 +41,48 @@ export { useForegroundService, type ForegroundServiceConfig } from './useForegro export { useCameraPermissions, useMicrophonePermissions, type PermissionStatus } from './hooks/usePermissions'; export { - useCamera, - useInitializeDevices, InitializeDevicesSettings, useConnection, - useCustomSource, useDataChannel, - useLivestreamStreamer, - useLivestreamViewer, - usePeers, useSandbox, - useScreenShare, useUpdatePeerMetadata, useVAD, Variant, } from '@fishjam-cloud/react-client'; -export const useMicrophone = useMicrophoneReactClient as () => Omit< - ReturnType, - 'toggleMicrophoneMute' ->; +export { + useCamera, + useInitializeDevices, + useMicrophone, + useScreenShare, + useCustomSource, + useLivestreamStreamer, + useLivestreamViewer, + usePeers, +} from './overrides/hooks'; export type { - UseInitializeDevicesParams, - JoinRoomConfig, - ConnectStreamerConfig, StreamerInputs, + ConnectStreamerConfig, UseLivestreamStreamerResult, - ConnectViewerConfig, UseLivestreamViewerResult, + Track, + CustomSource, + InitializeDevicesResult, PeerWithTracks, + TrackFields, +} from './overrides/types'; + +export type { + UseInitializeDevicesParams, + JoinRoomConfig, + ConnectViewerConfig, RoomType, UseSandboxProps, BandwidthLimits, Brand, - CustomSource, DeviceError, DeviceItem, - InitializeDevicesResult, InitializeDevicesStatus, MiddlewareResult, PeerId, @@ -87,7 +90,6 @@ export type { PersistLastDeviceHandlers, SimulcastBandwidthLimits, StreamConfig, - Track, TrackId, TrackMiddleware, TracksMiddleware, diff --git a/packages/mobile-client/src/overrides/RTCView.tsx b/packages/mobile-client/src/overrides/RTCView.tsx index 01787e1ad..9cbe15dc2 100644 --- a/packages/mobile-client/src/overrides/RTCView.tsx +++ b/packages/mobile-client/src/overrides/RTCView.tsx @@ -4,21 +4,20 @@ import type React from 'react'; import { useMemo } from 'react'; export type RTCVideoViewProps = Omit, 'streamURL'> & { - mediaStream: MediaStream; + mediaStream: RNMediaStream; }; export type RTCPIPViewProps = Omit, 'streamURL'> & { - mediaStream: MediaStream; + mediaStream: RNMediaStream; }; -const convertMediaStreamToURL = (mediaStream: MediaStream | undefined): string | undefined => { - const rnMediaStream = mediaStream as unknown as RNMediaStream; - if (rnMediaStream && typeof rnMediaStream.toURL === 'function') { - return rnMediaStream.toURL(); +const convertMediaStreamToURL = (mediaStream: RNMediaStream | undefined): string | undefined => { + if (mediaStream && typeof mediaStream.toURL === 'function') { + return mediaStream.toURL(); } else { console.error( 'mediaStream.toURL is not a function. Make sure to use the MediaStream type from @fishjam-cloud/react-native-webrtc', - rnMediaStream, + mediaStream, ); return undefined; } diff --git a/packages/mobile-client/src/overrides/hooks.ts b/packages/mobile-client/src/overrides/hooks.ts new file mode 100644 index 000000000..81df6fb82 --- /dev/null +++ b/packages/mobile-client/src/overrides/hooks.ts @@ -0,0 +1,81 @@ +import { + useCamera as useCameraReactClient, + useCustomSource as useCustomSourceReactClient, + useInitializeDevices as useInitializeDevicesReactClient, + useLivestreamStreamer as useLivestreamStreamerReactClient, + useLivestreamViewer as useLivestreamViewerReactClient, + useMicrophone as useMicrophoneReactClient, + usePeers as usePeersReactClient, + useScreenShare as useScreenShareReactClient, +} from '@fishjam-cloud/react-client'; +import type { MediaStream as RNMediaStream } from '@fishjam-cloud/react-native-webrtc'; +import { useCallback } from 'react'; + +import type { + ConnectStreamerConfig, + InitializeDevicesResult, + PeerWithTracks, + UseLivestreamStreamerResult, + UseLivestreamViewerResult, +} from './types'; + +export const useCamera = useCameraReactClient as () => Omit, 'cameraStream'> & { + cameraStream: RNMediaStream | null; +}; + +export const useMicrophone = useMicrophoneReactClient as () => Omit< + ReturnType, + 'toggleMicrophoneMute' | 'microphoneStream' +> & { + microphoneStream: RNMediaStream | null; +}; + +export const useScreenShare = useScreenShareReactClient as () => Omit< + ReturnType, + 'stream' +> & { + stream: RNMediaStream | null; +}; + +export const useCustomSource = useCustomSourceReactClient as ( + sourceId: T, +) => Omit, 'stream' | 'setStream'> & { + stream: RNMediaStream | undefined; + setStream: (newStream: RNMediaStream | null) => void; +}; + +export function useLivestreamStreamer(): UseLivestreamStreamerResult { + const { connect: reactConnect, ...rest } = useLivestreamStreamerReactClient(); + + const connect = useCallback( + async (config: ConnectStreamerConfig, urlOverride?: string) => { + // @ts-expect-error RNMediaStream is MediaStream at runtime via webrtc polyfill + await reactConnect(config, urlOverride); + }, + [reactConnect], + ); + + return { ...rest, connect }; +} + +export function useLivestreamViewer(): UseLivestreamViewerResult { + const { stream, ...rest } = useLivestreamViewerReactClient(); + return { + ...rest, + stream: stream as unknown as RNMediaStream | null, + }; +} + +export const useInitializeDevices = useInitializeDevicesReactClient as () => { + initializeDevices: ( + ...args: Parameters['initializeDevices']> + ) => Promise; +}; + +export function usePeers

, S = Record>() { + return usePeersReactClient() as unknown as { + localPeer: PeerWithTracks | null; + remotePeers: PeerWithTracks[]; + peers: PeerWithTracks[]; + }; +} diff --git a/packages/mobile-client/src/overrides/types.ts b/packages/mobile-client/src/overrides/types.ts new file mode 100644 index 000000000..9761f4eca --- /dev/null +++ b/packages/mobile-client/src/overrides/types.ts @@ -0,0 +1,53 @@ +import type { + CustomSource as ReactClientCustomSource, + InitializeDevicesResult as ReactClientInitializeDevicesResult, + PeerWithTracks as ReactClientPeerWithTracks, + Track as ReactClientTrack, + UseLivestreamStreamerResult as ReactClientUseLivestreamStreamerResult, + UseLivestreamViewerResult as ReactClientUseLivestreamViewerResult, +} from '@fishjam-cloud/react-client'; +import type { MediaStream as RNMediaStream } from '@fishjam-cloud/react-native-webrtc'; + +export type StreamerInputs = + | { video: RNMediaStream; audio?: RNMediaStream | null } + | { video?: null; audio: RNMediaStream }; + +export type ConnectStreamerConfig = { + inputs: StreamerInputs; + token: string; +}; + +export type UseLivestreamStreamerResult = Omit & { + connect: (config: ConnectStreamerConfig, urlOverride?: string) => Promise; +}; + +export type UseLivestreamViewerResult = Omit & { + stream: RNMediaStream | null; +}; + +export type Track = Omit & { stream: RNMediaStream | null }; + +export type CustomSource = Omit, 'stream'> & { stream?: RNMediaStream }; + +export type InitializeDevicesResult = Omit & { + stream: RNMediaStream | null; +}; + +export type TrackFields = + | 'tracks' + | 'cameraTrack' + | 'microphoneTrack' + | 'screenShareVideoTrack' + | 'screenShareAudioTrack' + | 'customVideoTracks' + | 'customAudioTracks'; + +export type PeerWithTracks = Omit, TrackFields> & { + tracks: Track[]; + cameraTrack?: Track; + microphoneTrack?: Track; + screenShareVideoTrack?: Track; + screenShareAudioTrack?: Track; + customVideoTracks: Track[]; + customAudioTracks: Track[]; +};