diff --git a/packages/react-bindings/src/hooks/callStateHooks.ts b/packages/react-bindings/src/hooks/callStateHooks.ts index 1acc23eae7..1c6ed58f7e 100644 --- a/packages/react-bindings/src/hooks/callStateHooks.ts +++ b/packages/react-bindings/src/hooks/callStateHooks.ts @@ -9,6 +9,7 @@ import { Comparator, EgressResponse, MemberResponse, + OwnCapability, StreamVideoParticipant, UserResponse, } from '@stream-io/video-client'; @@ -320,6 +321,24 @@ export const useCallThumbnail = () => { return useObservableValue(thumbnails$); }; +/** + * A hook which returns the local participant's own capabilities. + */ +export const useOwnCapabilities = (): OwnCapability[] | undefined => { + const { ownCapabilities$ } = useCallState(); + return useObservableValue(ownCapabilities$); +}; + +/** + * Hook that returns true if the local participant has all the given permissions. + * + * @param permissions the permissions to check. + */ +export const useHasPermissions = (...permissions: OwnCapability[]): boolean => { + const capabilities = useOwnCapabilities(); + return permissions.every((permission) => capabilities?.includes(permission)); +}; + /** * Returns the camera state of the current call. * diff --git a/packages/react-bindings/src/hooks/index.ts b/packages/react-bindings/src/hooks/index.ts index aed7cb0f38..10dd1ca90c 100644 --- a/packages/react-bindings/src/hooks/index.ts +++ b/packages/react-bindings/src/hooks/index.ts @@ -1,6 +1,5 @@ import * as CallStateHooks from './callStateHooks'; -export * from './permissions'; export * from './store'; /** diff --git a/packages/react-bindings/src/hooks/permissions.ts b/packages/react-bindings/src/hooks/permissions.ts deleted file mode 100644 index 13eac30983..0000000000 --- a/packages/react-bindings/src/hooks/permissions.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { OwnCapability } from '@stream-io/video-client'; -import { useCallState } from './callStateHooks'; -import { useObservableValue } from './useObservableValue'; - -/** - * Hook that returns true if the local participant has all the given permissions. - * - * @param permissions the permissions to check. - * - * @category Call State - */ -export const useHasPermissions = (...permissions: OwnCapability[]): boolean => { - const capabilities = useOwnCapabilities(); - return permissions.every((permission) => capabilities?.includes(permission)); -}; - -/** - * A hook which returns the local participant's own capabilities. - * - * @category Call State - */ -export const useOwnCapabilities = (): OwnCapability[] | undefined => { - const { ownCapabilities$ } = useCallState(); - return useObservableValue(ownCapabilities$); -}; diff --git a/packages/react-bindings/src/wrappers/Restricted.tsx b/packages/react-bindings/src/wrappers/Restricted.tsx index a0ff5b5b08..5b60ad619a 100644 --- a/packages/react-bindings/src/wrappers/Restricted.tsx +++ b/packages/react-bindings/src/wrappers/Restricted.tsx @@ -2,7 +2,7 @@ import { OwnCapability } from '@stream-io/video-client'; import { PropsWithChildren } from 'react'; import { useCall } from '../contexts'; -import { useCallStateHooks, useOwnCapabilities } from '../hooks'; +import { useCallStateHooks } from '../hooks'; type RestrictedProps = PropsWithChildren<{ /** @@ -32,8 +32,8 @@ export const Restricted = ({ children, }: RestrictedProps) => { const call = useCall(); + const { useCallSettings, useOwnCapabilities } = useCallStateHooks(); const ownCapabilities = useOwnCapabilities(); - const { useCallSettings } = useCallStateHooks(); const settings = useCallSettings(); const hasPermissions = requiredGrants[requireAll ? 'every' : 'some']( (capability) => ownCapabilities?.includes(capability), diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/09-permissions-and-moderation.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/09-permissions-and-moderation.mdx index c289c14787..cedaf0560a 100644 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/09-permissions-and-moderation.mdx +++ b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/09-permissions-and-moderation.mdx @@ -3,9 +3,6 @@ title: Permissions & Moderation description: Explanation of call permissions and moderation features --- -import SDKSpecific from '../SDKSpecific'; - - In many types of calls, there is a requirement for providing different users with certain permissions and capabilities. A typical example is a webinar where the host wants to control who can speak or who can share their video or screen. @@ -37,7 +34,7 @@ or perform some permission-related actions: ### Check permissions ```ts -import { OwnCapability } from '@stream-io/video-client'; +import { OwnCapability } from '@stream-io/video-react-native-sdk'; const call = streamVideoClient.call(type, id); const canSendAudio = call.permissionsContext.hasPermission( @@ -48,8 +45,12 @@ const canSendAudio = call.permissionsContext.hasPermission( In our React Native Video SDK, you can use the `useHasPermissions` hook to check for permissions. ```tsx -import { useHasPermissions, OwnCapability } from '@stream-io/video-react-native-sdk'; +import { + useCallStateHooks, + OwnCapability, +} from '@stream-io/video-react-native-sdk'; +const { useHasPermissions } = useCallStateHooks(); const canSendAudio = useHasPermissions(OwnCapability.SEND_AUDIO); ``` @@ -170,14 +171,13 @@ const call = streamVideoClient.call(type, id); const { own_capabilities } = call.state.metadata; ``` - - -In our React Video SDK, you can use the `useOwnCapabilities` hook. +In our React Native Video SDK, you can use the `useOwnCapabilities` hook. ```tsx +import { useCallStateHooks } from '@stream-io/video-react-bindings'; + +const { useOwnCapabilities } = useCallStateHooks(); const ownCapabilities = useOwnCapabilities(); ``` - - Capabilities will have the type [`OwnCapability`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/gen/coordinator/index.ts). diff --git a/packages/react-native-sdk/src/hooks/usePermissionNotification.tsx b/packages/react-native-sdk/src/hooks/usePermissionNotification.tsx index a5d0eed5da..a9f5127e17 100644 --- a/packages/react-native-sdk/src/hooks/usePermissionNotification.tsx +++ b/packages/react-native-sdk/src/hooks/usePermissionNotification.tsx @@ -1,8 +1,5 @@ import { CallingState, OwnCapability } from '@stream-io/video-client'; -import { - useCallStateHooks, - useHasPermissions, -} from '@stream-io/video-react-bindings'; +import { useCallStateHooks } from '@stream-io/video-react-bindings'; import { useCallback, useEffect } from 'react'; import { Alert } from 'react-native'; import { usePrevious } from '../utils/hooks/usePrevious'; @@ -30,9 +27,9 @@ export const usePermissionNotification = ( props: PermissionNotificationProps, ) => { const { permission, messageApproved, messageRevoked } = props; + const { useCallCallingState, useHasPermissions } = useCallStateHooks(); const hasPermission = useHasPermissions(permission); const previousHasPermission = usePrevious(hasPermission); - const { useCallCallingState } = useCallStateHooks(); const callingState = useCallCallingState(); const showGrantedNotification = useCallback(() => { diff --git a/packages/react-native-sdk/src/hooks/usePermissionRequest.tsx b/packages/react-native-sdk/src/hooks/usePermissionRequest.tsx index 19aab8d8c3..7e440f5753 100644 --- a/packages/react-native-sdk/src/hooks/usePermissionRequest.tsx +++ b/packages/react-native-sdk/src/hooks/usePermissionRequest.tsx @@ -1,11 +1,12 @@ import { OwnCapability, PermissionRequestEvent } from '@stream-io/video-client'; -import { useCall, useHasPermissions } from '@stream-io/video-react-bindings'; +import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings'; import { useCallback, useEffect } from 'react'; import { Alert } from 'react-native'; export const usePermissionRequest = () => { const call = useCall(); + const { useHasPermissions } = useCallStateHooks(); const userHasUpdateCallPermissionsCapability = useHasPermissions( OwnCapability.UPDATE_CALL_PERMISSIONS, ); diff --git a/packages/react-sdk/docusaurus/docs/React/02-guides/08-permissions-and-moderation.mdx b/packages/react-sdk/docusaurus/docs/React/02-guides/08-permissions-and-moderation.mdx index 36e6687186..2748d4518c 100644 --- a/packages/react-sdk/docusaurus/docs/React/02-guides/08-permissions-and-moderation.mdx +++ b/packages/react-sdk/docusaurus/docs/React/02-guides/08-permissions-and-moderation.mdx @@ -3,8 +3,6 @@ title: Permissions & Moderation description: Explanation of call permissions and moderation features --- -import SDKSpecific from '../SDKSpecific'; - In many types of calls, there is a requirement for providing different users with certain permissions and capabilities. A typical example is a webinar where the host wants to control who can speak or who can share their video or screen. @@ -44,16 +42,15 @@ const canSendAudio = call.permissionsContext.hasPermission( ); ``` - - In our React Video SDK, you can use the `useHasPermissions` hook to check for permissions. ```tsx +import { useCallStateHooks } from '@stream-io/video-react-sdk'; + +const { useHasPermissions } = useCallStateHooks(); const canSendAudio = useHasPermissions(OwnCapability.SEND_AUDIO); ``` - - ### Request permissions Every user may request permission to perform certain actions depending on the call type and call settings. @@ -171,14 +168,13 @@ const call = client.call(type, id); const { ownCapabilities } = call.state; ``` - - In our React Video SDK, you can use the `useOwnCapabilities` hook. ```tsx +import { useCallStateHooks } from '@stream-io/video-react-sdk'; + +const { useOwnCapabilities } = useCallStateHooks(); const ownCapabilities = useOwnCapabilities(); ``` - - Capabilities will have the type [`OwnCapability`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/gen/coordinator/index.ts). diff --git a/packages/react-sdk/docusaurus/docs/React/06-ui-cookbook/08-permission-requests.mdx b/packages/react-sdk/docusaurus/docs/React/06-ui-cookbook/08-permission-requests.mdx index 6aaa2bf72d..f02f9ea53e 100644 --- a/packages/react-sdk/docusaurus/docs/React/06-ui-cookbook/08-permission-requests.mdx +++ b/packages/react-sdk/docusaurus/docs/React/06-ui-cookbook/08-permission-requests.mdx @@ -96,7 +96,7 @@ For readability the code snippet only contains the `MyPermissionRequests` and th ```tsx import { useRequestPermission, - useHasPermissions, + useCallStateHooks, OwnCapability, } from '@stream-io/video-react-sdk'; @@ -119,6 +119,7 @@ const MyPermissionRequestButton = ({ children, capability }) => { const MyPermissionRequests = () => { const call = useCall(); + const { useHasPermissions } = useCallStateHooks(); const canSendAudio = useHasPermissions(OwnCapability.SEND_AUDIO); const canSendVideo = useHasPermissions(OwnCapability.SEND_VIDEO); const canShareScreen = useHasPermissions(OwnCapability.SCREENSHARE); @@ -163,7 +164,7 @@ For readability, the code snippet only contains the `MyPermissionRequestNotifica ```tsx const MyPermissionRequestNotifications = () => { const call = useCall(); - const { useLocalParticipant } = useCallStateHooks(); + const { useLocalParticipant, useHasPermissions } = useCallStateHooks(); const localParticipant = useLocalParticipant(); const canUpdateCallPermissions = useHasPermissions( OwnCapability.UPDATE_CALL_PERMISSIONS, @@ -245,7 +246,6 @@ import { useCall, useCallStateHooks, useRequestPermission, - useHasPermissions, } from '@stream-io/video-react-sdk'; import '@stream-io/video-react-sdk/dist/css/styles.css'; import { useEffect, useState } from 'react'; @@ -296,6 +296,7 @@ const MyPermissionRequestButton = ({ children, capability }) => { const MyPermissionRequests = () => { const call = useCall(); + const { useHasPermissions } = useCallStateHooks(); const canSendAudio = useHasPermissions(OwnCapability.SEND_AUDIO); const canSendVideo = useHasPermissions(OwnCapability.SEND_VIDEO); const canShareScreen = useHasPermissions(OwnCapability.SCREENSHARE); @@ -326,7 +327,7 @@ const MyPermissionRequests = () => { const MyPermissionRequestNotifications = () => { const call = useCall(); - const { useLocalParticipant } = useCallStateHooks(); + const { useLocalParticipant, useHasPermissions } = useCallStateHooks(); const localParticipant = useLocalParticipant(); const canUpdateCallPermissions = useHasPermissions( OwnCapability.UPDATE_CALL_PERMISSIONS, diff --git a/packages/react-sdk/src/components/Notification/PermissionNotification.tsx b/packages/react-sdk/src/components/Notification/PermissionNotification.tsx index f41e2ad065..025522bca2 100644 --- a/packages/react-sdk/src/components/Notification/PermissionNotification.tsx +++ b/packages/react-sdk/src/components/Notification/PermissionNotification.tsx @@ -7,7 +7,7 @@ import { useRef, useState, } from 'react'; -import { useHasPermissions } from '@stream-io/video-react-bindings'; +import { useCallStateHooks } from '@stream-io/video-react-bindings'; export type PermissionNotificationProps = PropsWithChildren<{ /** @@ -55,6 +55,7 @@ export const PermissionNotification = (props: PermissionNotificationProps) => { visibilityTimeout = 3500, children, } = props; + const { useHasPermissions } = useCallStateHooks(); const hasPermission = useHasPermissions(permission); const prevHasPermission = useRef(hasPermission); const [showNotification, setShowNotification] = useState< diff --git a/packages/react-sdk/src/components/Permissions/PermissionRequests.tsx b/packages/react-sdk/src/components/Permissions/PermissionRequests.tsx index c1428e3a64..c797323f63 100644 --- a/packages/react-sdk/src/components/Permissions/PermissionRequests.tsx +++ b/packages/react-sdk/src/components/Permissions/PermissionRequests.tsx @@ -16,7 +16,6 @@ import { TranslatorFunction, useCall, useCallStateHooks, - useHasPermissions, useI18n, } from '@stream-io/video-react-bindings'; import clsx from 'clsx'; @@ -38,7 +37,7 @@ type HandleUpdatePermission = ( export const PermissionRequests = () => { const call = useCall(); - const { useLocalParticipant } = useCallStateHooks(); + const { useLocalParticipant, useHasPermissions } = useCallStateHooks(); const localParticipant = useLocalParticipant(); const [expanded, setExpanded] = useState(false); const [permissionRequests, setPermissionRequests] = useState< diff --git a/packages/react-sdk/src/hooks/useRequestPermission.ts b/packages/react-sdk/src/hooks/useRequestPermission.ts index fbdcd526bd..ca104131c1 100644 --- a/packages/react-sdk/src/hooks/useRequestPermission.ts +++ b/packages/react-sdk/src/hooks/useRequestPermission.ts @@ -1,9 +1,10 @@ import { useCallback, useEffect, useState } from 'react'; import { OwnCapability } from '@stream-io/video-client'; -import { useCall, useHasPermissions } from '@stream-io/video-react-bindings'; +import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings'; export const useRequestPermission = (permission: OwnCapability) => { const call = useCall(); + const { useHasPermissions } = useCallStateHooks(); const hasPermission = useHasPermissions(permission); const [isAwaitingPermission, setIsAwaitingPermission] = useState(false); // TODO: load with possibly pending state diff --git a/sample-apps/react-native/dogfood/src/components/AudioRoom/PermissionRequestsPanel.tsx b/sample-apps/react-native/dogfood/src/components/AudioRoom/PermissionRequestsPanel.tsx index ce07cbd616..eb4c6903b5 100644 --- a/sample-apps/react-native/dogfood/src/components/AudioRoom/PermissionRequestsPanel.tsx +++ b/sample-apps/react-native/dogfood/src/components/AudioRoom/PermissionRequestsPanel.tsx @@ -2,7 +2,7 @@ import { OwnCapability, PermissionRequestEvent, useCall, - useHasPermissions, + useCallStateHooks, useI18n, } from '@stream-io/video-react-native-sdk'; import React, { useEffect, useState } from 'react'; @@ -12,6 +12,7 @@ export const PermissionRequestsPanel = () => { const call = useCall(); const { t } = useI18n(); + const { useHasPermissions } = useCallStateHooks(); const canUpdatePermissions = useHasPermissions( OwnCapability.UPDATE_CALL_PERMISSIONS, ); diff --git a/sample-apps/react-native/dogfood/src/components/AudioRoom/ToggleAudioButton.tsx b/sample-apps/react-native/dogfood/src/components/AudioRoom/ToggleAudioButton.tsx index de23193295..5f50a27d99 100644 --- a/sample-apps/react-native/dogfood/src/components/AudioRoom/ToggleAudioButton.tsx +++ b/sample-apps/react-native/dogfood/src/components/AudioRoom/ToggleAudioButton.tsx @@ -4,7 +4,6 @@ import { useCall, useCallStateHooks, useConnectedUser, - useHasPermissions, useIncallManager, } from '@stream-io/video-react-native-sdk'; import React, { useEffect, useState } from 'react'; @@ -15,7 +14,7 @@ export default function ToggleAudioButton() { const call = useCall(); const connectedUser = useConnectedUser(); - const { useLocalParticipant } = useCallStateHooks(); + const { useLocalParticipant, useHasPermissions } = useCallStateHooks(); const localParticipant = useLocalParticipant(); const isMuted = !localParticipant?.publishedTracks.includes( SfuModels.TrackType.AUDIO, diff --git a/sample-apps/react-native/dogfood/src/components/ParticipantActions.tsx b/sample-apps/react-native/dogfood/src/components/ParticipantActions.tsx index 4d67681520..fe8fdfdb34 100644 --- a/sample-apps/react-native/dogfood/src/components/ParticipantActions.tsx +++ b/sample-apps/react-native/dogfood/src/components/ParticipantActions.tsx @@ -14,11 +14,11 @@ import { VideoSlash } from '../assets/VideoSlash'; import { Pressable, StyleSheet, Text, View } from 'react-native'; import React, { useCallback } from 'react'; import { - useCall, - useHasPermissions, - useI18n, Avatar, colorPallet, + useCall, + useCallStateHooks, + useI18n, } from '@stream-io/video-react-native-sdk'; import { generateParticipantTitle } from '../utils'; @@ -39,6 +39,7 @@ export const ParticipantActions = (props: ParticipantActionsType) => { const { participant, setSelectedParticipant } = props; const call = useCall(); const { t } = useI18n(); + const { useHasPermissions } = useCallStateHooks(); const userHasMuteUsersCapability = useHasPermissions( OwnCapability.MUTE_USERS, ); diff --git a/sample-apps/react/audio-rooms/src/components/Room/LiveRoomControls.tsx b/sample-apps/react/audio-rooms/src/components/Room/LiveRoomControls.tsx index b138693734..f4c562e25b 100644 --- a/sample-apps/react/audio-rooms/src/components/Room/LiveRoomControls.tsx +++ b/sample-apps/react/audio-rooms/src/components/Room/LiveRoomControls.tsx @@ -6,7 +6,6 @@ import { useCall, useCallStateHooks, useConnectedUser, - useHasPermissions, } from '@stream-io/video-react-sdk'; import { useCallback, useEffect, useState } from 'react'; import { @@ -27,8 +26,12 @@ export const LiveRoomControls = ({ openRequestsList, }: LiveRoomControlsProps) => { const call = useCall(); - const { useCallCustomData, useCallCallingState, useLocalParticipant } = - useCallStateHooks(); + const { + useCallCustomData, + useCallCallingState, + useLocalParticipant, + useHasPermissions, + } = useCallStateHooks(); const customData = useCallCustomData(); const callingState = useCallCallingState(); const connectedUser = useConnectedUser(); diff --git a/sample-apps/react/audio-rooms/src/components/Room/RoomUI.tsx b/sample-apps/react/audio-rooms/src/components/Room/RoomUI.tsx index 93d8df30f4..4967607861 100644 --- a/sample-apps/react/audio-rooms/src/components/Room/RoomUI.tsx +++ b/sample-apps/react/audio-rooms/src/components/Room/RoomUI.tsx @@ -6,7 +6,6 @@ import { StreamVideoParticipant, useCall, useCallStateHooks, - useHasPermissions, } from '@stream-io/video-react-sdk'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { ChatIcon, CloseIcon, ListIcon, PersonIcon } from '../icons'; @@ -35,6 +34,7 @@ export const RoomUI = ({ loadRoom }: RoomUIProps) => { useParticipants, useCallEndedAt, useIsCallLive, + useHasPermissions, } = useCallStateHooks(); const customData = useCallCustomData(); const endedAt = useCallEndedAt(); diff --git a/sample-apps/react/audio-rooms/src/components/Room/SpeakerElement.tsx b/sample-apps/react/audio-rooms/src/components/Room/SpeakerElement.tsx index 9bb1acb1c3..4427dd2f38 100644 --- a/sample-apps/react/audio-rooms/src/components/Room/SpeakerElement.tsx +++ b/sample-apps/react/audio-rooms/src/components/Room/SpeakerElement.tsx @@ -6,7 +6,6 @@ import { StreamVideoParticipant, useCall, useCallStateHooks, - useHasPermissions, } from '@stream-io/video-react-sdk'; import { CloseIcon, MuteMicrophoneIcon } from '../icons'; import type { CustomCallData } from '../../types'; @@ -17,7 +16,7 @@ export const SpeakerElement = ({ speaker: StreamVideoParticipant; }) => { const call = useCall(); - const { useCallCustomData } = useCallStateHooks(); + const { useCallCustomData, useHasPermissions } = useCallStateHooks(); const customData = useCallCustomData(); const canMuteUsers = useHasPermissions(OwnCapability.MUTE_USERS); diff --git a/sample-apps/react/audio-rooms/src/hooks/useSpeakingRequests.ts b/sample-apps/react/audio-rooms/src/hooks/useSpeakingRequests.ts index 7044173b1d..0606903f88 100644 --- a/sample-apps/react/audio-rooms/src/hooks/useSpeakingRequests.ts +++ b/sample-apps/react/audio-rooms/src/hooks/useSpeakingRequests.ts @@ -3,11 +3,12 @@ import { OwnCapability, PermissionRequestEvent, useCall, - useHasPermissions, + useCallStateHooks, } from '@stream-io/video-react-sdk'; export const useSpeakingRequests = () => { const call = useCall(); + const { useHasPermissions } = useCallStateHooks(); const canUpdatePermissions = useHasPermissions( OwnCapability.UPDATE_CALL_PERMISSIONS, );