@edifice.io/react 2.3.1 → 2.3.2

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.
@@ -6,10 +6,23 @@ export type NumberDate = number;
6
6
  /** Date formats we are going to deal with. */
7
7
  export type CoreDate = IsoDate | MongoDate | NumberDate;
8
8
  /**
9
- * Hook to compute user-friendly dates from various format.
9
+ * Custom React hook for date parsing, formatting, and localization.
10
+ *
11
+ * Provides utility functions to:
12
+ * - Parse various date formats and timestamps into Dayjs objects, respecting the current language.
13
+ * - Format dates in user-friendly ways, including "time ago", "yesterday", and localized date strings.
14
+ * - Compute elapsed durations from a given date to now.
15
+ *
16
+ * @returns An object containing:
17
+ * - `fromNow(date: CoreDate | NumberDate): string` — Returns a human-readable elapsed duration from the given date to now.
18
+ * - `formatDate(date: CoreDate, format?: 'short' | 'long' | 'abbr' | string): string` — Formats a date according to the specified format and current language.
19
+ * - `formatTimeAgo(date: CoreDate | NumberDate): string` — Returns a localized string representing how long ago the date was, with special handling for today, yesterday, and recent dates.
20
+ *
21
+ * @remarks
22
+ * - Uses the current language from the Edifice client context for localization.
10
23
  */
11
24
  export default function useDate(): {
12
- fromNow: (date: CoreDate | NumberDate) => string;
25
+ fromNow: (date: CoreDate) => string;
13
26
  formatDate: (date: CoreDate, format?: string) => string;
14
- formatTimeAgo: (date: CoreDate | NumberDate) => string;
27
+ formatTimeAgo: (date: CoreDate) => string;
15
28
  };
@@ -8,8 +8,8 @@ import "dayjs/locale/es.js";
8
8
  import "dayjs/locale/fr.js";
9
9
  import "dayjs/locale/it.js";
10
10
  import "dayjs/locale/pt.js";
11
- import { useEdificeClient } from "../../providers/EdificeClientProvider/EdificeClientProvider.hook.js";
12
11
  import { useTranslation } from "react-i18next";
12
+ import { useEdificeClient } from "../../providers/EdificeClientProvider/EdificeClientProvider.hook.js";
13
13
  dayjs.extend(relativeTime);
14
14
  dayjs.extend(customParseFormat);
15
15
  dayjs.extend(localizedFormat);
@@ -1,23 +1,18 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { forwardRef, useState, useRef, useImperativeHandle, useEffect, useCallback } from "react";
3
3
  import { odeServices } from "@edifice.io/client";
4
4
  import { getBestSupportedMimeType, convertMsToMS } from "@edifice.io/utilities";
5
5
  import { useTranslation } from "react-i18next";
6
- import SvgIconPause from "../../icons/components/IconPause.js";
7
- import SvgIconPlayFilled from "../../icons/components/IconPlayFilled.js";
8
- import SvgIconRecordStop from "../../icons/components/IconRecordStop.js";
9
6
  import SvgIconRecordVideo from "../../icons/components/IconRecordVideo.js";
10
7
  import SvgIconRecord from "../../icons/components/IconRecord.js";
11
- import SvgIconRefresh from "../../icons/components/IconRefresh.js";
12
- import SvgIconSave from "../../icons/components/IconSave.js";
8
+ import { useCameras } from "./useCameras.js";
9
+ import { VideoRecorderToolbar } from "./VideoRecorderToolbar.js";
13
10
  import useUpload from "../../../hooks/useUpload/useUpload.js";
14
- import useBrowserInfo from "../../../hooks/useBrowserInfo/useBrowserInfo.js";
15
11
  import FormControl from "../../../components/Form/FormControl.js";
16
12
  import Label from "../../../components/Label/Label.js";
17
13
  import Select from "../../../components/Select/Select.js";
18
14
  import LoadingScreen from "../../../components/LoadingScreen/LoadingScreen.js";
