diff --git a/net/web/src/session/conversation/topicItem/TopicItem.jsx b/net/web/src/session/conversation/topicItem/TopicItem.jsx
index 0136eb9b..fd10666d 100644
--- a/net/web/src/session/conversation/topicItem/TopicItem.jsx
+++ b/net/web/src/session/conversation/topicItem/TopicItem.jsx
@@ -8,10 +8,10 @@ import { ExclamationCircleOutlined, DeleteOutlined, EditOutlined, FireOutlined,
import { Carousel } from 'carousel/Carousel';
import { useTopicItem } from './useTopicItem.hook';
-export function TopicItem({ host, sealed, topic, update, remove }) {
+export function TopicItem({ host, contentKey, sealed, topic, update, remove }) {
const [ modal, modalContext ] = Modal.useModal();
- const { state, actions } = useTopicItem();
+ const { state, actions } = useTopicItem(topic, contentKey);
const removeTopic = () => {
modal.confirm({
@@ -52,16 +52,14 @@ export function TopicItem({ host, sealed, topic, update, remove }) {
};
const renderAsset = (asset, idx) => {
- if (asset.image) {
- return
+ if (asset.type === 'image') {
+ return
}
- if (asset.video) {
- return
+ if (asset.type === 'video') {
+ return
}
- if (asset.audio) {
- return
+ if (asset.type === 'audio') {
+ return
}
return <>>
}
@@ -113,7 +111,7 @@ export function TopicItem({ host, sealed, topic, update, remove }) {
)}
{ topic.transform === 'complete' && (
-
+
)}
>
diff --git a/net/web/src/session/conversation/topicItem/audioAsset/AudioAsset.jsx b/net/web/src/session/conversation/topicItem/audioAsset/AudioAsset.jsx
index 8b9fef97..d074dce6 100644
--- a/net/web/src/session/conversation/topicItem/audioAsset/AudioAsset.jsx
+++ b/net/web/src/session/conversation/topicItem/audioAsset/AudioAsset.jsx
@@ -1,37 +1,21 @@
import React, { useEffect, useState, useRef } from 'react';
-import { Spin } from 'antd';
+import { Modal, Spin } from 'antd';
import ReactResizeDetector from 'react-resize-detector';
import { PlayCircleOutlined, MinusCircleOutlined, SoundOutlined } from '@ant-design/icons';
-import { AudioAssetWrapper } from './AudioAsset.styled';
+import { AudioAssetWrapper, AudioModalWrapper } from './AudioAsset.styled';
+import { useAudioAsset } from './useAudioAsset.hook';
import background from 'images/audio.png';
-export function AudioAsset({ label, audioUrl }) {
+export function AudioAsset({ asset }) {
- const [active, setActive] = useState(false);
const [width, setWidth] = useState(0);
- const [ready, setReady] = useState(false);
const [playing, setPlaying] = useState(true);
- const [url, setUrl] = useState(null);
+
+ const { actions, state } = useAudioAsset(asset);
const audio = useRef(null);
- useEffect(() => {
- setActive(false);
- setReady(false);
- setPlaying(true);
- setUrl(null);
- }, [label, audioUrl]);
-
- const onActivate = () => {
- setUrl(audioUrl);
- setActive(true);
- }
-
- const onReady = () => {
- setReady(true);
- }
-
const play = (on) => {
setPlaying(on);
if (on) {
@@ -54,32 +38,44 @@ export function AudioAsset({ label, audioUrl }) {
![audio background]({background})
-
- { !active && (
-
onActivate()}>
-
-
- )}
- { active && !ready && (
-
-
-
- )}
- { active && ready && playing && (
-
play(false)}>
-
-
- )}
- { active && ready && !playing && (
-
- )}
-
+
+
+
{ asset.label }
-
{ label }
+
+
+
+
+ { state.loading && state.error && (
+
+
+
+ )}
+ { state.loading && !state.error && (
+
+
+
+ )}
+ { !state.ready && !state.loading && (
+
+
+
+ )}
+ { state.ready && !state.loading && playing && (
+ play(false)}>
+
+
+ )}
+ { state.ready && !state.loading && !playing && (
+
+ )}
+ { asset.label }
+
+
)
}
diff --git a/net/web/src/session/conversation/topicItem/audioAsset/AudioAsset.styled.js b/net/web/src/session/conversation/topicItem/audioAsset/AudioAsset.styled.js
index 595fbcf7..e3dcd5c0 100644
--- a/net/web/src/session/conversation/topicItem/audioAsset/AudioAsset.styled.js
+++ b/net/web/src/session/conversation/topicItem/audioAsset/AudioAsset.styled.js
@@ -1,4 +1,5 @@
import styled from 'styled-components';
+import Colors from 'constants/Colors';
export const AudioAssetWrapper = styled.div`
position: relative;
@@ -41,3 +42,48 @@ export const AudioAssetWrapper = styled.div`
`;
+export const AudioModalWrapper = styled.div`
+ width: 256px;
+ height: 256px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: #aaaaaa;
+
+ .background {
+ width: 256px;
+ height: 256px;
+ }
+
+ .control {
+ position: absolute;
+ }
+
+ .label {
+ padding-top: 8px;
+ position: absolute;
+ top: 0;
+ font-size: 20px;
+ }
+
+ .failed {
+ position: absolute;
+ color: white;
+ border-radius: 8px;
+
+ .ant-spin-dot-item {
+ background-color: ${Colors.alert};
+ }
+ }
+
+ .loading {
+ position: absolute;
+ color: white;
+ border-radius: 8px;
+
+ .ant-spin-dot-item {
+ background-color: ${Colors.white};
+ }
+ }
+`;
+
diff --git a/net/web/src/session/conversation/topicItem/audioAsset/useAudioAsset.hook.js b/net/web/src/session/conversation/topicItem/audioAsset/useAudioAsset.hook.js
new file mode 100644
index 00000000..6dd0d523
--- /dev/null
+++ b/net/web/src/session/conversation/topicItem/audioAsset/useAudioAsset.hook.js
@@ -0,0 +1,55 @@
+import { useState, useRef } from 'react';
+
+export function useAudioAsset(asset) {
+
+ const revoke = useRef();
+ const index = useRef(0);
+
+ const [state, setState] = useState({
+ active: false,
+ loading: false,
+ error: false,
+ ready: false,
+ url: null,
+ });
+
+ const updateState = (value) => {
+ setState((s) => ({ ...s, ...value }));
+ }
+
+ const actions = {
+ setActive: async () => {
+ if (asset.encrypted) {
+ try {
+ const view = index.current;
+ updateState({ active: true, ready: false, error: false, loading: true, url: null });
+ const blob = await asset.getDecryptedBlob(() => view != index.current);
+ const url = URL.createObjectURL(blob);
+ revoke.current = url;
+ updateState({ loading: false, url });
+ }
+ catch (err) {
+ console.log(err);
+ updateState({ error: true });
+ }
+ }
+ else {
+ updateState({ active: true, loading: false, url: asset.full });
+ }
+ },
+ clearActive: () => {
+ index.current += 1;
+ updateState({ active: false, url: null });
+ if (revoke.current) {
+ URL.revokeObjectURL(revoke.current);
+ revoke.current = null;
+ }
+ },
+ ready: () => {
+ updateState({ ready: true });
+ }
+ };
+
+ return { state, actions };
+}
+
diff --git a/net/web/src/session/conversation/topicItem/imageAsset/ImageAsset.jsx b/net/web/src/session/conversation/topicItem/imageAsset/ImageAsset.jsx
index 4ebf15af..57514679 100644
--- a/net/web/src/session/conversation/topicItem/imageAsset/ImageAsset.jsx
+++ b/net/web/src/session/conversation/topicItem/imageAsset/ImageAsset.jsx
@@ -1,12 +1,12 @@
import React, { useState } from 'react';
-import { Modal } from 'antd';
+import { Modal, Spin } from 'antd';
import ReactResizeDetector from 'react-resize-detector';
-import { ImageAssetWrapper } from './ImageAsset.styled';
+import { ImageAssetWrapper, ImageModalWrapper } from './ImageAsset.styled';
import { useImageAsset } from './useImageAsset.hook';
-export function ImageAsset({ thumbUrl, fullUrl }) {
+export function ImageAsset({ asset }) {
- const { state, actions } = useImageAsset();
+ const { state, actions } = useImageAsset(asset);
const [dimension, setDimension] = useState({ width: 0, height: 0 });
const popout = () => {
@@ -29,16 +29,31 @@ export function ImageAsset({ thumbUrl, fullUrl }) {
if (width !== dimension.width || height !== dimension.height) {
setDimension({ width, height });
}
- return
![]({thumbUrl})
+ return
![]({asset.thumb})
}}
-
-
![topic asset]({fullUrl})
-
+
+
+
![topic asset]({asset.thumb})
+ { !state.error && (
+
+
+
+ )}
+ { state.error && (
+
+
+
+ )}
+ { !state.loading && (
+
![topic asset]({state.url})
+ )}
+
+
diff --git a/net/web/src/session/conversation/topicItem/imageAsset/ImageAsset.styled.js b/net/web/src/session/conversation/topicItem/imageAsset/ImageAsset.styled.js
index bbaeb449..1d4e6cb8 100644
--- a/net/web/src/session/conversation/topicItem/imageAsset/ImageAsset.styled.js
+++ b/net/web/src/session/conversation/topicItem/imageAsset/ImageAsset.styled.js
@@ -1,4 +1,5 @@
import styled from 'styled-components';
+import Colors from 'constants/Colors';
export const ImageAssetWrapper = styled.div`
position: relative;
@@ -40,3 +41,44 @@ export const ImageAssetWrapper = styled.div`
}
`;
+export const ImageModalWrapper = styled.div`
+ .frame {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: ${Colors.black};
+ position: relative;
+ }
+
+ .thumb {
+ opacity: 0.5;
+ width: 100%;
+ object-fit: contain;
+ }
+
+ .full {
+ width: 100%;
+ object-fit: contain;
+ position: absolute;
+ }
+
+ .failed {
+ position: absolute;
+ color: white;
+ border-radius: 8px;
+
+ .ant-spin-dot-item {
+ background-color: ${Colors.alert};
+ }
+ }
+
+ .loading {
+ position: absolute;
+ color: white;
+ border-radius: 8px;
+
+ .ant-spin-dot-item {
+ background-color: ${Colors.white};
+ }
+ }
+`;
diff --git a/net/web/src/session/conversation/topicItem/imageAsset/useImageAsset.hook.js b/net/web/src/session/conversation/topicItem/imageAsset/useImageAsset.hook.js
index 45030865..e278d43f 100644
--- a/net/web/src/session/conversation/topicItem/imageAsset/useImageAsset.hook.js
+++ b/net/web/src/session/conversation/topicItem/imageAsset/useImageAsset.hook.js
@@ -1,11 +1,17 @@
-import { useState } from 'react';
+import { useState, useRef } from 'react';
-export function useImageAsset() {
+export function useImageAsset(asset) {
+
+ const revoke = useRef();
+ const index = useRef(0);
const [state, setState] = useState({
popout: false,
width: 0,
height: 0,
+ loading: false,
+ error: false,
+ url: null,
});
const updateState = (value) => {
@@ -13,11 +19,32 @@ export function useImageAsset() {
}
const actions = {
- setPopout: (width, height) => {
- updateState({ popout: true, width, height });
+ setPopout: async (width, height) => {
+ if (asset.encrypted) {
+ try {
+ const view = index.current;
+ updateState({ popout: true, width, height, error: false, loading: true, url: null });
+ const blob = await asset.getDecryptedBlob(() => view != index.current);
+ const url = URL.createObjectURL(blob);
+ updateState({ loading: false, url });
+ revoke.current = url;
+ }
+ catch(err) {
+ console.log(err);
+ updateState({ error: true });
+ }
+ }
+ else {
+ updateState({ popout: true, width, height, loading: false, url: asset.full });
+ }
},
clearPopout: () => {
+ index.current += 1;
updateState({ popout: false });
+ if (revoke.current) {
+ URL.revokeObjectURL(revoke.current);
+ revoke.current = null;
+ }
},
};
diff --git a/net/web/src/session/conversation/topicItem/useTopicItem.hook.js b/net/web/src/session/conversation/topicItem/useTopicItem.hook.js
index e56d20cd..6fd97da5 100644
--- a/net/web/src/session/conversation/topicItem/useTopicItem.hook.js
+++ b/net/web/src/session/conversation/topicItem/useTopicItem.hook.js
@@ -1,16 +1,91 @@
-import { useState } from 'react';
+import { useState, useEffect } from 'react';
+import { checkResponse, fetchWithTimeout } from 'api/fetchUtil';
+import { decryptBlock } from 'context/sealUtil';
-export function useTopicItem() {
+export function useTopicItem(topic, contentKey) {
const [state, setState] = useState({
editing: false,
message: null,
+ assets: [],
});
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
+ const base64ToUint8Array = (base64) => {
+ var binaryString = atob(base64);
+ var bytes = new Uint8Array(binaryString.length);
+ for (var i = 0; i < binaryString.length; i++) {
+ bytes[i] = binaryString.charCodeAt(i);
+ }
+ return bytes;
+ }
+
+ useEffect(() => {
+ const assets = [];
+ if (topic.assets?.length) {
+ topic.assets.forEach(asset => {
+ if (asset.encrypted) {
+ const encrypted = true;
+ const { type, thumb, label, parts } = asset.encrypted;
+ const getDecryptedBlob = async (abort) => {
+ let pos = 0;
+ let len = 0;
+
+ const slices = []
+ for (let i = 0; i < parts.length; i++) {
+ if (abort()) {
+ throw new Error("asset unseal aborted");
+ }
+ const part = parts[i];
+ const url = topic.assetUrl(part.partId, topic.id);
+ const response = await fetchWithTimeout(url, { method: 'GET' });
+ const block = await response.text();
+ const decrypted = decryptBlock(block, part.blockIv, contentKey);
+ const slice = base64ToUint8Array(decrypted);
+ slices.push(slice);
+ len += slice.byteLength;
+ };
+
+ const data = new Uint8Array(len)
+ for (let i = 0; i < slices.length; i++) {
+ const slice = slices[i];
+ data.set(slice, pos);
+ pos += slice.byteLength
+ }
+ return new Blob([data]);
+ }
+ assets.push({ type, thumb, label, encrypted, getDecryptedBlob });
+ }
+ else {
+ const encrypted = false
+ if (asset.image) {
+ const type = 'image';
+ const thumb = topic.assetUrl(asset.image.thumb, topic.id);
+ const full = topic.assetUrl(asset.image.full, topic.id);
+ assets.push({ type, thumb, encrypted, full });
+ }
+ else if (asset.video) {
+ const type = 'video';
+ const thumb = topic.assetUrl(asset.video.thumb, topic.id);
+ const lq = topic.assetUrl(asset.video.lq, topic.id);
+ const hd = topic.assetUrl(asset.video.hd, topic.id);
+ assets.push({ type, thumb, encrypted, lq, hd });
+ }
+ else if (asset.audio) {
+ const type = 'audio';
+ const label = asset.audio.label;
+ const full = topic.assetUrl(asset.audio.full, topic.id);
+ assets.push({ type, label, encrypted, full });
+ }
+ }
+ });
+ updateState({ assets });
+ }
+ }, [topic.assets]);
+
const actions = {
setEditing: (message) => {
updateState({ editing: true, message });
diff --git a/net/web/src/session/conversation/topicItem/videoAsset/VideoAsset.jsx b/net/web/src/session/conversation/topicItem/videoAsset/VideoAsset.jsx
index 43e04793..76b1eae2 100644
--- a/net/web/src/session/conversation/topicItem/videoAsset/VideoAsset.jsx
+++ b/net/web/src/session/conversation/topicItem/videoAsset/VideoAsset.jsx
@@ -1,12 +1,12 @@
-import { Modal } from 'antd';
+import { Modal, Spin } from 'antd';
import ReactResizeDetector from 'react-resize-detector';
import { VideoCameraOutlined } from '@ant-design/icons';
-import { VideoAssetWrapper } from './VideoAsset.styled';
+import { VideoAssetWrapper, VideoModalWrapper } from './VideoAsset.styled';
import { useVideoAsset } from './useVideoAsset.hook';
-export function VideoAsset({ thumbUrl, lqUrl, hdUrl }) {
+export function VideoAsset({ asset }) {
- const { state, actions } = useVideoAsset();
+ const { state, actions } = useVideoAsset(asset);
const activate = () => {
if (state.dimension.width / state.dimension.height > window.innerWidth / window.innerHeight) {
@@ -28,7 +28,7 @@ export function VideoAsset({ thumbUrl, lqUrl, hdUrl }) {
if (width !== state.dimension.width || height !== state.dimension.height) {
actions.setDimension({ width, height });
}
- return
![]({thumbUrl})
+ return
![]({asset.thumb})
}}
@@ -38,8 +38,29 @@ export function VideoAsset({ thumbUrl, lqUrl, hdUrl }) {
)}
-
+
+
+ { !state.loaded && (
+
+
![topic asset]({asset.thumb})
+ { state.error && (
+
+
+
+ )}
+ { !state.error && (
+
+
+
+ )}
+
+ )}
+ { !state.loading && (
+
+ )}
+
+
diff --git a/net/web/src/session/conversation/topicItem/videoAsset/VideoAsset.styled.js b/net/web/src/session/conversation/topicItem/videoAsset/VideoAsset.styled.js
index fc6c3e34..892c468c 100644
--- a/net/web/src/session/conversation/topicItem/videoAsset/VideoAsset.styled.js
+++ b/net/web/src/session/conversation/topicItem/videoAsset/VideoAsset.styled.js
@@ -1,4 +1,5 @@
import styled from 'styled-components';
+import Colors from 'constants/Colors';
export const VideoAssetWrapper = styled.div`
position: relative;
@@ -14,3 +15,42 @@ export const VideoAssetWrapper = styled.div`
}
`;
+export const VideoModalWrapper = styled.div`
+
+ .wrapper {
+ padding-bottom: 6px;
+ }
+
+ .frame {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: black;
+ }
+
+ .thumb {
+ opacity: 0.3;
+ width: 100%;
+ object-fit: contain;
+ }
+
+ .failed {
+ position: absolute;
+ color: white;
+ border-radius: 8px;
+
+ .ant-spin-dot-item {
+ background-color: ${Colors.alert};
+ }
+ }
+
+ .loading {
+ position: absolute;
+ color: white;
+ border-radius: 8px;
+
+ .ant-spin-dot-item {
+ background-color: ${Colors.white};
+ }
+ }
+`;
diff --git a/net/web/src/session/conversation/topicItem/videoAsset/useVideoAsset.hook.js b/net/web/src/session/conversation/topicItem/videoAsset/useVideoAsset.hook.js
index 54eded4e..ff353d12 100644
--- a/net/web/src/session/conversation/topicItem/videoAsset/useVideoAsset.hook.js
+++ b/net/web/src/session/conversation/topicItem/videoAsset/useVideoAsset.hook.js
@@ -1,12 +1,19 @@
-import { useState } from 'react';
+import { useState, useRef } from 'react';
-export function useVideoAsset() {
+export function useVideoAsset(asset) {
+
+ const revoke = useRef();
+ const index = useRef(0);
const [state, setState] = useState({
width: 0,
height: 0,
active: false,
dimension: { width: 0, height: 0 },
+ loading: false,
+ error: false,
+ url: null,
+ loaded: false,
});
const updateState = (value) => {
@@ -14,15 +21,39 @@ export function useVideoAsset() {
}
const actions = {
- setActive: (width, height, url) => {
- updateState({ active: true, width, height });
+ setActive: async (width, height) => {
+ if (asset.encrypted) {
+ try {
+ const view = index.current;
+ updateState({ active: true, width, height, error: false, loaded: false, loading: true, url: null });
+ const blob = await asset.getDecryptedBlob(() => view != index.current);
+ const url = URL.createObjectURL(blob);
+ revoke.current = url;
+ updateState({ url, loading: false });
+ }
+ catch (err) {
+ console.log(err);
+ updateState({ error: true });
+ }
+ }
+ else {
+ updateState({ active: true, width, height, loading: false, url: asset.hd });
+ }
},
clearActive: () => {
+ index.current += 1;
updateState({ active: false });
+ if (revoke.current) {
+ URL.revokeObjectURL(revoke.current);
+ revoke.current = null;
+ }
},
setDimension: (dimension) => {
updateState({ dimension });
},
+ setLoaded: () => {
+ updateState({ loaded: true });
+ },
};
return { state, actions };
diff --git a/net/web/src/session/conversation/useConversation.hook.js b/net/web/src/session/conversation/useConversation.hook.js
index a8783b8f..492acd29 100644
--- a/net/web/src/session/conversation/useConversation.hook.js
+++ b/net/web/src/session/conversation/useConversation.hook.js
@@ -145,7 +145,7 @@ export function useConversation(cardId, channelId) {
let group = '';
let clickable = [];
- const words = text == null ? '' : DOMPurify.sanitize(text).split(' ');
+ const words = text == [] ? '' : DOMPurify.sanitize(text).split(' ');
words.forEach((word, index) => {
if (!!urlPattern.test(word)) {
clickable.push(
{ group });
diff --git a/net/web/src/session/useSession.hook.js b/net/web/src/session/useSession.hook.js
index 6d55b5a5..745e9487 100644
--- a/net/web/src/session/useSession.hook.js
+++ b/net/web/src/session/useSession.hook.js
@@ -167,7 +167,6 @@ export function useSession() {
await ring.actions.decline(cardId, contactNode, contactToken, callId);
},
accept: async (call) => {
-console.log("ACCEPTING:", call);
const { cardId, callId, contactNode, contactToken, calleeToken, iceUrl, iceUsername, icePassword } = call;
await ring.actions.accept(cardId, callId, contactNode, contactToken, calleeToken, iceUrl, iceUsername, icePassword);
},
diff --git a/net/web/yarn.lock b/net/web/yarn.lock
index 76474821..e643a006 100644
--- a/net/web/yarn.lock
+++ b/net/web/yarn.lock
@@ -4171,7 +4171,7 @@ deep-is@^0.1.3, deep-is@~0.1.3:
resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
-deepmerge@^4.0.0, deepmerge@^4.2.2:
+deepmerge@^4.2.2:
version "4.2.2"
resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz"
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
@@ -7162,11 +7162,6 @@ lines-and-columns@^1.1.6:
resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
-load-script@^1.0.0:
- version "1.0.0"
- resolved "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz"
- integrity sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==
-
loader-runner@^4.2.0:
version "4.3.0"
resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz"
@@ -7421,11 +7416,6 @@ memfs@^3.1.2, memfs@^3.4.3:
dependencies:
fs-monkey "^1.0.3"
-memoize-one@^5.1.1:
- version "5.2.1"
- resolved "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz"
- integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
-
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz"
@@ -8600,7 +8590,7 @@ prompts@^2.0.1, prompts@^2.4.2:
kleur "^3.0.3"
sisteransi "^1.0.5"
-prop-types@^15.5.10, prop-types@^15.7.2, prop-types@^15.8.1:
+prop-types@^15.5.10, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -9129,16 +9119,16 @@ react-error-overlay@^6.0.11:
resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz"
integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==
-react-fast-compare@^3.0.1:
- version "3.2.0"
- resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz"
- integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
-
react-icons@^4.8.0:
version "4.8.0"
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.8.0.tgz#621e900caa23b912f737e41be57f27f6b2bff445"
integrity sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg==
+react-image-file-resizer@^0.4.8:
+ version "0.4.8"
+ resolved "https://registry.yarnpkg.com/react-image-file-resizer/-/react-image-file-resizer-0.4.8.tgz#85f4ae4469fd2867d961568af660ef403d7a79af"
+ integrity sha512-Ue7CfKnSlsfJ//SKzxNMz8avDgDSpWQDOnTKOp/GNRFJv4dO9L5YGHNEnj40peWkXXAK2OK0eRIoXhOYpUzUTQ==
+
react-is@^16.12.0, "react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
@@ -9154,17 +9144,6 @@ react-is@^18.0.0, react-is@^18.2.0:
resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
-react-player@^2.10.0:
- version "2.11.0"
- resolved "https://registry.npmjs.org/react-player/-/react-player-2.11.0.tgz"
- integrity sha512-fIrwpuXOBXdEg1FiyV9isKevZOaaIsAAtZy5fcjkQK9Nhmk1I2NXzY/hkPos8V0zb/ZX416LFy8gv7l/1k3a5w==
- dependencies:
- deepmerge "^4.0.0"
- load-script "^1.0.0"
- memoize-one "^5.1.1"
- prop-types "^15.7.2"
- react-fast-compare "^3.0.1"
-
react-refresh@^0.11.0:
version "0.11.0"
resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz"