@stream-io/video-react-sdk 0.0.92 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -15
- package/dist/src/components/CallControls/ReactionsButton.js +3 -2
- package/dist/src/components/CallControls/ReactionsButton.js.map +1 -1
- package/dist/src/components/CallControls/RecordCallButton.js +7 -40
- package/dist/src/components/CallControls/RecordCallButton.js.map +1 -1
- package/dist/src/components/CallControls/ScreenShareButton.js +6 -46
- package/dist/src/components/CallControls/ScreenShareButton.js.map +1 -1
- package/dist/src/components/CallControls/ToggleAudioButton.js +2 -2
- package/dist/src/components/CallControls/ToggleAudioButton.js.map +1 -1
- package/dist/src/components/CallControls/ToggleVideoButton.js +2 -2
- package/dist/src/components/CallControls/ToggleVideoButton.js.map +1 -1
- package/dist/src/components/Permissions/PermissionRequests.d.ts +3 -1
- package/dist/src/components/Permissions/PermissionRequests.js +25 -13
- package/dist/src/components/Permissions/PermissionRequests.js.map +1 -1
- package/dist/src/hooks/index.d.ts +3 -0
- package/dist/src/hooks/index.js +3 -0
- package/dist/src/hooks/index.js.map +1 -1
- package/dist/src/hooks/useRequestPermission.d.ts +7 -0
- package/dist/src/hooks/useRequestPermission.js +52 -0
- package/dist/src/hooks/useRequestPermission.js.map +1 -0
- package/dist/src/hooks/useToggleAudioMuteState.d.ts +1 -1
- package/dist/src/hooks/useToggleAudioMuteState.js +14 -41
- package/dist/src/hooks/useToggleAudioMuteState.js.map +1 -1
- package/dist/src/hooks/useToggleCallRecording.d.ts +5 -0
- package/dist/src/hooks/useToggleCallRecording.js +43 -0
- package/dist/src/hooks/useToggleCallRecording.js.map +1 -0
- package/dist/src/hooks/useToggleScreenShare.d.ts +5 -0
- package/dist/src/hooks/useToggleScreenShare.js +37 -0
- package/dist/src/hooks/useToggleScreenShare.js.map +1 -0
- package/dist/src/hooks/useToggleVideoMuteState.d.ts +1 -1
- package/dist/src/hooks/useToggleVideoMuteState.js +14 -41
- package/dist/src/hooks/useToggleVideoMuteState.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +3 -3
- package/src/components/CallControls/ReactionsButton.tsx +5 -3
- package/src/components/CallControls/RecordCallButton.tsx +9 -33
- package/src/components/CallControls/ScreenShareButton.tsx +13 -51
- package/src/components/CallControls/ToggleAudioButton.tsx +5 -5
- package/src/components/CallControls/ToggleVideoButton.tsx +7 -5
- package/src/components/Permissions/PermissionRequests.tsx +49 -22
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useRequestPermission.ts +48 -0
- package/src/hooks/useToggleAudioMuteState.ts +19 -48
- package/src/hooks/useToggleCallRecording.ts +39 -0
- package/src/hooks/useToggleScreenShare.ts +42 -0
- package/src/hooks/useToggleVideoMuteState.ts +19 -48
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
11
|
+
import { useCall, useIsCallRecordingInProgress, } from '@stream-io/video-react-bindings';
|
|
12
|
+
export const useToggleCallRecording = () => {
|
|
13
|
+
const call = useCall();
|
|
14
|
+
const isCallRecordingInProgress = useIsCallRecordingInProgress();
|
|
15
|
+
const [isAwaitingResponse, setIsAwaitingResponse] = useState(false);
|
|
16
|
+
// TODO: add permissions
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
// we wait until call.recording_started/stopped event to flips the
|
|
19
|
+
// `isCallRecordingInProgress` state variable.
|
|
20
|
+
// Once the flip happens, we remove the loading indicator
|
|
21
|
+
setIsAwaitingResponse((isAwaiting) => {
|
|
22
|
+
if (isAwaiting)
|
|
23
|
+
return false;
|
|
24
|
+
return isAwaiting;
|
|
25
|
+
});
|
|
26
|
+
}, [isCallRecordingInProgress]);
|
|
27
|
+
const toggleCallRecording = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
|
+
try {
|
|
29
|
+
setIsAwaitingResponse(true);
|
|
30
|
+
if (isCallRecordingInProgress) {
|
|
31
|
+
yield (call === null || call === void 0 ? void 0 : call.stopRecording());
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
yield (call === null || call === void 0 ? void 0 : call.startRecording());
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
console.error(`Failed start recording`, e);
|
|
39
|
+
}
|
|
40
|
+
}), [call, isCallRecordingInProgress]);
|
|
41
|
+
return { toggleCallRecording, isAwaitingResponse, isCallRecordingInProgress };
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=useToggleCallRecording.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useToggleCallRecording.js","sourceRoot":"","sources":["../../../src/hooks/useToggleCallRecording.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEzD,OAAO,EACL,OAAO,EACP,4BAA4B,GAC7B,MAAM,iCAAiC,CAAC;AAEzC,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,EAAE;IACzC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,yBAAyB,GAAG,4BAA4B,EAAE,CAAC;IACjE,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEpE,wBAAwB;IAExB,SAAS,CAAC,GAAG,EAAE;QACb,kEAAkE;QAClE,8CAA8C;QAC9C,yDAAyD;QACzD,qBAAqB,CAAC,CAAC,UAAU,EAAE,EAAE;YACnC,IAAI,UAAU;gBAAE,OAAO,KAAK,CAAC;YAC7B,OAAO,UAAU,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAC,CAAC;IAEhC,MAAM,mBAAmB,GAAG,WAAW,CAAC,GAAS,EAAE;QACjD,IAAI;YACF,qBAAqB,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,yBAAyB,EAAE;gBAC7B,MAAM,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,aAAa,EAAE,CAAA,CAAC;aAC7B;iBAAM;gBACL,MAAM,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,cAAc,EAAE,CAAA,CAAC;aAC9B;SACF;QAAC,OAAO,CAAC,EAAE;YACV,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,CAAC,CAAC,CAAC;SAC5C;IACH,CAAC,CAAA,EAAE,CAAC,IAAI,EAAE,yBAAyB,CAAC,CAAC,CAAC;IAEtC,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,CAAC;AAChF,CAAC,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { useCallback, useRef } from 'react';
|
|
11
|
+
import { useCall, useLocalParticipant } from '@stream-io/video-react-bindings';
|
|
12
|
+
import { OwnCapability, SfuModels, getScreenShareStream, } from '@stream-io/video-client';
|
|
13
|
+
import { useRequestPermission } from './useRequestPermission';
|
|
14
|
+
export const useToggleScreenShare = () => {
|
|
15
|
+
const localParticipant = useLocalParticipant();
|
|
16
|
+
const call = useCall();
|
|
17
|
+
const isScreenSharingReference = useRef(false);
|
|
18
|
+
const { isAwaitingPermission, requestPermission } = useRequestPermission(OwnCapability.SCREENSHARE);
|
|
19
|
+
const isScreenSharing = !!(localParticipant === null || localParticipant === void 0 ? void 0 : localParticipant.publishedTracks.includes(SfuModels.TrackType.SCREEN_SHARE));
|
|
20
|
+
isScreenSharingReference.current = isScreenSharing;
|
|
21
|
+
const toggleScreenShare = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
|
+
if (!isScreenSharingReference.current) {
|
|
23
|
+
const canPublish = yield requestPermission();
|
|
24
|
+
if (!canPublish)
|
|
25
|
+
return;
|
|
26
|
+
const stream = yield getScreenShareStream().catch((e) => {
|
|
27
|
+
console.log(`Can't share screen: ${e}`);
|
|
28
|
+
});
|
|
29
|
+
if (stream) {
|
|
30
|
+
return call === null || call === void 0 ? void 0 : call.publishScreenShareStream(stream);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
call === null || call === void 0 ? void 0 : call.stopPublish(SfuModels.TrackType.SCREEN_SHARE);
|
|
34
|
+
}), [call, requestPermission]);
|
|
35
|
+
return { toggleScreenShare, isAwaitingPermission, isScreenSharing };
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=useToggleScreenShare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useToggleScreenShare.js","sourceRoot":"","sources":["../../../src/hooks/useToggleScreenShare.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EACL,aAAa,EACb,SAAS,EACT,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,EAAE;IACvC,MAAM,gBAAgB,GAAG,mBAAmB,EAAE,CAAC;IAC/C,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,wBAAwB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,GAAG,oBAAoB,CACtE,aAAa,CAAC,WAAW,CAC1B,CAAC;IAEF,MAAM,eAAe,GAAG,CAAC,CAAC,CAAA,gBAAgB,aAAhB,gBAAgB,uBAAhB,gBAAgB,CAAE,eAAe,CAAC,QAAQ,CAClE,SAAS,CAAC,SAAS,CAAC,YAAY,CACjC,CAAA,CAAC;IAEF,wBAAwB,CAAC,OAAO,GAAG,eAAe,CAAC;IAEnD,MAAM,iBAAiB,GAAG,WAAW,CAAC,GAAS,EAAE;QAC/C,IAAI,CAAC,wBAAwB,CAAC,OAAO,EAAE;YACrC,MAAM,UAAU,GAAG,MAAM,iBAAiB,EAAE,CAAC;YAC7C,IAAI,CAAC,UAAU;gBAAE,OAAO;YAExB,MAAM,MAAM,GAAG,MAAM,oBAAoB,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;gBACtD,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC;YAC1C,CAAC,CAAC,CAAC;YAEH,IAAI,MAAM,EAAE;gBACV,OAAO,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,wBAAwB,CAAC,MAAM,CAAC,CAAC;aAC/C;SACF;QAED,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,WAAW,CAAC,SAAS,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACtD,CAAC,CAAA,EAAE,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAE9B,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,eAAe,EAAE,CAAC;AACtE,CAAC,CAAC"}
|
|
@@ -7,54 +7,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { useCallback,
|
|
11
|
-
import {
|
|
10
|
+
import { useCallback, useRef } from 'react';
|
|
11
|
+
import { useLocalParticipant } from '@stream-io/video-react-bindings';
|
|
12
12
|
import { OwnCapability, SfuModels } from '@stream-io/video-client';
|
|
13
13
|
import { useMediaDevices } from '../core';
|
|
14
|
+
import { useRequestPermission } from './useRequestPermission';
|
|
14
15
|
export const useToggleVideoMuteState = () => {
|
|
15
16
|
const { publishVideoStream, stopPublishingVideo } = useMediaDevices();
|
|
16
17
|
const localParticipant = useLocalParticipant();
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
if (hasPermission) {
|
|
23
|
-
setIsAwaitingApproval(false);
|
|
24
|
-
}
|
|
25
|
-
}, [hasPermission]);
|
|
18
|
+
const { isAwaitingPermission, requestPermission } = useRequestPermission(OwnCapability.SEND_VIDEO);
|
|
19
|
+
// to keep the toggle function as stable as possible
|
|
20
|
+
const isVideoMutedReference = useRef(false);
|
|
21
|
+
isVideoMutedReference.current = !(localParticipant === null || localParticipant === void 0 ? void 0 : localParticipant.publishedTracks.includes(SfuModels.TrackType.VIDEO));
|
|
26
22
|
const toggleVideoMuteState = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
yield call
|
|
32
|
-
.requestPermissions({
|
|
33
|
-
permissions: [OwnCapability.SEND_VIDEO],
|
|
34
|
-
})
|
|
35
|
-
.catch((reason) => {
|
|
36
|
-
console.log('RequestPermissions failed', reason);
|
|
37
|
-
});
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
if (isVideoMute) {
|
|
41
|
-
if (hasPermission) {
|
|
42
|
-
yield publishVideoStream();
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
console.log('Cannot publish video. Insufficient permissions.');
|
|
46
|
-
}
|
|
23
|
+
if (isVideoMutedReference.current) {
|
|
24
|
+
const canPublish = yield requestPermission();
|
|
25
|
+
if (canPublish)
|
|
26
|
+
return publishVideoStream();
|
|
47
27
|
}
|
|
48
|
-
|
|
28
|
+
if (!isVideoMutedReference.current)
|
|
49
29
|
stopPublishingVideo();
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
call,
|
|
53
|
-
hasPermission,
|
|
54
|
-
isVideoMute,
|
|
55
|
-
publishVideoStream,
|
|
56
|
-
stopPublishingVideo,
|
|
57
|
-
]);
|
|
58
|
-
return { toggleVideoMuteState, isAwaitingApproval };
|
|
30
|
+
}), [publishVideoStream, requestPermission, stopPublishingVideo]);
|
|
31
|
+
return { toggleVideoMuteState, isAwaitingPermission };
|
|
59
32
|
};
|
|
60
33
|
//# sourceMappingURL=useToggleVideoMuteState.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useToggleVideoMuteState.js","sourceRoot":"","sources":["../../../src/hooks/useToggleVideoMuteState.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"useToggleVideoMuteState.js","sourceRoot":"","sources":["../../../src/hooks/useToggleVideoMuteState.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEnE,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,EAAE;IAC1C,MAAM,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,GAAG,eAAe,EAAE,CAAC;IACtE,MAAM,gBAAgB,GAAG,mBAAmB,EAAE,CAAC;IAE/C,MAAM,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,GAAG,oBAAoB,CACtE,aAAa,CAAC,UAAU,CACzB,CAAC;IAEF,oDAAoD;IACpD,MAAM,qBAAqB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAE5C,qBAAqB,CAAC,OAAO,GAAG,CAAC,CAAA,gBAAgB,aAAhB,gBAAgB,uBAAhB,gBAAgB,CAAE,eAAe,CAAC,QAAQ,CACzE,SAAS,CAAC,SAAS,CAAC,KAAK,CAC1B,CAAA,CAAC;IAEF,MAAM,oBAAoB,GAAG,WAAW,CAAC,GAAS,EAAE;QAClD,IAAI,qBAAqB,CAAC,OAAO,EAAE;YACjC,MAAM,UAAU,GAAG,MAAM,iBAAiB,EAAE,CAAC;YAC7C,IAAI,UAAU;gBAAE,OAAO,kBAAkB,EAAE,CAAC;SAC7C;QAED,IAAI,CAAC,qBAAqB,CAAC,OAAO;YAAE,mBAAmB,EAAE,CAAC;IAC5D,CAAC,CAAA,EAAE,CAAC,kBAAkB,EAAE,iBAAiB,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAEjE,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,CAAC;AACxD,CAAC,CAAC"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "0.0
|
|
1
|
+
export declare const version = "0.1.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = '0.0
|
|
1
|
+
export const version = '0.1.0';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
package/dist/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stream-io/video-react-sdk",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"packageManager": "yarn@3.2.4",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"@floating-ui/react": "^0.22.0",
|
|
33
33
|
"@nivo/core": "^0.80.0",
|
|
34
34
|
"@nivo/line": "^0.80.0",
|
|
35
|
-
"@stream-io/i18n": "^0.1.
|
|
35
|
+
"@stream-io/i18n": "^0.1.1",
|
|
36
36
|
"@stream-io/video-client": "^0.0.51",
|
|
37
|
-
"@stream-io/video-react-bindings": "^0.0
|
|
37
|
+
"@stream-io/video-react-bindings": "^0.1.0",
|
|
38
38
|
"clsx": "^1.2.1",
|
|
39
39
|
"prop-types": "^15.8.1",
|
|
40
40
|
"rxjs": "~7.8.1"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OwnCapability, StreamReaction } from '@stream-io/video-client';
|
|
2
|
-
import { Restricted, useCall } from '@stream-io/video-react-bindings';
|
|
2
|
+
import { Restricted, useCall, useI18n } from '@stream-io/video-react-bindings';
|
|
3
3
|
|
|
4
4
|
import { CompositeButton, IconButton } from '../Button';
|
|
5
5
|
import { defaultEmojiReactionMap } from '../Reaction';
|
|
@@ -27,17 +27,19 @@ export interface ReactionsButtonProps {
|
|
|
27
27
|
export const ReactionsButton = ({
|
|
28
28
|
reactions = defaultReactions,
|
|
29
29
|
}: ReactionsButtonProps) => {
|
|
30
|
+
const { t } = useI18n();
|
|
31
|
+
|
|
30
32
|
return (
|
|
31
33
|
<Restricted requiredGrants={[OwnCapability.CREATE_REACTION]}>
|
|
32
34
|
<CompositeButton
|
|
33
35
|
active={false}
|
|
34
|
-
caption=
|
|
36
|
+
caption={t('Reactions')}
|
|
35
37
|
menuPlacement="top-start"
|
|
36
38
|
Menu={<DefaultReactionsMenu reactions={reactions} />}
|
|
37
39
|
>
|
|
38
40
|
<IconButton
|
|
39
41
|
icon="reactions"
|
|
40
|
-
title=
|
|
42
|
+
title={t('Reactions')}
|
|
41
43
|
onClick={() => {
|
|
42
44
|
console.log('Reactions');
|
|
43
45
|
}}
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from 'react';
|
|
2
1
|
import { OwnCapability } from '@stream-io/video-client';
|
|
3
|
-
import {
|
|
4
|
-
Restricted,
|
|
5
|
-
useCall,
|
|
6
|
-
useIsCallRecordingInProgress,
|
|
7
|
-
} from '@stream-io/video-react-bindings';
|
|
2
|
+
import { Restricted, useCall, useI18n } from '@stream-io/video-react-bindings';
|
|
8
3
|
import { CompositeButton, IconButton } from '../Button/';
|
|
9
4
|
import { LoadingIndicator } from '../LoadingIndicator';
|
|
5
|
+
import { useToggleCallRecording } from '../../hooks/useToggleCallRecording';
|
|
10
6
|
|
|
11
7
|
export type RecordCallButtonProps = {
|
|
12
8
|
caption?: string;
|
|
@@ -16,30 +12,10 @@ export const RecordCallButton = ({
|
|
|
16
12
|
caption = 'Record',
|
|
17
13
|
}: RecordCallButtonProps) => {
|
|
18
14
|
const call = useCall();
|
|
19
|
-
const isCallRecordingInProgress = useIsCallRecordingInProgress();
|
|
20
|
-
const [isAwaitingResponse, setIsAwaitingResponse] = useState(false);
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
// we wait until call.recording_started/stopped event to flips the
|
|
23
|
-
// `isCallRecordingInProgress` state variable.
|
|
24
|
-
// Once the flip happens, we remove the loading indicator
|
|
25
|
-
setIsAwaitingResponse((isAwaiting) => {
|
|
26
|
-
if (isAwaiting) return false;
|
|
27
|
-
return isAwaiting;
|
|
28
|
-
});
|
|
29
|
-
}, [isCallRecordingInProgress]);
|
|
30
15
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (isCallRecordingInProgress) {
|
|
35
|
-
await call?.stopRecording();
|
|
36
|
-
} else {
|
|
37
|
-
await call?.startRecording();
|
|
38
|
-
}
|
|
39
|
-
} catch (e) {
|
|
40
|
-
console.error(`Failed start recording`, e);
|
|
41
|
-
}
|
|
42
|
-
}, [call, isCallRecordingInProgress]);
|
|
16
|
+
const { t } = useI18n();
|
|
17
|
+
const { toggleCallRecording, isAwaitingResponse, isCallRecordingInProgress } =
|
|
18
|
+
useToggleCallRecording();
|
|
43
19
|
|
|
44
20
|
return (
|
|
45
21
|
<Restricted
|
|
@@ -53,8 +29,8 @@ export const RecordCallButton = ({
|
|
|
53
29
|
<LoadingIndicator
|
|
54
30
|
tooltip={
|
|
55
31
|
isCallRecordingInProgress
|
|
56
|
-
? 'Waiting for recording to stop...
|
|
57
|
-
: 'Waiting for recording to start...'
|
|
32
|
+
? t('Waiting for recording to stop...')
|
|
33
|
+
: t('Waiting for recording to start...')
|
|
58
34
|
}
|
|
59
35
|
/>
|
|
60
36
|
) : (
|
|
@@ -63,8 +39,8 @@ export const RecordCallButton = ({
|
|
|
63
39
|
enabled={!!call}
|
|
64
40
|
disabled={!call}
|
|
65
41
|
icon={isCallRecordingInProgress ? 'recording-on' : 'recording-off'}
|
|
66
|
-
title=
|
|
67
|
-
onClick={
|
|
42
|
+
title={t('Record call')}
|
|
43
|
+
onClick={toggleCallRecording}
|
|
68
44
|
/>
|
|
69
45
|
)}
|
|
70
46
|
</CompositeButton>
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
getScreenShareStream,
|
|
4
|
-
OwnCapability,
|
|
5
|
-
SfuModels,
|
|
6
|
-
} from '@stream-io/video-client';
|
|
1
|
+
import { OwnCapability } from '@stream-io/video-client';
|
|
7
2
|
import {
|
|
8
3
|
Restricted,
|
|
9
4
|
useCall,
|
|
10
5
|
useHasOngoingScreenShare,
|
|
11
|
-
|
|
12
|
-
useLocalParticipant,
|
|
6
|
+
useI18n,
|
|
13
7
|
} from '@stream-io/video-react-bindings';
|
|
14
8
|
import { CompositeButton, IconButton } from '../Button/';
|
|
15
9
|
import { PermissionNotification } from '../Notification';
|
|
10
|
+
import { useToggleScreenShare } from '../../hooks';
|
|
16
11
|
|
|
17
12
|
export type ScreenShareButtonProps = {
|
|
18
13
|
caption?: string;
|
|
@@ -22,60 +17,27 @@ export const ScreenShareButton = ({
|
|
|
22
17
|
caption = 'Screen Share',
|
|
23
18
|
}: ScreenShareButtonProps) => {
|
|
24
19
|
const call = useCall();
|
|
25
|
-
const localParticipant = useLocalParticipant();
|
|
26
20
|
const isSomeoneScreenSharing = useHasOngoingScreenShare();
|
|
27
|
-
const isScreenSharing = localParticipant?.publishedTracks.includes(
|
|
28
|
-
SfuModels.TrackType.SCREEN_SHARE,
|
|
29
|
-
);
|
|
30
21
|
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
setIsAwaitingApproval(false);
|
|
36
|
-
}
|
|
37
|
-
}, [hasPermission]);
|
|
22
|
+
const { t } = useI18n();
|
|
23
|
+
const { toggleScreenShare, isAwaitingPermission, isScreenSharing } =
|
|
24
|
+
useToggleScreenShare();
|
|
25
|
+
|
|
38
26
|
return (
|
|
39
27
|
<Restricted requiredGrants={[OwnCapability.SCREENSHARE]}>
|
|
40
28
|
<PermissionNotification
|
|
41
29
|
permission={OwnCapability.SCREENSHARE}
|
|
42
|
-
isAwaitingApproval={
|
|
43
|
-
messageApproved=
|
|
44
|
-
messageAwaitingApproval=
|
|
45
|
-
messageRevoked=
|
|
30
|
+
isAwaitingApproval={isAwaitingPermission}
|
|
31
|
+
messageApproved={t('You can now share your screen.')}
|
|
32
|
+
messageAwaitingApproval={t('Awaiting for an approval to share screen.')}
|
|
33
|
+
messageRevoked={t('You can no longer share your screen.')}
|
|
46
34
|
>
|
|
47
35
|
<CompositeButton active={isSomeoneScreenSharing} caption={caption}>
|
|
48
36
|
<IconButton
|
|
49
37
|
icon={isScreenSharing ? 'screen-share-on' : 'screen-share-off'}
|
|
50
|
-
title=
|
|
38
|
+
title={t('Share screen')}
|
|
51
39
|
disabled={(!isScreenSharing && isSomeoneScreenSharing) || !call}
|
|
52
|
-
onClick={
|
|
53
|
-
if (
|
|
54
|
-
!hasPermission &&
|
|
55
|
-
call?.permissionsContext.canRequest(OwnCapability.SCREENSHARE)
|
|
56
|
-
) {
|
|
57
|
-
setIsAwaitingApproval(true);
|
|
58
|
-
await call
|
|
59
|
-
.requestPermissions({
|
|
60
|
-
permissions: [OwnCapability.SCREENSHARE],
|
|
61
|
-
})
|
|
62
|
-
.catch((reason) => {
|
|
63
|
-
console.log('RequestPermissions failed', reason);
|
|
64
|
-
});
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (!isScreenSharing && hasPermission) {
|
|
69
|
-
const stream = await getScreenShareStream().catch((e) => {
|
|
70
|
-
console.log(`Can't share screen: ${e}`);
|
|
71
|
-
});
|
|
72
|
-
if (stream) {
|
|
73
|
-
await call?.publishScreenShareStream(stream);
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
await call?.stopPublish(SfuModels.TrackType.SCREEN_SHARE);
|
|
77
|
-
}
|
|
78
|
-
}}
|
|
40
|
+
onClick={toggleScreenShare}
|
|
79
41
|
/>
|
|
80
42
|
</CompositeButton>
|
|
81
43
|
</PermissionNotification>
|
|
@@ -56,17 +56,17 @@ export const ToggleAudioPublishingButton = (
|
|
|
56
56
|
SfuModels.TrackType.AUDIO,
|
|
57
57
|
);
|
|
58
58
|
|
|
59
|
-
const { toggleAudioMuteState: handleClick,
|
|
59
|
+
const { toggleAudioMuteState: handleClick, isAwaitingPermission } =
|
|
60
60
|
useToggleAudioMuteState();
|
|
61
61
|
|
|
62
62
|
return (
|
|
63
63
|
<Restricted requiredGrants={[OwnCapability.SEND_AUDIO]}>
|
|
64
64
|
<PermissionNotification
|
|
65
65
|
permission={OwnCapability.SEND_AUDIO}
|
|
66
|
-
isAwaitingApproval={
|
|
67
|
-
messageApproved=
|
|
68
|
-
messageAwaitingApproval=
|
|
69
|
-
messageRevoked=
|
|
66
|
+
isAwaitingApproval={isAwaitingPermission}
|
|
67
|
+
messageApproved={t('You can now speak.')}
|
|
68
|
+
messageAwaitingApproval={t('Awaiting for an approval to speak.')}
|
|
69
|
+
messageRevoked={t('You can no longer speak.')}
|
|
70
70
|
>
|
|
71
71
|
<CompositeButton Menu={Menu} active={isAudioMute} caption={caption}>
|
|
72
72
|
<IconButton
|
|
@@ -55,17 +55,19 @@ export const ToggleVideoPublishingButton = (
|
|
|
55
55
|
SfuModels.TrackType.VIDEO,
|
|
56
56
|
);
|
|
57
57
|
|
|
58
|
-
const { toggleVideoMuteState: handleClick,
|
|
58
|
+
const { toggleVideoMuteState: handleClick, isAwaitingPermission } =
|
|
59
59
|
useToggleVideoMuteState();
|
|
60
60
|
|
|
61
61
|
return (
|
|
62
62
|
<Restricted requiredGrants={[OwnCapability.SEND_VIDEO]}>
|
|
63
63
|
<PermissionNotification
|
|
64
64
|
permission={OwnCapability.SEND_VIDEO}
|
|
65
|
-
isAwaitingApproval={
|
|
66
|
-
messageApproved=
|
|
67
|
-
messageAwaitingApproval=
|
|
68
|
-
|
|
65
|
+
isAwaitingApproval={isAwaitingPermission}
|
|
66
|
+
messageApproved={t('You can now share your video.')}
|
|
67
|
+
messageAwaitingApproval={t(
|
|
68
|
+
'Awaiting for an approval to share your video.',
|
|
69
|
+
)}
|
|
70
|
+
messageRevoked={t('You can no longer share your video.')}
|
|
69
71
|
>
|
|
70
72
|
<CompositeButton Menu={Menu} active={isVideoMute} caption={caption}>
|
|
71
73
|
<IconButton
|
|
@@ -16,11 +16,13 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
useCall,
|
|
18
18
|
useHasPermissions,
|
|
19
|
+
useI18n,
|
|
19
20
|
useLocalParticipant,
|
|
20
21
|
} from '@stream-io/video-react-bindings';
|
|
21
22
|
import clsx from 'clsx';
|
|
22
23
|
|
|
23
24
|
import { useFloatingUIPreset } from '../../hooks';
|
|
25
|
+
import { TranslatorFunction } from '@stream-io/i18n';
|
|
24
26
|
|
|
25
27
|
const byNameOrId = (a: UserResponse, b: UserResponse) => {
|
|
26
28
|
if (a.name && b.name && a.name < b.name) return -1;
|
|
@@ -30,6 +32,11 @@ const byNameOrId = (a: UserResponse, b: UserResponse) => {
|
|
|
30
32
|
return 0;
|
|
31
33
|
};
|
|
32
34
|
|
|
35
|
+
type HandleUpdatePermission = (
|
|
36
|
+
request: PermissionRequestEvent,
|
|
37
|
+
type: 'grant' | 'revoke' | 'dismiss',
|
|
38
|
+
) => () => Promise<void>;
|
|
39
|
+
|
|
33
40
|
export const PermissionRequests = () => {
|
|
34
41
|
const call = useCall();
|
|
35
42
|
const localParticipant = useLocalParticipant();
|
|
@@ -63,16 +70,18 @@ export const PermissionRequests = () => {
|
|
|
63
70
|
};
|
|
64
71
|
}, [call, canUpdateCallPermissions, localParticipant]);
|
|
65
72
|
|
|
66
|
-
const handleUpdatePermission = (
|
|
67
|
-
request: PermissionRequestEvent,
|
|
68
|
-
allow: boolean,
|
|
69
|
-
) => {
|
|
73
|
+
const handleUpdatePermission: HandleUpdatePermission = (request, type) => {
|
|
70
74
|
return async () => {
|
|
71
75
|
const { user, permissions } = request;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
switch (type) {
|
|
77
|
+
case 'grant':
|
|
78
|
+
await call?.grantPermissions(user.id, permissions);
|
|
79
|
+
break;
|
|
80
|
+
case 'revoke':
|
|
81
|
+
await call?.revokePermissions(user.id, permissions);
|
|
82
|
+
break;
|
|
83
|
+
default:
|
|
84
|
+
break;
|
|
76
85
|
}
|
|
77
86
|
setPermissionRequests((requests) =>
|
|
78
87
|
requests.filter((r) => r !== request),
|
|
@@ -122,10 +131,7 @@ export const PermissionRequests = () => {
|
|
|
122
131
|
|
|
123
132
|
export type PermissionRequestListProps = ComponentProps<'div'> & {
|
|
124
133
|
permissionRequests: PermissionRequestEvent[];
|
|
125
|
-
handleUpdatePermission:
|
|
126
|
-
request: PermissionRequestEvent,
|
|
127
|
-
allow: boolean,
|
|
128
|
-
) => () => Promise<void>;
|
|
134
|
+
handleUpdatePermission: HandleUpdatePermission;
|
|
129
135
|
};
|
|
130
136
|
|
|
131
137
|
export const PermissionRequestList = forwardRef<
|
|
@@ -133,6 +139,9 @@ export const PermissionRequestList = forwardRef<
|
|
|
133
139
|
PermissionRequestListProps
|
|
134
140
|
>((props, ref) => {
|
|
135
141
|
const { permissionRequests, handleUpdatePermission, ...rest } = props;
|
|
142
|
+
|
|
143
|
+
const { t } = useI18n();
|
|
144
|
+
|
|
136
145
|
return (
|
|
137
146
|
<div className="str-video__permission-requests-list" ref={ref} {...rest}>
|
|
138
147
|
{permissionRequests.map((request, reqIndex) => {
|
|
@@ -142,21 +151,28 @@ export const PermissionRequestList = forwardRef<
|
|
|
142
151
|
{permissions.map((permission) => (
|
|
143
152
|
<div className="str-video__permission-request" key={permission}>
|
|
144
153
|
<div className="str-video__permission-request__message">
|
|
145
|
-
{messageForPermission(user.name || user.id, permission)}
|
|
154
|
+
{messageForPermission(user.name || user.id, permission, t)}
|
|
146
155
|
</div>
|
|
147
156
|
<Button
|
|
148
157
|
className="str-video__permission-request__button--allow"
|
|
149
158
|
type="button"
|
|
150
|
-
onClick={handleUpdatePermission(request,
|
|
159
|
+
onClick={handleUpdatePermission(request, 'grant')}
|
|
151
160
|
>
|
|
152
|
-
Allow
|
|
161
|
+
{t('Allow')}
|
|
153
162
|
</Button>
|
|
154
163
|
<Button
|
|
155
164
|
className="str-video__permission-request__button--reject"
|
|
156
165
|
type="button"
|
|
157
|
-
onClick={handleUpdatePermission(request,
|
|
166
|
+
onClick={handleUpdatePermission(request, 'revoke')}
|
|
158
167
|
>
|
|
159
|
-
|
|
168
|
+
{t('Revoke')}
|
|
169
|
+
</Button>
|
|
170
|
+
<Button
|
|
171
|
+
className="str-video__permission-request__button--reject"
|
|
172
|
+
type="button"
|
|
173
|
+
onClick={handleUpdatePermission(request, 'dismiss')}
|
|
174
|
+
>
|
|
175
|
+
{t('Dismiss')}
|
|
160
176
|
</Button>
|
|
161
177
|
</div>
|
|
162
178
|
))}
|
|
@@ -179,15 +195,26 @@ const Button = (
|
|
|
179
195
|
);
|
|
180
196
|
};
|
|
181
197
|
|
|
182
|
-
const messageForPermission = (
|
|
198
|
+
const messageForPermission = (
|
|
199
|
+
userName: string,
|
|
200
|
+
permission: string,
|
|
201
|
+
t: TranslatorFunction,
|
|
202
|
+
) => {
|
|
183
203
|
switch (permission) {
|
|
184
204
|
case OwnCapability.SEND_AUDIO:
|
|
185
|
-
return
|
|
205
|
+
return t('{{ userName }} is requesting to speak', { userName });
|
|
186
206
|
case OwnCapability.SEND_VIDEO:
|
|
187
|
-
return
|
|
207
|
+
return t('{{ userName }} is requesting to share their camera', {
|
|
208
|
+
userName,
|
|
209
|
+
});
|
|
188
210
|
case OwnCapability.SCREENSHARE:
|
|
189
|
-
return
|
|
211
|
+
return t('{{ userName }} is requesting to present their screen', {
|
|
212
|
+
userName,
|
|
213
|
+
});
|
|
190
214
|
default:
|
|
191
|
-
return
|
|
215
|
+
return t('{{ userName }} is requesting permission: {{ permission }}', {
|
|
216
|
+
userName,
|
|
217
|
+
permission,
|
|
218
|
+
});
|
|
192
219
|
}
|
|
193
220
|
};
|
package/src/hooks/index.ts
CHANGED
|
@@ -2,3 +2,6 @@ export * from './useFloatingUIPreset';
|
|
|
2
2
|
export * from './useScrollPosition';
|
|
3
3
|
export * from './useToggleAudioMuteState';
|
|
4
4
|
export * from './useToggleVideoMuteState';
|
|
5
|
+
export * from './useToggleScreenShare';
|
|
6
|
+
export * from './useToggleCallRecording';
|
|
7
|
+
export * from './useRequestPermission';
|