19
- import { Toolbar } from "../../../components/Toolbar/Toolbar.js";
20
- const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwardRef(({
15
+ const VideoRecorder = /* @__PURE__ */ forwardRef(({
21
16
  appCode,
22
17
  caption,
23
18
  onSuccess,
@@ -25,17 +20,14 @@ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwar
25
20
  onRecordUpdated,
26
21
  hideSaveAction = !1
27
22
  }, ref) => {
28
- const [maxDuration, setMaxDuration] = useState(18e4), [inputDevices, setInputDevices] = useState([]), [recording, setRecording] = useState(!1), [recorded, setRecorded] = useState(!1), [playing, setPlaying] = useState(!1), [saving, setSaving] = useState(!1), [saved, setSaved] = useState(!1), [mediaStreamConstraints, setMediaStreamConstraints] = useState({
29
- audio: !0,
30
- video: {
31
- facingMode: "environment",
32
- aspectRatio: VIDEO_WIDTH / VIDEO_HEIGHT
33
- }
34
- }), [stream, setStream] = useState(), [mimeType, setMimeType] = useState(""), [recordedChunks, setRecordedChunks] = useState([]), [recordedVideo, setRecordedVideo] = useState(), [recordedTime, setRecordedTime] = useState(0), [playedTime, setPlayedTime] = useState(0), videoRef = useRef(null), recorderRef = useRef(null), {
23
+ const {
24
+ inputDevices,
25
+ setPreferedDevice,
26
+ restartStream,
27
+ stream
28
+ } = useCameras(), [maxDuration, setMaxDuration] = useState(18e4), [recording, setRecording] = useState(!1), [recorded, setRecorded] = useState(!1), [playing, setPlaying] = useState(!1), [saving, setSaving] = useState(!1), [saved, setSaved] = useState(!1), [mimeType, setMimeType] = useState(""), [recordedChunks, setRecordedChunks] = useState([]), [recordedVideo, setRecordedVideo] = useState(), [recordedTime, setRecordedTime] = useState(0), [playedTime, setPlayedTime] = useState(0), videoRef = useRef(null), recorderRef = useRef(null), {
35
29
  uploadBlob
36
- } = useUpload(void 0, appCode), {
37
- device
38
- } = useBrowserInfo(navigator.userAgent);
30
+ } = useUpload(void 0, appCode);
39
31
  useImperativeHandle(ref, () => ({
40
32
  save: handleSave
41
33
  }));
@@ -43,10 +35,23 @@ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwar
43
35
  t
44
36
  } = useTranslation();
45
37
  useEffect(() => {
46
- initMaxDuration(), initInputDevices();
47
- }, []), useEffect(() => (stream || enableStream(mediaStreamConstraints), () => {
48
- stream && stream.getTracks().forEach((track) => track.stop());
49
- }), [stream]), useEffect(() => {
38
+ initMaxDuration();
39
+ }, []);
40
+ async function initMaxDuration() {
41
+ try {
42
+ const videoConfResponse = await odeServices.video().getVideoConf();
43
+ setMaxDuration(videoConfResponse.maxDuration * 60 * 1e3);
44
+ } catch {
45
+ setMaxDuration(3 * 60 * 1e3);
46
+ }
47
+ }
48
+ useEffect(() => {
49
+ try {
50
+ videoRef.current && (videoRef.current.src && (window.URL.revokeObjectURL(videoRef.current.src), videoRef.current.src = ""), videoRef.current.srcObject instanceof MediaStream && (videoRef.current.srcObject = null), stream && (videoRef.current.srcObject = stream, videoRef.current.autoplay = !0, videoRef.current.volume = 1, videoRef.current.muted = !0, videoRef.current.load()));
51
+ } catch (err) {
52
+ console.error(err);
53
+ }
54
+ }, [stream]), useEffect(() => {
50
55
  if (recordedChunks.length && !recording && videoRef.current) {
51
56
  const finalVideo = new Blob(recordedChunks, {
52
57
  type: mimeType
@@ -76,40 +81,7 @@ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwar
76
81
  };
77
82
  }
78
83
  }, [playing]);
79
- const initMaxDuration = async () => {
80
- const videoConfResponse = await odeServices.video().getVideoConf();
81
- setMaxDuration((videoConfResponse.maxDuration ?? 3) * 60 * 1e3);
82
- }, initInputDevices = async () => {
83
- const videoDevices = (await navigator.mediaDevices.enumerateDevices()).filter((device2) => device2.kind === "videoinput");
84
- switch (device.type) {
85
- case "mobile":
86
- case "tablet": {
87
- const backCamera = {
88
- deviceId: "environment",
89
- label: t("video.back.camera"),
90
- groupId: "",
91
- kind: "videoinput"
92
- }, frontCamera = {
93
- deviceId: "user",
94
- label: t("video.front.camera"),
95
- groupId: "",
96
- kind: "videoinput"
97
- };
98
- (videoDevices == null ? void 0 : videoDevices.length) > 1 ? setInputDevices([backCamera, frontCamera]) : setInputDevices([backCamera]);
99
- break;
100
- }
101
- default:
102
- setInputDevices(videoDevices);
103
- break;
104
- }
105
- }, enableStream = async (mediaStreamConstraints2) => {
106
- try {
107
- const mediaStream = await navigator.mediaDevices.getUserMedia(mediaStreamConstraints2);
108
- setStream(mediaStream), videoRef.current && (videoRef.current.src && (window.URL.revokeObjectURL(videoRef.current.src), videoRef.current.src = ""), videoRef.current.srcObject = mediaStream, videoRef.current.autoplay = !0, videoRef.current.volume = 1, videoRef.current.muted = !0);
109
- } catch (err) {
110
- console.error(err);
111
- }
112
- }, handleRecord = useCallback(() => {
84
+ const handleRecord = useCallback(() => {
113
85
  setRecording(!0), videoRef && videoRef.current && (videoRef.current.muted = !0);
114
86
  const mimeType2 = getBestSupportedMimeType();
115
87
  setMimeType(mimeType2), stream && (recorderRef.current = new MediaRecorder(stream, {
@@ -126,7 +98,7 @@ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwar
126
98
  var _a, _b;
127
99
  videoRef && videoRef.current && (videoRef.current.muted = !1), playing ? ((_b = videoRef == null ? void 0 : videoRef.current) == null || _b.pause(), setPlaying(!1)) : ((_a = videoRef == null ? void 0 : videoRef.current) == null || _a.play(), setPlaying(!0));
128
100
  }, [playing]), handleReset = () => {
129
- setRecorded(!1), setRecording(!1), setPlaying(!1), setSaved(!1), setRecordedTime(0), setRecordedChunks([]), setRecordedVideo(void 0), enableStream(mediaStreamConstraints), onRecordUpdated && onRecordUpdated();
101
+ setRecorded(!1), setRecording(!1), setPlaying(!1), setSaved(!1), setRecordedTime(0), setRecordedChunks([]), setRecordedVideo(void 0), restartStream(), onRecordUpdated && onRecordUpdated();
130
102
  }, handleSave = async () => {
131
103
  var _a;
132
104
  if ((_a = videoRef == null ? void 0 : videoRef.current) == null || _a.pause(), setSaving(!0), !recordedVideo) {
@@ -144,92 +116,11 @@ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwar
144
116
  }, handleInputDeviceChange = (option) => {
145
117
  var _a;
146
118
  const selectedDevice = inputDevices.find((inputDevice) => inputDevice.label === option);
147
- let mediaStreamConstraints2 = {};
148
- selectedDevice != null && selectedDevice.deviceId ? ((selectedDevice == null ? void 0 : selectedDevice.deviceId) === "environment" || (selectedDevice == null ? void 0 : selectedDevice.deviceId) === "user" ? mediaStreamConstraints2 = {
149
- audio: !0,
150
- video: {
151
- aspectRatio: VIDEO_WIDTH / VIDEO_HEIGHT,
152
- facingMode: selectedDevice == null ? void 0 : selectedDevice.deviceId
153
- }
154
- } : mediaStreamConstraints2 = {
155
- audio: !0,
156
- video: {
157
- aspectRatio: VIDEO_WIDTH / VIDEO_HEIGHT,
158
- deviceId: selectedDevice.deviceId
159
- }
160
- }, setMediaStreamConstraints(mediaStreamConstraints2), stream && (((_a = recorderRef.current) == null ? void 0 : _a.state) === "recording" && (recorderRef.current.requestData(), recorderRef.current.stop()), stream.getTracks().forEach((track) => track.stop()), setStream(void 0)), enableStream(mediaStreamConstraints2)) : console.error("Selected input device id is null");
119
+ ((_a = recorderRef.current) == null ? void 0 : _a.state) === "recording" && (recorderRef.current.requestData(), recorderRef.current.stop()), setPreferedDevice(selectedDevice);
161
120
  };
162
- useEffect(() => {
121
+ return useEffect(() => {
163
122
  recordedTime >= maxDuration && handleStop();
164
- }, [recordedTime, handleStop]);
165
- const toolbarItems = [{
166
- type: "icon",
167
- name: "record",
168
- props: {
169
- icon: /* @__PURE__ */ jsx(SvgIconRecord, { color: recording || recorded ? "" : "red" }),
170
- color: "danger",
171
- disabled: recording || recorded || saving,
172
- onClick: handleRecord,
173
- "aria-label": t("bbm.video.record.start")
174
- },
175
- tooltip: t("bbm.video.record.start")
176
- }, {
177
- type: "icon",
178
- name: "stop",
179
- props: {
180
- icon: /* @__PURE__ */ jsx(SvgIconRecordStop, {}),
181
- disabled: !recording || recorded || saving,
182
- onClick: handleStop,
183
- "aria-label": t("bbm.video.record.stop")
184
- },
185
- tooltip: t("bbm.video.record.stop")
186
- }, {
187
- type: "icon",
188
- name: "play",
189
- visibility: playing ? "hide" : "show",
190
- props: {
191
- icon: /* @__PURE__ */ jsx(SvgIconPlayFilled, {}),
192
- disabled: !recorded || saving,
193
- onClick: handlePlayPause,
194
- "aria-label": t("bbm.video.play.start")
195
- },
196
- tooltip: t("bbm.video.play.start")
197
- }, {
198
- type: "icon",
199
- name: "pause",
200
- visibility: playing ? "show" : "hide",
201
- props: {
202
- icon: /* @__PURE__ */ jsx(SvgIconPause, {}),
203
- disabled: !recorded || saving,
204
- onClick: handlePlayPause,
205
- "aria-label": t("bbm.video.play.pause")
206
- },
207
- tooltip: t("bbm.video.play.pause")
208
- }, {
209
- type: "divider"
210
- }, {
211
- type: "icon",
212
- name: "reset",
213
- props: {
214
- icon: /* @__PURE__ */ jsx(SvgIconRefresh, {}),
215
- disabled: !recorded || saving,
216
- onClick: handleReset,
217
- "aria-label": t("bbm.video.record.reset")
218
- },
219
- tooltip: t("bbm.video.record.reset")
220
- }, {
221
- type: "icon",
222
- name: "save",
223
- visibility: hideSaveAction ? "hide" : "show",
224
- props: {
225
- icon: /* @__PURE__ */ jsx(SvgIconSave, {}),
226
- disabled: !recorded || saving || saved,
227
- onClick: handleSave,
228
- "aria-label": t("bbm.video.record.save")
229
- },
230
- tooltip: t("bbm.video.record.save")
231
- }];
232
- return /* @__PURE__ */ jsxs("div", { className: "video-recorder d-flex flex-fill flex-column align-items-center pb-8", children: [
123
+ }, [recordedTime, handleStop]), /* @__PURE__ */ jsxs("div", { className: "video-recorder d-flex flex-fill flex-column align-items-center pb-8", children: [
233
124
  /* @__PURE__ */ jsx("div", { className: "video-recorder-caption d-none d-md-block", children: caption }),
234
125
  inputDevices.length > 1 && /* @__PURE__ */ jsx("div", { className: "video-recorder-devices mb-12", children: /* @__PURE__ */ jsxs(FormControl, { id: "selectInputDevice", children: [
235
126
  /* @__PURE__ */ jsx(Label, { className: "d-none d-md-block", children: t("bbm.video.record.select.devices.label") }),
@@ -255,7 +146,7 @@ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16, VideoRecorder = /* @__PURE__ */ forwar
255
146
  ] })
256
147
  ] })
257
148
  ] }),
258
- stream && /* @__PURE__ */ jsx(Toolbar, { items: toolbarItems, className: "position-absolute bottom-0 start-50 bg-white" })
149
+ stream && /* @__PURE__ */ jsx(VideoRecorderToolbar, { playing, recording, recorded, saving, saved, hideSaveAction, handleRecord, handleStop, handlePlayPause, handleReset, handleSave })
259
150
  ] }),
260
151
  saving && /* @__PURE__ */ jsx(LoadingScreen, { position: !1, caption: t("bbm.video.save.loader.caption") })
261
152
  ] });
@@ -0,0 +1,18 @@
1
+ import { WorkspaceElement } from '@edifice.io/client';
2
+ export interface VideoRecorderToolbarProps {
3
+ playing: boolean;
4
+ recording: boolean;
5
+ recorded: boolean;
6
+ saving: boolean;
7
+ saved: boolean;
8
+ hideSaveAction: boolean;
9
+ handleRecord: () => void;
10
+ handleStop: () => void;
11
+ handlePlayPause: () => void;
12
+ handleReset: () => void;
13
+ handleSave: () => void;
14
+ }
15
+ export interface VideoRecorderRef {
16
+ save: () => Promise<WorkspaceElement | undefined>;
17
+ }
18
+ export declare const VideoRecorderToolbar: ({ playing, recording, recorded, saving, saved, hideSaveAction, handleRecord, handleStop, handlePlayPause, handleReset, handleSave, }: VideoRecorderToolbarProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,96 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useTranslation } from "react-i18next";
3
+ import SvgIconPause from "../../icons/components/IconPause.js";
4
+ import SvgIconPlayFilled from "../../icons/components/IconPlayFilled.js";
5
+ import SvgIconRecordStop from "../../icons/components/IconRecordStop.js";
6
+ import SvgIconRecord from "../../icons/components/IconRecord.js";
7
+ import SvgIconRefresh from "../../icons/components/IconRefresh.js";
8
+ import SvgIconSave from "../../icons/components/IconSave.js";
9
+ import { Toolbar } from "../../../components/Toolbar/Toolbar.js";
10
+ const VideoRecorderToolbar = ({
11
+ playing,
12
+ recording,
13
+ recorded,
14
+ saving,
15
+ saved,
16
+ hideSaveAction,
17
+ handleRecord,
18
+ handleStop,
19
+ handlePlayPause,
20
+ handleReset,
21
+ handleSave
22
+ }) => {
23
+ const {
24
+ t
25
+ } = useTranslation(), toolbarItems = [{
26
+ type: "icon",
27
+ name: "record",
28
+ props: {
29
+ icon: /* @__PURE__ */ jsx(SvgIconRecord, { color: recording || recorded ? "" : "red" }),
30
+ color: "danger",
31
+ disabled: recording || recorded || saving,
32
+ onClick: handleRecord,
33
+ "aria-label": t("bbm.video.record.start")
34
+ },
35
+ tooltip: t("bbm.video.record.start")
36
+ }, {
37
+ type: "icon",
38
+ name: "stop",
39
+ props: {
40
+ icon: /* @__PURE__ */ jsx(SvgIconRecordStop, {}),
41
+ disabled: !recording || recorded || saving,
42
+ onClick: handleStop,
43
+ "aria-label": t("bbm.video.record.stop")
44
+ },
45
+ tooltip: t("bbm.video.record.stop")
46
+ }, {
47
+ type: "icon",
48
+ name: "play",
49
+ visibility: playing ? "hide" : "show",
50
+ props: {
51
+ icon: /* @__PURE__ */ jsx(SvgIconPlayFilled, {}),
52
+ disabled: !recorded || saving,
53
+ onClick: handlePlayPause,
54
+ "aria-label": t("bbm.video.play.start")
55
+ },
56
+ tooltip: t("bbm.video.play.start")
57
+ }, {
58
+ type: "icon",
59
+ name: "pause",
60
+ visibility: playing ? "show" : "hide",
61
+ props: {
62
+ icon: /* @__PURE__ */ jsx(SvgIconPause, {}),
63
+ disabled: !recorded || saving,
64
+ onClick: handlePlayPause,
65
+ "aria-label": t("bbm.video.play.pause")
66
+ },
67
+ tooltip: t("bbm.video.play.pause")
68
+ }, {
69
+ type: "divider"
70
+ }, {
71
+ type: "icon",
72
+ name: "reset",
73
+ props: {
74
+ icon: /* @__PURE__ */ jsx(SvgIconRefresh, {}),
75
+ disabled: !recorded || saving,
76
+ onClick: handleReset,
77
+ "aria-label": t("bbm.video.record.reset")
78
+ },
79
+ tooltip: t("bbm.video.record.reset")
80
+ }, {
81
+ type: "icon",
82
+ name: "save",
83
+ visibility: hideSaveAction ? "hide" : "show",
84
+ props: {
85
+ icon: /* @__PURE__ */ jsx(SvgIconSave, {}),
86
+ disabled: !recorded || saving || saved,
87
+ onClick: handleSave,
88
+ "aria-label": t("bbm.video.record.save")
89
+ },
90
+ tooltip: t("bbm.video.record.save")
91
+ }];
92
+ return /* @__PURE__ */ jsx(Toolbar, { items: toolbarItems, className: "position-absolute bottom-0 start-50 bg-white" });
93
+ };
94
+ export {
95
+ VideoRecorderToolbar
96
+ };
@@ -0,0 +1,10 @@
1
+ export declare function useCameras(): {
2
+ /** Readonly list (array) of available video input devices. */
3
+ inputDevices: MediaDeviceInfo[];
4
+ /** Select which input video device to use. */
5
+ setPreferedDevice: (device?: MediaDeviceInfo) => void;
6
+ /** The current video stream. */
7
+ stream: MediaStream | undefined;
8
+ /** Start a video stream from the default or prefered device. */
9
+ restartStream: () => void;
10
+ };
@@ -0,0 +1,86 @@
1
+ import { useState, useEffect } from "react";
2
+ import { useTranslation } from "react-i18next";
3
+ import useBrowserInfo from "../../../hooks/useBrowserInfo/useBrowserInfo.js";
4
+ const VIDEO_HEIGHT = 9, VIDEO_WIDTH = 16;
5
+ function useCameras() {
6
+ const {
7
+ device
8
+ } = useBrowserInfo(navigator.userAgent), {
9
+ t
10
+ } = useTranslation(), [inputDevices, setInputDevices] = useState([]), [mediaStreamConstraints, setMediaStreamConstraints] = useState({
11
+ audio: !0,
12
+ video: !0
13
+ }), [stream, setStream] = useState();
14
+ async function getVideoInputDevices() {
15
+ return (await navigator.mediaDevices.enumerateDevices()).filter((device2) => device2.kind === "videoinput");
16
+ }
17
+ const enableStream = async (constraints) => {
18
+ try {
19
+ const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
20
+ setStream(mediaStream);
21
+ } catch (err) {
22
+ console.error(err);
23
+ }
24
+ }, restartStream = () => {
25
+ stream == null || stream.getTracks().forEach((track) => track.stop()), enableStream(mediaStreamConstraints);
26
+ }, setPreferedDevice = (device2) => {
27
+ let constraints = {};
28
+ device2 != null && device2.deviceId ? (device2.deviceId === "environment" || device2.deviceId === "user" ? constraints = {
29
+ audio: !0,
30
+ video: {
31
+ aspectRatio: VIDEO_WIDTH / VIDEO_HEIGHT,
32
+ facingMode: device2 == null ? void 0 : device2.deviceId
33
+ }
34
+ } : constraints = {
35
+ audio: !0,
36
+ video: {
37
+ aspectRatio: VIDEO_WIDTH / VIDEO_HEIGHT,
38
+ deviceId: {
39
+ exact: device2.deviceId
40
+ }
41
+ }
42
+ }, setMediaStreamConstraints(constraints)) : console.error("Selected input device id is null");
43
+ };
44
+ return useEffect(() => {
45
+ restartStream();
46
+ }, [mediaStreamConstraints]), useEffect(() => {
47
+ async function initInputDevices() {
48
+ await enableStream(mediaStreamConstraints);
49
+ const videoDevices = await getVideoInputDevices();
50
+ switch (device.type) {
51
+ case "mobile":
52
+ case "tablet": {
53
+ const backCamera = {
54
+ deviceId: "environment",
55
+ label: t("video.back.camera"),
56
+ groupId: "",
57
+ kind: "videoinput"
58
+ }, frontCamera = {
59
+ deviceId: "user",
60
+ label: t("video.front.camera"),
61
+ groupId: "",
62
+ kind: "videoinput"
63
+ };
64
+ (videoDevices == null ? void 0 : videoDevices.length) > 1 ? setInputDevices([backCamera, frontCamera]) : setInputDevices([backCamera]);
65
+ break;
66
+ }
67
+ default:
68
+ setInputDevices(videoDevices);
69
+ break;
70
+ }
71
+ }
72
+ initInputDevices();
73
+ }, []), {
74
+ /** Readonly list (array) of available video input devices. */
75
+ inputDevices,
76
+ /** Select which input video device to use. */
77
+ setPreferedDevice,
78
+ /** The current video stream. */
79
+ stream,
80
+ /** Start a video stream from the default or prefered device. */
81
+ restartStream
82
+ };
83
+ }
84
+ export {
85
+ useCameras
86
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edifice.io/react",
3
- "version": "2.3.1",
3
+ "version": "2.3.2",
4
4
  "description": "Edifice React Library",
5
5
  "keywords": [
6
6
  "react",
@@ -130,9 +130,9 @@
130
130
  "react-slugify": "^3.0.3",
131
131
  "swiper": "^10.1.0",
132
132
  "ua-parser-js": "^1.0.36",
133
- "@edifice.io/bootstrap": "2.3.1",
134
- "@edifice.io/tiptap-extensions": "2.3.1",
135
- "@edifice.io/utilities": "2.3.1"
133
+ "@edifice.io/bootstrap": "2.3.2",
134
+ "@edifice.io/tiptap-extensions": "2.3.2",
135
+ "@edifice.io/utilities": "2.3.2"
136
136
  },
137
137
  "devDependencies": {
138
138
  "@babel/plugin-transform-react-pure-annotations": "^7.23.3",
@@ -163,8 +163,8 @@
163
163
  "vite": "^5.4.11",
164
164
  "vite-plugin-dts": "^4.1.0",
165
165
  "vite-tsconfig-paths": "^5.0.1",
166
- "@edifice.io/client": "2.3.1",
167
- "@edifice.io/config": "2.3.1"
166
+ "@edifice.io/client": "2.3.2",
167
+ "@edifice.io/config": "2.3.2"
168
168
  },
169
169
  "peerDependencies": {
170
170
  "@react-spring/web": "^9.7.5",