@stream-io/video-react-sdk 1.12.1 → 1.12.3

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.
@@ -2,3 +2,4 @@ export * from './useFloatingUIPreset';
2
2
  export * from './usePersistedDevicePreferences';
3
3
  export * from './useScrollPosition';
4
4
  export * from './useRequestPermission';
5
+ export * from './useDeviceList';
@@ -0,0 +1,17 @@
1
+ export interface DeviceListItem {
2
+ deviceId: string;
3
+ label: string;
4
+ isSelected: boolean;
5
+ }
6
+ /**
7
+ * Utility hook that helps render a list of devices or implement a device selector.
8
+ * Compared to someting like `useCameraState().devices`, it has some handy features:
9
+ * 1. Adds the "Default" device to the list if applicable (either the user did not
10
+ * select a device, or a previously selected device is no longer available).
11
+ * 2. Maps the device list to a format more suitable for rendering.
12
+ */
13
+ export declare function useDeviceList(devices: MediaDeviceInfo[], selectedDeviceId: string | undefined): {
14
+ deviceList: DeviceListItem[];
15
+ selectedDeviceInfo: DeviceListItem;
16
+ selectedIndex: number;
17
+ };
@@ -4,7 +4,7 @@ export declare const useFloatingUIPreset: ({ middleware, placement, strategy, of
4
4
  }) => {
5
5
  refs: {
6
6
  reference: import("react").MutableRefObject<import("@floating-ui/react-dom").ReferenceType | null>;
7
- floating: import("react").MutableRefObject<HTMLElement | null>;
7
+ floating: React.MutableRefObject<HTMLElement | null>;
8
8
  setReference: (node: import("@floating-ui/react-dom").ReferenceType | null) => void;
9
9
  setFloating: (node: HTMLElement | null) => void;
10
10
  } & import("@floating-ui/react").ExtendedRefs<import("@floating-ui/react").ReferenceType>;
@@ -21,11 +21,11 @@ export declare const useFloatingUIPreset: ({ middleware, placement, strategy, of
21
21
  middlewareData: import("@floating-ui/core").MiddlewareData;
22
22
  isPositioned: boolean;
23
23
  update: () => void;
24
- floatingStyles: import("react").CSSProperties;
24
+ floatingStyles: React.CSSProperties;
25
25
  open: boolean;
26
26
  onOpenChange: (open: boolean, event?: Event, reason?: import("@floating-ui/react").OpenChangeReason) => void;
27
27
  events: import("@floating-ui/react").FloatingEvents;
28
- dataRef: import("react").MutableRefObject<import("@floating-ui/react").ContextData>;
28
+ dataRef: React.MutableRefObject<import("@floating-ui/react").ContextData>;
29
29
  nodeId: string | undefined;
30
30
  floatingId: string;
31
31
  refs: import("@floating-ui/react").ExtendedRefs<import("@floating-ui/react").ReferenceType>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-react-sdk",
3
- "version": "1.12.1",
3
+ "version": "1.12.3",
4
4
  "packageManager": "yarn@3.2.4",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "module": "./dist/index.es.js",
@@ -31,9 +31,9 @@
31
31
  ],
32
32
  "dependencies": {
33
33
  "@floating-ui/react": "^0.26.24",
34
- "@stream-io/video-client": "1.17.1",
34
+ "@stream-io/video-client": "1.18.0",
35
35
  "@stream-io/video-filters-web": "0.1.7",
36
- "@stream-io/video-react-bindings": "1.5.1",
36
+ "@stream-io/video-react-bindings": "1.5.2",
37
37
  "chart.js": "^4.4.4",
38
38
  "clsx": "^2.0.0",
39
39
  "react-chartjs-2": "^5.3.0"
@@ -1,9 +1,9 @@
1
1
  import clsx from 'clsx';
2
2
  import { ChangeEventHandler, useCallback } from 'react';
3
3
 
4
+ import { useDeviceList } from '../../hooks/useDeviceList';
4
5
  import { DropDownSelect, DropDownSelectOption } from '../DropdownSelect';
5
6
  import { useMenuContext } from '../Menu';
6
- import { useI18n } from '@stream-io/video-react-bindings';
7
7
 
8
8
  type DeviceSelectorOptionProps = {
9
9
  id: string;
@@ -60,7 +60,7 @@ const DeviceSelectorList = (props: {
60
60
  }) => {
61
61
  const { devices = [], selectedDeviceId, title, type, onChange } = props;
62
62
  const { close } = useMenuContext();
63
- const { t } = useI18n();
63
+ const { deviceList } = useDeviceList(devices, selectedDeviceId);
64
64
 
65
65
  return (
66
66
  <div className="str-video__device-settings__device-kind">
@@ -69,34 +69,25 @@ const DeviceSelectorList = (props: {
69
69
  {title}
70
70
  </div>
71
71
  )}
72
- {devices.length === 0 ? (
73
- <DeviceSelectorOption
74
- id={`${type}--default`}
75
- label={t('Default')}
76
- name={type}
77
- defaultChecked
78
- value="default"
79
- />
80
- ) : (
81
- devices.map((device) => {
82
- return (
83
- <DeviceSelectorOption
84
- id={`${type}--${device.deviceId}`}
85
- value={device.deviceId}
86
- label={device.label}
87
- key={device.deviceId}
88
- onChange={(e) => {
89
- onChange?.(e.target.value);
90
- close?.();
91
- }}
92
- name={type}
93
- selected={
94
- device.deviceId === selectedDeviceId || devices.length === 1
72
+ {deviceList.map((device) => {
73
+ return (
74
+ <DeviceSelectorOption
75
+ id={`${type}--${device.deviceId}`}
76
+ value={device.deviceId}
77
+ label={device.label}
78
+ key={device.deviceId}
79
+ onChange={(e) => {
80
+ const deviceId = e.target.value;
81
+ if (deviceId !== 'default') {
82
+ onChange?.(deviceId);
95
83
  }
96
- />
97
- );
98
- })
99
- )}
84
+ close?.();
85
+ }}
86
+ name={type}
87
+ selected={device.isSelected}
88
+ />
89
+ );
90
+ })}
100
91
  </div>
101
92
  );
102
93
  };
@@ -109,17 +100,19 @@ const DeviceSelectorDropdown = (props: {
109
100
  icon: string;
110
101
  }) => {
111
102
  const { devices = [], selectedDeviceId, title, onChange, icon } = props;
112
- const { t } = useI18n();
113
-
114
- const selectedIndex = devices.findIndex(
115
- (d) => d.deviceId === selectedDeviceId,
103
+ const { deviceList, selectedDeviceInfo, selectedIndex } = useDeviceList(
104
+ devices,
105
+ selectedDeviceId,
116
106
  );
117
107
 
118
108
  const handleSelect = useCallback(
119
109
  (index: number) => {
120
- onChange?.(devices[index].deviceId);
110
+ const deviceId = deviceList[index].deviceId;
111
+ if (deviceId !== 'default') {
112
+ onChange?.(deviceId);
113
+ }
121
114
  },
122
- [devices, onChange],
115
+ [deviceList, onChange],
123
116
  );
124
117
 
125
118
  return (
@@ -130,23 +123,17 @@ const DeviceSelectorDropdown = (props: {
130
123
  <DropDownSelect
131
124
  icon={icon}
132
125
  defaultSelectedIndex={selectedIndex}
133
- defaultSelectedLabel={devices[selectedIndex]?.label ?? t('Default')}
126
+ defaultSelectedLabel={selectedDeviceInfo.label}
134
127
  handleSelect={handleSelect}
135
128
  >
136
- {devices.length === 0 ? (
137
- <DropDownSelectOption icon={icon} label={t('Default')} selected />
138
- ) : (
139
- devices.map((device) => (
140
- <DropDownSelectOption
141
- key={device.deviceId}
142
- icon={icon}
143
- label={device.label}
144
- selected={
145
- device.deviceId === selectedDeviceId || devices.length === 1
146
- }
147
- />
148
- ))
149
- )}
129
+ {deviceList.map((device) => (
130
+ <DropDownSelectOption
131
+ key={device.deviceId}
132
+ icon={icon}
133
+ label={device.label}
134
+ selected={device.isSelected}
135
+ />
136
+ ))}
150
137
  </DropDownSelect>
151
138
  </div>
152
139
  );
@@ -2,3 +2,4 @@ export * from './useFloatingUIPreset';
2
2
  export * from './usePersistedDevicePreferences';
3
3
  export * from './useScrollPosition';
4
4
  export * from './useRequestPermission';
5
+ export * from './useDeviceList';
@@ -0,0 +1,57 @@
1
+ import { useI18n } from '@stream-io/video-react-bindings';
2
+ import { useMemo } from 'react';
3
+
4
+ export interface DeviceListItem {
5
+ deviceId: string;
6
+ label: string;
7
+ isSelected: boolean;
8
+ }
9
+
10
+ /**
11
+ * Utility hook that helps render a list of devices or implement a device selector.
12
+ * Compared to someting like `useCameraState().devices`, it has some handy features:
13
+ * 1. Adds the "Default" device to the list if applicable (either the user did not
14
+ * select a device, or a previously selected device is no longer available).
15
+ * 2. Maps the device list to a format more suitable for rendering.
16
+ */
17
+ export function useDeviceList(
18
+ devices: MediaDeviceInfo[],
19
+ selectedDeviceId: string | undefined,
20
+ ): {
21
+ deviceList: DeviceListItem[];
22
+ selectedDeviceInfo: DeviceListItem;
23
+ selectedIndex: number;
24
+ } {
25
+ const { t } = useI18n();
26
+
27
+ return useMemo(() => {
28
+ let selectedDeviceInfo: DeviceListItem | null = null;
29
+ let selectedIndex: number | null = null;
30
+
31
+ const deviceList: DeviceListItem[] = devices.map((d, i) => {
32
+ const isSelected = d.deviceId === selectedDeviceId;
33
+ const device = { deviceId: d.deviceId, label: d.label, isSelected };
34
+
35
+ if (isSelected) {
36
+ selectedDeviceInfo = device;
37
+ selectedIndex = i;
38
+ }
39
+
40
+ return device;
41
+ });
42
+
43
+ if (selectedDeviceInfo === null || selectedIndex === null) {
44
+ const defaultDevice = {
45
+ deviceId: 'default',
46
+ label: t('Default'),
47
+ isSelected: true,
48
+ };
49
+
50
+ selectedDeviceInfo = defaultDevice;
51
+ selectedIndex = 0;
52
+ deviceList.unshift(defaultDevice);
53
+ }
54
+
55
+ return { deviceList, selectedDeviceInfo, selectedIndex };
56
+ }, [devices, selectedDeviceId, t]);
57
+ }