@stream-io/video-react-sdk 1.3.6 → 1.4.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 +13 -0
- package/dist/index.cjs.js +35 -96
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +37 -95
- package/dist/index.es.js.map +1 -1
- package/dist/latency-chart-Bj5OSYzg.es.js +57 -0
- package/dist/latency-chart-Bj5OSYzg.es.js.map +1 -0
- package/dist/latency-chart-CpL1M_s0.cjs.js +59 -0
- package/dist/latency-chart-CpL1M_s0.cjs.js.map +1 -0
- package/dist/src/components/CallStats/CallStats.d.ts +5 -15
- package/dist/src/components/CallStats/CallStatsLatencyChart.d.ts +2 -1
- package/dist/src/components/CallStats/index.d.ts +0 -1
- package/package.json +12 -9
- package/src/components/CallStats/CallStats.tsx +47 -48
- package/src/components/CallStats/CallStatsLatencyChart.tsx +30 -41
- package/src/components/CallStats/index.ts +0 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Chart, CategoryScale, LinearScale, LineElement, PointElement } from 'chart.js';
|
|
3
|
+
import { Line } from 'react-chartjs-2';
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
|
|
6
|
+
// NOTE: this is a side effect by definition, but this component is
|
|
7
|
+
// isolated in a separate chunk, and it won't affect the rest of the app.
|
|
8
|
+
// See CallStats.tsx for more details.
|
|
9
|
+
Chart.register(CategoryScale, LinearScale, LineElement, PointElement);
|
|
10
|
+
const CallStatsLatencyChart = (props) => {
|
|
11
|
+
const { values } = props;
|
|
12
|
+
let max = 0;
|
|
13
|
+
const data = {
|
|
14
|
+
labels: values.map((point) => {
|
|
15
|
+
const date = new Date(point.x * 1000);
|
|
16
|
+
return `${date.getHours()}:${date.getMinutes()}`;
|
|
17
|
+
}),
|
|
18
|
+
datasets: [
|
|
19
|
+
{
|
|
20
|
+
data: values.map((point) => {
|
|
21
|
+
const { y } = point;
|
|
22
|
+
max = Math.max(max, y);
|
|
23
|
+
return point;
|
|
24
|
+
}),
|
|
25
|
+
borderColor: '#00e2a1',
|
|
26
|
+
backgroundColor: '#00e2a1',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
const options = useMemo(() => getLineOptions(max), [max]);
|
|
31
|
+
return (jsx("div", { className: "str-video__call-stats-line-chart-container", children: jsx(Line, { options: options, data: data, className: "str-video__call-stats__latencychart" }) }));
|
|
32
|
+
};
|
|
33
|
+
const getLineOptions = (max) => ({
|
|
34
|
+
maintainAspectRatio: false,
|
|
35
|
+
animation: { duration: 0 },
|
|
36
|
+
elements: {
|
|
37
|
+
line: { borderWidth: 1 },
|
|
38
|
+
point: { radius: 2 },
|
|
39
|
+
},
|
|
40
|
+
scales: {
|
|
41
|
+
y: {
|
|
42
|
+
position: 'right',
|
|
43
|
+
stacked: true,
|
|
44
|
+
min: 0,
|
|
45
|
+
max: Math.max(180, Math.ceil((max + 10) / 10) * 10),
|
|
46
|
+
grid: { display: true, color: '#979ca0' },
|
|
47
|
+
ticks: { stepSize: 30 },
|
|
48
|
+
},
|
|
49
|
+
x: {
|
|
50
|
+
grid: { display: false },
|
|
51
|
+
ticks: { display: false },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export { CallStatsLatencyChart as default };
|
|
57
|
+
//# sourceMappingURL=latency-chart-Bj5OSYzg.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"latency-chart-Bj5OSYzg.es.js","sources":["../../src/components/CallStats/CallStatsLatencyChart.tsx"],"sourcesContent":["import {\n CategoryScale,\n Chart as ChartJS,\n ChartData,\n ChartOptions,\n LinearScale,\n LineElement,\n PointElement,\n} from 'chart.js';\nimport { Line } from 'react-chartjs-2';\nimport { useMemo } from 'react';\n\n// NOTE: this is a side effect by definition, but this component is\n// isolated in a separate chunk, and it won't affect the rest of the app.\n// See CallStats.tsx for more details.\nChartJS.register(CategoryScale, LinearScale, LineElement, PointElement);\n\nconst CallStatsLatencyChart = (props: {\n values: Array<{ x: number; y: number }>;\n}) => {\n const { values } = props;\n let max = 0;\n const data: ChartData<'line'> = {\n labels: values.map((point) => {\n const date = new Date(point.x * 1000);\n return `${date.getHours()}:${date.getMinutes()}`;\n }),\n datasets: [\n {\n data: values.map((point) => {\n const { y } = point;\n max = Math.max(max, y);\n return point;\n }),\n borderColor: '#00e2a1',\n backgroundColor: '#00e2a1',\n },\n ],\n };\n\n const options = useMemo(() => getLineOptions(max), [max]);\n return (\n <div className=\"str-video__call-stats-line-chart-container\">\n <Line\n options={options}\n data={data}\n className=\"str-video__call-stats__latencychart\"\n />\n </div>\n );\n};\n\nexport default CallStatsLatencyChart;\n\nconst getLineOptions = (max: number): ChartOptions<'line'> => ({\n maintainAspectRatio: false,\n animation: { duration: 0 },\n elements: {\n line: { borderWidth: 1 },\n point: { radius: 2 },\n },\n scales: {\n y: {\n position: 'right',\n stacked: true,\n min: 0,\n max: Math.max(180, Math.ceil((max + 10) / 10) * 10),\n grid: { display: true, color: '#979ca0' },\n ticks: { stepSize: 30 },\n },\n x: {\n grid: { display: false },\n ticks: { display: false },\n },\n },\n});\n"],"names":["ChartJS","_jsx"],"mappings":";;;;;AAYA;AACA;AACA;AACAA,KAAO,CAAC,QAAQ,CAAC,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;AAExE,MAAM,qBAAqB,GAAG,CAAC,KAE9B,KAAI;AACH,IAAA,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACzB,IAAI,GAAG,GAAG,CAAC,CAAC;AACZ,IAAA,MAAM,IAAI,GAAsB;QAC9B,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,KAAI;YAC3B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;YACtC,OAAO,CAAA,EAAG,IAAI,CAAC,QAAQ,EAAE,CAAI,CAAA,EAAA,IAAI,CAAC,UAAU,EAAE,CAAA,CAAE,CAAC;AACnD,SAAC,CAAC;AACF,QAAA,QAAQ,EAAE;AACR,YAAA;gBACE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,KAAI;AACzB,oBAAA,MAAM,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC;oBACpB,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AACvB,oBAAA,OAAO,KAAK,CAAC;AACf,iBAAC,CAAC;AACF,gBAAA,WAAW,EAAE,SAAS;AACtB,gBAAA,eAAe,EAAE,SAAS;AAC3B,aAAA;AACF,SAAA;KACF,CAAC;AAEF,IAAA,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,QACEC,aAAK,SAAS,EAAC,4CAA4C,EACzD,QAAA,EAAAA,GAAA,CAAC,IAAI,EAAA,EACH,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,IAAI,EACV,SAAS,EAAC,qCAAqC,EAAA,CAC/C,EACE,CAAA,EACN;AACJ,EAAE;AAIF,MAAM,cAAc,GAAG,CAAC,GAAW,MAA4B;AAC7D,IAAA,mBAAmB,EAAE,KAAK;AAC1B,IAAA,SAAS,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE;AAC1B,IAAA,QAAQ,EAAE;AACR,QAAA,IAAI,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE;AACxB,QAAA,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE;AACrB,KAAA;AACD,IAAA,MAAM,EAAE;AACN,QAAA,CAAC,EAAE;AACD,YAAA,QAAQ,EAAE,OAAO;AACjB,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,GAAG,EAAE,CAAC;YACN,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC;YACnD,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE;AACzC,YAAA,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;AACxB,SAAA;AACD,QAAA,CAAC,EAAE;AACD,YAAA,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;AACxB,YAAA,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;AAC1B,SAAA;AACF,KAAA;AACF,CAAA,CAAC;;;;"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var chart_js = require('chart.js');
|
|
5
|
+
var reactChartjs2 = require('react-chartjs-2');
|
|
6
|
+
var react = require('react');
|
|
7
|
+
|
|
8
|
+
// NOTE: this is a side effect by definition, but this component is
|
|
9
|
+
// isolated in a separate chunk, and it won't affect the rest of the app.
|
|
10
|
+
// See CallStats.tsx for more details.
|
|
11
|
+
chart_js.Chart.register(chart_js.CategoryScale, chart_js.LinearScale, chart_js.LineElement, chart_js.PointElement);
|
|
12
|
+
const CallStatsLatencyChart = (props) => {
|
|
13
|
+
const { values } = props;
|
|
14
|
+
let max = 0;
|
|
15
|
+
const data = {
|
|
16
|
+
labels: values.map((point) => {
|
|
17
|
+
const date = new Date(point.x * 1000);
|
|
18
|
+
return `${date.getHours()}:${date.getMinutes()}`;
|
|
19
|
+
}),
|
|
20
|
+
datasets: [
|
|
21
|
+
{
|
|
22
|
+
data: values.map((point) => {
|
|
23
|
+
const { y } = point;
|
|
24
|
+
max = Math.max(max, y);
|
|
25
|
+
return point;
|
|
26
|
+
}),
|
|
27
|
+
borderColor: '#00e2a1',
|
|
28
|
+
backgroundColor: '#00e2a1',
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
const options = react.useMemo(() => getLineOptions(max), [max]);
|
|
33
|
+
return (jsxRuntime.jsx("div", { className: "str-video__call-stats-line-chart-container", children: jsxRuntime.jsx(reactChartjs2.Line, { options: options, data: data, className: "str-video__call-stats__latencychart" }) }));
|
|
34
|
+
};
|
|
35
|
+
const getLineOptions = (max) => ({
|
|
36
|
+
maintainAspectRatio: false,
|
|
37
|
+
animation: { duration: 0 },
|
|
38
|
+
elements: {
|
|
39
|
+
line: { borderWidth: 1 },
|
|
40
|
+
point: { radius: 2 },
|
|
41
|
+
},
|
|
42
|
+
scales: {
|
|
43
|
+
y: {
|
|
44
|
+
position: 'right',
|
|
45
|
+
stacked: true,
|
|
46
|
+
min: 0,
|
|
47
|
+
max: Math.max(180, Math.ceil((max + 10) / 10) * 10),
|
|
48
|
+
grid: { display: true, color: '#979ca0' },
|
|
49
|
+
ticks: { stepSize: 30 },
|
|
50
|
+
},
|
|
51
|
+
x: {
|
|
52
|
+
grid: { display: false },
|
|
53
|
+
ticks: { display: false },
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
exports.default = CallStatsLatencyChart;
|
|
59
|
+
//# sourceMappingURL=latency-chart-CpL1M_s0.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"latency-chart-CpL1M_s0.cjs.js","sources":["../../src/components/CallStats/CallStatsLatencyChart.tsx"],"sourcesContent":["import {\n CategoryScale,\n Chart as ChartJS,\n ChartData,\n ChartOptions,\n LinearScale,\n LineElement,\n PointElement,\n} from 'chart.js';\nimport { Line } from 'react-chartjs-2';\nimport { useMemo } from 'react';\n\n// NOTE: this is a side effect by definition, but this component is\n// isolated in a separate chunk, and it won't affect the rest of the app.\n// See CallStats.tsx for more details.\nChartJS.register(CategoryScale, LinearScale, LineElement, PointElement);\n\nconst CallStatsLatencyChart = (props: {\n values: Array<{ x: number; y: number }>;\n}) => {\n const { values } = props;\n let max = 0;\n const data: ChartData<'line'> = {\n labels: values.map((point) => {\n const date = new Date(point.x * 1000);\n return `${date.getHours()}:${date.getMinutes()}`;\n }),\n datasets: [\n {\n data: values.map((point) => {\n const { y } = point;\n max = Math.max(max, y);\n return point;\n }),\n borderColor: '#00e2a1',\n backgroundColor: '#00e2a1',\n },\n ],\n };\n\n const options = useMemo(() => getLineOptions(max), [max]);\n return (\n <div className=\"str-video__call-stats-line-chart-container\">\n <Line\n options={options}\n data={data}\n className=\"str-video__call-stats__latencychart\"\n />\n </div>\n );\n};\n\nexport default CallStatsLatencyChart;\n\nconst getLineOptions = (max: number): ChartOptions<'line'> => ({\n maintainAspectRatio: false,\n animation: { duration: 0 },\n elements: {\n line: { borderWidth: 1 },\n point: { radius: 2 },\n },\n scales: {\n y: {\n position: 'right',\n stacked: true,\n min: 0,\n max: Math.max(180, Math.ceil((max + 10) / 10) * 10),\n grid: { display: true, color: '#979ca0' },\n ticks: { stepSize: 30 },\n },\n x: {\n grid: { display: false },\n ticks: { display: false },\n },\n },\n});\n"],"names":["ChartJS","CategoryScale","LinearScale","LineElement","PointElement","useMemo","_jsx","Line"],"mappings":";;;;;;;AAYA;AACA;AACA;AACAA,cAAO,CAAC,QAAQ,CAACC,sBAAa,EAAEC,oBAAW,EAAEC,oBAAW,EAAEC,qBAAY,CAAC,CAAC;AAExE,MAAM,qBAAqB,GAAG,CAAC,KAE9B,KAAI;AACH,IAAA,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACzB,IAAI,GAAG,GAAG,CAAC,CAAC;AACZ,IAAA,MAAM,IAAI,GAAsB;QAC9B,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,KAAI;YAC3B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;YACtC,OAAO,CAAA,EAAG,IAAI,CAAC,QAAQ,EAAE,CAAI,CAAA,EAAA,IAAI,CAAC,UAAU,EAAE,CAAA,CAAE,CAAC;AACnD,SAAC,CAAC;AACF,QAAA,QAAQ,EAAE;AACR,YAAA;gBACE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,KAAI;AACzB,oBAAA,MAAM,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC;oBACpB,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AACvB,oBAAA,OAAO,KAAK,CAAC;AACf,iBAAC,CAAC;AACF,gBAAA,WAAW,EAAE,SAAS;AACtB,gBAAA,eAAe,EAAE,SAAS;AAC3B,aAAA;AACF,SAAA;KACF,CAAC;AAEF,IAAA,MAAM,OAAO,GAAGC,aAAO,CAAC,MAAM,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,QACEC,wBAAK,SAAS,EAAC,4CAA4C,EACzD,QAAA,EAAAA,cAAA,CAACC,kBAAI,EAAA,EACH,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,IAAI,EACV,SAAS,EAAC,qCAAqC,EAAA,CAC/C,EACE,CAAA,EACN;AACJ,EAAE;AAIF,MAAM,cAAc,GAAG,CAAC,GAAW,MAA4B;AAC7D,IAAA,mBAAmB,EAAE,KAAK;AAC1B,IAAA,SAAS,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE;AAC1B,IAAA,QAAQ,EAAE;AACR,QAAA,IAAI,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE;AACxB,QAAA,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE;AACrB,KAAA;AACD,IAAA,MAAM,EAAE;AACN,QAAA,CAAC,EAAE;AACD,YAAA,QAAQ,EAAE,OAAO;AACjB,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,GAAG,EAAE,CAAC;YACN,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC;YACnD,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE;AACzC,YAAA,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;AACxB,SAAA;AACD,QAAA,CAAC,EAAE;AACD,YAAA,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;AACxB,YAAA,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;AAC1B,SAAA;AACF,KAAA;AACF,CAAA,CAAC;;;;"}
|
|
@@ -1,21 +1,11 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
export
|
|
3
|
-
GOOD = "Good",
|
|
4
|
-
OK = "Ok",
|
|
5
|
-
BAD = "Bad"
|
|
6
|
-
}
|
|
7
|
-
export type Status = Statuses.GOOD | Statuses.OK | Statuses.BAD;
|
|
8
|
-
export declare const CallStats: (props: {
|
|
2
|
+
export type CallStatsProps = {
|
|
9
3
|
latencyLowBound?: number;
|
|
10
4
|
latencyHighBound?: number;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export declare const StatsTag: ({ children, status, }: {
|
|
16
|
-
children: ReactNode;
|
|
17
|
-
status: Statuses.GOOD | Statuses.OK | Statuses.BAD;
|
|
18
|
-
}) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
showCodecInfo?: boolean;
|
|
6
|
+
LatencyChartSuspenseFallback?: ReactNode;
|
|
7
|
+
};
|
|
8
|
+
export declare const CallStats: (props: CallStatsProps) => import("react/jsx-runtime").JSX.Element;
|
|
19
9
|
export declare const StatCard: (props: {
|
|
20
10
|
label: string;
|
|
21
11
|
value: string | ReactNode;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stream-io/video-react-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"packageManager": "yarn@3.2.4",
|
|
5
5
|
"main": "./dist/index.cjs.js",
|
|
6
6
|
"module": "./dist/index.es.js",
|
|
@@ -27,12 +27,15 @@
|
|
|
27
27
|
"LICENSE",
|
|
28
28
|
"CHANGELOG.md"
|
|
29
29
|
],
|
|
30
|
+
"sideEffects": [
|
|
31
|
+
"*.css"
|
|
32
|
+
],
|
|
30
33
|
"dependencies": {
|
|
31
|
-
"@floating-ui/react": "^0.26.
|
|
32
|
-
"@stream-io/video-client": "1.
|
|
33
|
-
"@stream-io/video-filters-web": "0.1.
|
|
34
|
-
"@stream-io/video-react-bindings": "1.0.
|
|
35
|
-
"chart.js": "^4.4.
|
|
34
|
+
"@floating-ui/react": "^0.26.24",
|
|
35
|
+
"@stream-io/video-client": "1.7.0",
|
|
36
|
+
"@stream-io/video-filters-web": "0.1.4",
|
|
37
|
+
"@stream-io/video-react-bindings": "1.0.6",
|
|
38
|
+
"chart.js": "^4.4.4",
|
|
36
39
|
"clsx": "^2.0.0",
|
|
37
40
|
"react-chartjs-2": "^5.2.0"
|
|
38
41
|
},
|
|
@@ -42,16 +45,16 @@
|
|
|
42
45
|
},
|
|
43
46
|
"devDependencies": {
|
|
44
47
|
"@rollup/plugin-json": "^6.1.0",
|
|
45
|
-
"@rollup/plugin-replace": "^5.0.
|
|
48
|
+
"@rollup/plugin-replace": "^5.0.7",
|
|
46
49
|
"@rollup/plugin-typescript": "^11.1.6",
|
|
47
|
-
"@stream-io/audio-filters-web": "^0.2.
|
|
50
|
+
"@stream-io/audio-filters-web": "^0.2.2",
|
|
48
51
|
"@stream-io/video-styling": "^1.0.6",
|
|
49
52
|
"@types/react": "^18.3.2",
|
|
50
53
|
"@types/react-dom": "^18.3.0",
|
|
51
54
|
"react": "^18.3.1",
|
|
52
55
|
"react-dom": "^18.3.1",
|
|
53
56
|
"rimraf": "^5.0.7",
|
|
54
|
-
"rollup": "^
|
|
57
|
+
"rollup": "^4.22.0",
|
|
55
58
|
"typescript": "^5.5.2"
|
|
56
59
|
}
|
|
57
60
|
}
|
|
@@ -1,52 +1,35 @@
|
|
|
1
|
-
import { ReactNode, useEffect, useRef, useState } from 'react';
|
|
1
|
+
import { lazy, ReactNode, Suspense, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
3
|
import {
|
|
4
4
|
AggregatedStatsReport,
|
|
5
5
|
CallStatsReport,
|
|
6
6
|
} from '@stream-io/video-client';
|
|
7
7
|
import { useCallStateHooks, useI18n } from '@stream-io/video-react-bindings';
|
|
8
|
-
|
|
9
8
|
import { useFloating, useHover, useInteractions } from '@floating-ui/react';
|
|
10
|
-
|
|
11
|
-
import { CallStatsLatencyChart } from './CallStatsLatencyChart';
|
|
12
9
|
import { Icon } from '../Icon';
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
const CallStatsLatencyChart = lazy(() => import('./CallStatsLatencyChart'));
|
|
12
|
+
|
|
13
|
+
enum Status {
|
|
15
14
|
GOOD = 'Good',
|
|
16
15
|
OK = 'Ok',
|
|
17
16
|
BAD = 'Bad',
|
|
18
17
|
}
|
|
19
|
-
export type Status = Statuses.GOOD | Statuses.OK | Statuses.BAD;
|
|
20
|
-
|
|
21
|
-
const statsStatus = ({
|
|
22
|
-
value,
|
|
23
|
-
lowBound,
|
|
24
|
-
highBound,
|
|
25
|
-
}: {
|
|
26
|
-
value: number;
|
|
27
|
-
lowBound: number;
|
|
28
|
-
highBound: number;
|
|
29
|
-
}): Status => {
|
|
30
|
-
if (value <= lowBound) {
|
|
31
|
-
return Statuses.GOOD;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (value >= lowBound && value <= highBound) {
|
|
35
|
-
return Statuses.OK;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (value >= highBound) {
|
|
39
|
-
return Statuses.BAD;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return Statuses.GOOD;
|
|
43
|
-
};
|
|
44
18
|
|
|
45
|
-
export
|
|
19
|
+
export type CallStatsProps = {
|
|
46
20
|
latencyLowBound?: number;
|
|
47
21
|
latencyHighBound?: number;
|
|
48
|
-
|
|
49
|
-
|
|
22
|
+
showCodecInfo?: boolean;
|
|
23
|
+
LatencyChartSuspenseFallback?: ReactNode;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const CallStats = (props: CallStatsProps) => {
|
|
27
|
+
const {
|
|
28
|
+
latencyLowBound = 75,
|
|
29
|
+
latencyHighBound = 400,
|
|
30
|
+
showCodecInfo = false,
|
|
31
|
+
LatencyChartSuspenseFallback = null,
|
|
32
|
+
} = props;
|
|
50
33
|
const [latencyBuffer, setLatencyBuffer] = useState<
|
|
51
34
|
Array<{ x: number; y: number }>
|
|
52
35
|
>(() => {
|
|
@@ -116,7 +99,9 @@ export const CallStats = (props: {
|
|
|
116
99
|
</div>
|
|
117
100
|
|
|
118
101
|
<div className="str-video__call-stats__latencychart">
|
|
119
|
-
<
|
|
102
|
+
<Suspense fallback={LatencyChartSuspenseFallback}>
|
|
103
|
+
<CallStatsLatencyChart values={latencyBuffer} />
|
|
104
|
+
</Suspense>
|
|
120
105
|
</div>
|
|
121
106
|
|
|
122
107
|
<div className="str-video__call-stats__header">
|
|
@@ -156,7 +141,7 @@ export const CallStats = (props: {
|
|
|
156
141
|
}}
|
|
157
142
|
/>
|
|
158
143
|
<StatCard
|
|
159
|
-
label={t('Publish resolution')}
|
|
144
|
+
label={`${t('Publish resolution')}${showCodecInfo ? formatCodec(callStatsReport) : ''}`}
|
|
160
145
|
value={toFrameSize(callStatsReport.publisherStats)}
|
|
161
146
|
/>
|
|
162
147
|
<StatCard
|
|
@@ -180,7 +165,7 @@ export const CallStats = (props: {
|
|
|
180
165
|
);
|
|
181
166
|
};
|
|
182
167
|
|
|
183
|
-
|
|
168
|
+
const StatCardExplanation = (props: { description: string }) => {
|
|
184
169
|
const { description } = props;
|
|
185
170
|
const [isOpen, setIsOpen] = useState(false);
|
|
186
171
|
|
|
@@ -216,19 +201,14 @@ export const StatCardExplanation = (props: { description: string }) => {
|
|
|
216
201
|
);
|
|
217
202
|
};
|
|
218
203
|
|
|
219
|
-
|
|
220
|
-
children,
|
|
221
|
-
status = Statuses.GOOD,
|
|
222
|
-
}: {
|
|
223
|
-
children: ReactNode;
|
|
224
|
-
status: Statuses.GOOD | Statuses.OK | Statuses.BAD;
|
|
225
|
-
}) => {
|
|
204
|
+
const StatsTag = (props: { children: ReactNode; status: Status }) => {
|
|
205
|
+
const { children, status } = props;
|
|
226
206
|
return (
|
|
227
207
|
<div
|
|
228
208
|
className={clsx('str-video__call-stats__tag', {
|
|
229
|
-
'str-video__call-stats__tag--good': status ===
|
|
230
|
-
'str-video__call-stats__tag--ok': status ===
|
|
231
|
-
'str-video__call-stats__tag--bad': status ===
|
|
209
|
+
'str-video__call-stats__tag--good': status === Status.GOOD,
|
|
210
|
+
'str-video__call-stats__tag--ok': status === Status.OK,
|
|
211
|
+
'str-video__call-stats__tag--bad': status === Status.BAD,
|
|
232
212
|
})}
|
|
233
213
|
>
|
|
234
214
|
<div className="str-video__call-stats__tag__text">{children}</div>
|
|
@@ -245,7 +225,7 @@ export const StatCard = (props: {
|
|
|
245
225
|
const { label, value, description, comparison } = props;
|
|
246
226
|
|
|
247
227
|
const { t } = useI18n();
|
|
248
|
-
const status = comparison ?
|
|
228
|
+
const status = comparison ? toStatus(comparison) : undefined;
|
|
249
229
|
|
|
250
230
|
return (
|
|
251
231
|
<div className="str-video__call-stats__card">
|
|
@@ -256,11 +236,23 @@ export const StatCard = (props: {
|
|
|
256
236
|
</div>
|
|
257
237
|
<div className="str-video__call-stats__card-value">{value}</div>
|
|
258
238
|
</div>
|
|
259
|
-
{
|
|
239
|
+
{status && <StatsTag status={status}>{t(status)}</StatsTag>}
|
|
260
240
|
</div>
|
|
261
241
|
);
|
|
262
242
|
};
|
|
263
243
|
|
|
244
|
+
const toStatus = (config: {
|
|
245
|
+
value: number;
|
|
246
|
+
lowBound: number;
|
|
247
|
+
highBound: number;
|
|
248
|
+
}): Status => {
|
|
249
|
+
const { value, lowBound, highBound } = config;
|
|
250
|
+
if (value <= lowBound) return Status.GOOD;
|
|
251
|
+
if (value >= lowBound && value <= highBound) return Status.OK;
|
|
252
|
+
if (value >= highBound) return Status.BAD;
|
|
253
|
+
return Status.GOOD;
|
|
254
|
+
};
|
|
255
|
+
|
|
264
256
|
const toFrameSize = (stats: AggregatedStatsReport) => {
|
|
265
257
|
const {
|
|
266
258
|
highestFrameWidth: w,
|
|
@@ -277,6 +269,13 @@ const toFrameSize = (stats: AggregatedStatsReport) => {
|
|
|
277
269
|
return size;
|
|
278
270
|
};
|
|
279
271
|
|
|
272
|
+
const formatCodec = (callStatsReport: CallStatsReport): string => {
|
|
273
|
+
const { codec } = callStatsReport.publisherStats;
|
|
274
|
+
if (!codec) return '';
|
|
275
|
+
const [, name] = codec.split('/');
|
|
276
|
+
return name ? ` (${name})` : '';
|
|
277
|
+
};
|
|
278
|
+
|
|
280
279
|
const calculatePublishBitrate = (
|
|
281
280
|
previousCallStatsReport: CallStatsReport,
|
|
282
281
|
callStatsReport: CallStatsReport,
|
|
@@ -10,9 +10,12 @@ import {
|
|
|
10
10
|
import { Line } from 'react-chartjs-2';
|
|
11
11
|
import { useMemo } from 'react';
|
|
12
12
|
|
|
13
|
+
// NOTE: this is a side effect by definition, but this component is
|
|
14
|
+
// isolated in a separate chunk, and it won't affect the rest of the app.
|
|
15
|
+
// See CallStats.tsx for more details.
|
|
13
16
|
ChartJS.register(CategoryScale, LinearScale, LineElement, PointElement);
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
const CallStatsLatencyChart = (props: {
|
|
16
19
|
values: Array<{ x: number; y: number }>;
|
|
17
20
|
}) => {
|
|
18
21
|
const { values } = props;
|
|
@@ -35,46 +38,7 @@ export const CallStatsLatencyChart = (props: {
|
|
|
35
38
|
],
|
|
36
39
|
};
|
|
37
40
|
|
|
38
|
-
const options = useMemo
|
|
39
|
-
return {
|
|
40
|
-
maintainAspectRatio: false,
|
|
41
|
-
animation: {
|
|
42
|
-
duration: 0,
|
|
43
|
-
},
|
|
44
|
-
elements: {
|
|
45
|
-
line: {
|
|
46
|
-
borderWidth: 1,
|
|
47
|
-
},
|
|
48
|
-
point: {
|
|
49
|
-
radius: 2,
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
scales: {
|
|
53
|
-
y: {
|
|
54
|
-
position: 'right',
|
|
55
|
-
stacked: true,
|
|
56
|
-
min: 0,
|
|
57
|
-
max: Math.max(180, Math.ceil((max + 10) / 10) * 10),
|
|
58
|
-
grid: {
|
|
59
|
-
display: true,
|
|
60
|
-
color: '#979ca0',
|
|
61
|
-
},
|
|
62
|
-
ticks: {
|
|
63
|
-
stepSize: 30,
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
x: {
|
|
67
|
-
grid: {
|
|
68
|
-
display: false,
|
|
69
|
-
},
|
|
70
|
-
ticks: {
|
|
71
|
-
display: false,
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
}, [max]);
|
|
77
|
-
|
|
41
|
+
const options = useMemo(() => getLineOptions(max), [max]);
|
|
78
42
|
return (
|
|
79
43
|
<div className="str-video__call-stats-line-chart-container">
|
|
80
44
|
<Line
|
|
@@ -85,3 +49,28 @@ export const CallStatsLatencyChart = (props: {
|
|
|
85
49
|
</div>
|
|
86
50
|
);
|
|
87
51
|
};
|
|
52
|
+
|
|
53
|
+
export default CallStatsLatencyChart;
|
|
54
|
+
|
|
55
|
+
const getLineOptions = (max: number): ChartOptions<'line'> => ({
|
|
56
|
+
maintainAspectRatio: false,
|
|
57
|
+
animation: { duration: 0 },
|
|
58
|
+
elements: {
|
|
59
|
+
line: { borderWidth: 1 },
|
|
60
|
+
point: { radius: 2 },
|
|
61
|
+
},
|
|
62
|
+
scales: {
|
|
63
|
+
y: {
|
|
64
|
+
position: 'right',
|
|
65
|
+
stacked: true,
|
|
66
|
+
min: 0,
|
|
67
|
+
max: Math.max(180, Math.ceil((max + 10) / 10) * 10),
|
|
68
|
+
grid: { display: true, color: '#979ca0' },
|
|
69
|
+
ticks: { stepSize: 30 },
|
|
70
|
+
},
|
|
71
|
+
x: {
|
|
72
|
+
grid: { display: false },
|
|
73
|
+
ticks: { display: false },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
});
|