test new hooks

This commit is contained in:
plebbitor
2023-03-24 18:08:07 +01:00
parent 1b34241d8f
commit 7eb30c6c0a
7 changed files with 208 additions and 40 deletions
+2 -1
View File
@@ -7,7 +7,7 @@
"@babel/plugin-syntax-flow": "^7.18.6", "@babel/plugin-syntax-flow": "^7.18.6",
"@babel/plugin-transform-react-jsx": "^7.21.0", "@babel/plugin-transform-react-jsx": "^7.21.0",
"@plebbit/plebbit-logger": "https://github.com/plebbit/plebbit-logger.git", "@plebbit/plebbit-logger": "https://github.com/plebbit/plebbit-logger.git",
"@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#b4428eb09249497c8b9584e9b56941876de76ab7", "@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#e3ad7681a7a2e594ed8f78ef1e5ccf39834f4f88",
"@testing-library/dom": "^9.0.1", "@testing-library/dom": "^9.0.1",
"@testing-library/jest-dom": "^5.14.1", "@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^14.0.0", "@testing-library/react": "^14.0.0",
@@ -30,6 +30,7 @@
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"react-toastify": "^9.1.1", "react-toastify": "^9.1.1",
"react-tooltip": "^5.10.0", "react-tooltip": "^5.10.0",
"react-video-thumbnail": "^0.1.3",
"styled-components": "^5.3.9", "styled-components": "^5.3.9",
"web-vitals": "^3.3.0", "web-vitals": "^3.3.0",
"zustand": "^4.3.6" "zustand": "^4.3.6"
+26 -9
View File
@@ -2,7 +2,7 @@ import React, { useState, useEffect, Fragment } from 'react';
import { Link, useNavigate, useParams } from 'react-router-dom'; import { Link, useNavigate, useParams } from 'react-router-dom';
import InfiniteScroll from 'react-infinite-scroller'; import InfiniteScroll from 'react-infinite-scroller';
import { Tooltip } from 'react-tooltip'; import { Tooltip } from 'react-tooltip';
import { useFeed, useAccountsActions } from '@plebbit/plebbit-react-hooks'; import { useFeed, usePublishComment } from '@plebbit/plebbit-react-hooks';
import useAppStore from '../../useAppStore'; import useAppStore from '../../useAppStore';
import { Container, NavBar, Header, Break, PostFormLink, PostFormTable, PostForm, TopBar, BoardForm } from './styles/Board.styled'; import { Container, NavBar, Header, Break, PostFormLink, PostFormTable, PostForm, TopBar, BoardForm } from './styles/Board.styled';
import CaptchaModal from '../CaptchaModal'; import CaptchaModal from '../CaptchaModal';
@@ -17,6 +17,7 @@ import onError from '../../utils/onError';
import onSuccess from '../../utils/onSuccess'; import onSuccess from '../../utils/onSuccess';
import renderComments from '../../utils/renderComments'; import renderComments from '../../utils/renderComments';
import useClickForm from '../../hooks/useClickForm'; import useClickForm from '../../hooks/useClickForm';
import VideoThumbnail from 'react-video-thumbnail';
const Board = () => { const Board = () => {
@@ -35,7 +36,7 @@ const Board = () => {
const [name, setName] = useState(''); const [name, setName] = useState('');
const [subject, setSubject] = useState(''); const [subject, setSubject] = useState('');
const [comment, setComment] = useState(''); const [comment, setComment] = useState('');
const { publishComment } = useAccountsActions(); const { publishComment } = usePublishComment();
const [isCaptchaOpen, setIsCaptchaOpen] = useState(false); const [isCaptchaOpen, setIsCaptchaOpen] = useState(false);
const [isReplyOpen, setIsReplyOpen] = useState(false); const [isReplyOpen, setIsReplyOpen] = useState(false);
const [captchaImage, setCaptchaImage] = useState(''); const [captchaImage, setCaptchaImage] = useState('');
@@ -43,8 +44,8 @@ const Board = () => {
const [prevScrollPos, setPrevScrollPos] = useState(0); const [prevScrollPos, setPrevScrollPos] = useState(0);
const [visible, setVisible] = useState(true); const [visible, setVisible] = useState(true);
const [endIndex, setEndIndex] = useState(2); const [endIndex, setEndIndex] = useState(2);
const { feed, hasMore, loadMore } = useFeed([`${selectedAddress}`], 'new'); const { feed, hasMore, loadMore } = useFeed({subplebbitAddresses: [`${selectedAddress}`], sortType: 'new'});
const [selectedFeed, setSelectedFeed] = useState(feed); const [selectedFeed, setSelectedFeed] = useState(feed.slice(0, endIndex));
const renderedFeed = selectedFeed.slice(0, endIndex); const renderedFeed = selectedFeed.slice(0, endIndex);
const { subplebbitAddress } = useParams(); const { subplebbitAddress } = useParams();
const handleClickForm = useClickForm(); const handleClickForm = useClickForm();
@@ -84,7 +85,6 @@ const Board = () => {
}, [selectedAddress]); }, [selectedAddress]);
const tryLoadMore = async () => { const tryLoadMore = async () => {
try { try {
loadMore(); loadMore();
@@ -425,12 +425,20 @@ const Board = () => {
) : null} ) : null}
{commentMediaInfo?.type === "video" ? ( {commentMediaInfo?.type === "video" ? (
<span key={`fta-${thread.cid}`} className="file-thumb"> <span key={`fta-${thread.cid}`} className="file-thumb">
<video key={`fti-${thread.cid}`} src={commentMediaInfo.url} alt={commentMediaInfo.type} onError={(e) => e.target.src = fallbackImgUrl} /> <VideoThumbnail
videoUrl={commentMediaInfo.url}
thumbnailHandler={(thumbnail) => {
const img = document.querySelector(`img[key="img-${thread.cid}"]`);
if (img) {
img.src = thumbnail;
}
}}
/>
</span> </span>
) : null} ) : null}
{commentMediaInfo?.type === "audio" ? ( {commentMediaInfo?.type === "audio" ? (
<span key={`fta-${thread.cid}`} className="file-thumb"> <span key={`fta-${thread.cid}`} className="file-thumb">
<audio key={`fti-${thread.cid}`} src={commentMediaInfo.url} alt={commentMediaInfo.type} onError={(e) => e.target.src = fallbackImgUrl} /> {/* <audio key={`fti-${thread.cid}`} src={commentMediaInfo.url} alt={commentMediaInfo.type} onError={(e) => e.target.src = fallbackImgUrl} /> */}
</span> </span>
) : null} ) : null}
</div> </div>
@@ -711,14 +719,23 @@ const Board = () => {
) : commentMediaInfo.type === "video" ? ( ) : commentMediaInfo.type === "video" ? (
<div key={`mob-f-${thread.cid}`} className="file-mobile"> <div key={`mob-f-${thread.cid}`} className="file-mobile">
<span key={`mob-ft${thread.cid}`} className="file-thumb-mobile"> <span key={`mob-ft${thread.cid}`} className="file-thumb-mobile">
<video key={`mob-img-${thread.cid}`} src={commentMediaInfo.url} alt={commentMediaInfo.type} onError={(e) => e.target.src = fallbackImgUrl} /> <VideoThumbnail
videoUrl={commentMediaInfo.url}
thumbnailHandler={(thumbnail) => {
const img = document.querySelector(`img[key="img-${thread.cid}"]`);
if (img) {
img.src = thumbnail;
}
}}
/>
{/* <video key={`mob-img-${thread.cid}`} src={commentMediaInfo.url} alt={commentMediaInfo.type} onError={(e) => e.target.src = fallbackImgUrl} /> */}
<div key={`mob-fi-${thread.cid}`} className="file-info-mobile">{commentMediaInfo.type}</div> <div key={`mob-fi-${thread.cid}`} className="file-info-mobile">{commentMediaInfo.type}</div>
</span> </span>
</div> </div>
) : commentMediaInfo.type === "audio" ? ( ) : commentMediaInfo.type === "audio" ? (
<div key={`mob-f-${thread.cid}`} className="file-mobile"> <div key={`mob-f-${thread.cid}`} className="file-mobile">
<span key={`mob-ft${thread.cid}`} className="file-thumb-mobile"> <span key={`mob-ft${thread.cid}`} className="file-thumb-mobile">
<audio key={`mob-img-${thread.cid}`} src={commentMediaInfo.url} alt={commentMediaInfo.type} onError={(e) => e.target.src = fallbackImgUrl} /> {/* <audio key={`mob-img-${thread.cid}`} src={commentMediaInfo.url} alt={commentMediaInfo.type} onError={(e) => e.target.src = fallbackImgUrl} /> */}
<div key={`mob-fi-${thread.cid}`} className="file-info-mobile">{commentMediaInfo.type}</div> <div key={`mob-fi-${thread.cid}`} className="file-info-mobile">{commentMediaInfo.type}</div>
</span> </span>
</div> </div>
+23 -5
View File
@@ -1,7 +1,8 @@
import React, { useState, useEffect, Fragment } from 'react'; import React, { useState, useEffect, Fragment } from 'react';
import { Link, useNavigate, useParams } from 'react-router-dom'; import { Link, useNavigate, useParams } from 'react-router-dom';
import InfiniteScroll from 'react-infinite-scroller'; import InfiniteScroll from 'react-infinite-scroller';
import { useFeed, useAccountsActions } from '@plebbit/plebbit-react-hooks'; import VideoThumbnail from 'react-video-thumbnail';
import { useFeed, usePublishComment } from '@plebbit/plebbit-react-hooks';
import useAppStore from '../../useAppStore'; import useAppStore from '../../useAppStore';
import { Container, NavBar, Header, Break, PostForm, PostFormLink, PostFormTable } from './styles/Board.styled'; import { Container, NavBar, Header, Break, PostForm, PostFormLink, PostFormTable } from './styles/Board.styled';
import { Threads } from './styles/Catalog.styled'; import { Threads } from './styles/Catalog.styled';
@@ -33,13 +34,13 @@ const Catalog = () => {
const [name, setName] = useState(''); const [name, setName] = useState('');
const [subject, setSubject] = useState(''); const [subject, setSubject] = useState('');
const [comment, setComment] = useState(''); const [comment, setComment] = useState('');
const { publishComment } = useAccountsActions(); const { publishComment } = usePublishComment();
const [isCaptchaOpen, setIsCaptchaOpen] = useState(false); const [isCaptchaOpen, setIsCaptchaOpen] = useState(false);
const [captchaImage, setCaptchaImage] = useState(''); const [captchaImage, setCaptchaImage] = useState('');
const navigate = useNavigate(); const navigate = useNavigate();
const [prevScrollPos, setPrevScrollPos] = useState(0); const [prevScrollPos, setPrevScrollPos] = useState(0);
const [visible, setVisible] = useState(true); const [visible, setVisible] = useState(true);
const { feed, hasMore, loadMore } = useFeed([`${selectedAddress}`], 'new'); const { feed, hasMore, loadMore } = useFeed({subplebbitAddresses: [`${selectedAddress}`], sortType: 'new'});
const { subplebbitAddress } = useParams(); const { subplebbitAddress } = useParams();
const handleClickForm = useClickForm(); const handleClickForm = useClickForm();
@@ -336,7 +337,7 @@ const Catalog = () => {
> >
{feed.map(thread => { {feed.map(thread => {
const commentMediaInfo = getCommentMediaInfo(thread); const commentMediaInfo = getCommentMediaInfo(thread);
const fallbackImgUrl = "/assets/plebchan-thumbdown.png"; const fallbackImgUrl = "/assets/filedeleted-res.gif";
return ( return (
<div key={`thread-${thread.cid}`} className="thread"> <div key={`thread-${thread.cid}`} className="thread">
<Link key={`a-${thread.cid}`} to={`/${selectedAddress}/thread/${thread.cid}`} onClick={() => handleClickThread(thread.cid)}> <Link key={`a-${thread.cid}`} to={`/${selectedAddress}/thread/${thread.cid}`} onClick={() => handleClickThread(thread.cid)}>
@@ -354,8 +355,25 @@ const Catalog = () => {
e.target.src = fallbackImgUrl e.target.src = fallbackImgUrl
e.target.onerror = null;}} /> e.target.onerror = null;}} />
) : null} ) : null}
{commentMediaInfo?.type === "video" ? (
<VideoThumbnail
videoUrl={commentMediaInfo.url}
thumbnailHandler={(thumbnail) => {
const img = document.querySelector(`img[key="img-${thread.cid}"]`);
if (img) {
img.src = thumbnail;
}
}}
/>
) : null}
{commentMediaInfo?.type === "audio" ? (
<img key={`img-${thread.cid}`} alt="thread" src={commentMediaInfo.url}
onError={(e) => {
e.target.src = fallbackImgUrl
e.target.onerror = null;}} />
) : null}
</Fragment> </Fragment>
) : (<img key={`img-${thread.cid}`} alt="thread" src={fallbackImgUrl} />)} ) : null}
</Link> </Link>
{/* <div key={`ti-${thread.cid}`} className="thread-icons" > {/* <div key={`ti-${thread.cid}`} className="thread-icons" >
<span key={`si-${thread.cid}`} className="thread-icon sticky-icon" title="Sticky"></span> <span key={`si-${thread.cid}`} className="thread-icon sticky-icon" title="Sticky"></span>
+7 -7
View File
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Link, useNavigate, useParams } from 'react-router-dom'; import { Link, useNavigate, useParams } from 'react-router-dom';
import { Tooltip } from 'react-tooltip'; import { Tooltip } from 'react-tooltip';
import { useComment, useAccountsActions } from '@plebbit/plebbit-react-hooks'; import { useComment, usePublishComment } from '@plebbit/plebbit-react-hooks';
import useAppStore from '../../useAppStore'; import useAppStore from '../../useAppStore';
import { Container, NavBar, Header, Break, PostForm, PostFormTable, BoardForm } from './styles/Board.styled'; import { Container, NavBar, Header, Break, PostForm, PostFormTable, BoardForm } from './styles/Board.styled';
import { ReplyFormLink, TopBar, BottomBar } from './styles/Thread.styled'; import { ReplyFormLink, TopBar, BottomBar } from './styles/Thread.styled';
@@ -36,14 +36,14 @@ const Thread = () => {
const [name, setName] = useState(''); const [name, setName] = useState('');
const [subject, setSubject] = useState(''); const [subject, setSubject] = useState('');
const [commentContent, setCommentContent] = useState(''); const [commentContent, setCommentContent] = useState('');
const { publishComment } = useAccountsActions(); const { publishComment } = usePublishComment();
const [isCaptchaOpen, setIsCaptchaOpen] = useState(false); const [isCaptchaOpen, setIsCaptchaOpen] = useState(false);
const [isReplyOpen, setIsReplyOpen] = useState(false); const [isReplyOpen, setIsReplyOpen] = useState(false);
const [captchaImage, setCaptchaImage] = useState(''); const [captchaImage, setCaptchaImage] = useState('');
const navigate = useNavigate(); const navigate = useNavigate();
const [prevScrollPos, setPrevScrollPos] = useState(0); const [prevScrollPos, setPrevScrollPos] = useState(0);
const [visible, setVisible] = useState(true); const [visible, setVisible] = useState(true);
const comment = useComment(selectedThread); const comment = useComment({ selectedThread });
const { subplebbitAddress, threadCid } = useParams(); const { subplebbitAddress, threadCid } = useParams();
const handleClickForm = useClickForm(); const handleClickForm = useClickForm();
@@ -479,7 +479,7 @@ const Thread = () => {
</span>}) </span>})
</span> </span>
&nbsp; &nbsp;
<span className="date-time" data-utc="data">{getDate(comment.timestamp)}</span> <span className="date-time" data-utc="data">{getDate(comment?.timestamp)}</span>
&nbsp; &nbsp;
<span className="post-number"> <span className="post-number">
<Link to="" onClick={handleVoidClick} title="Link to this post">c/</Link> <Link to="" onClick={handleVoidClick} title="Link to this post">c/</Link>
@@ -553,7 +553,7 @@ const Thread = () => {
</span> </span>
</span> </span>
&nbsp; &nbsp;
<span key={`dt-${reply.cid}`} className="date-time" data-utc="data">{getDate(reply.timestamp)}</span> <span key={`dt-${reply.cid}`} className="date-time" data-utc="data">{getDate(reply?.timestamp)}</span>
&nbsp; &nbsp;
<span key={`pn-${reply.cid}`} className="post-number"> <span key={`pn-${reply.cid}`} className="post-number">
<Link to="" key={`pl1-${reply.cid}`} onClick={handleVoidClick} title="Link to this post">c/</Link> <Link to="" key={`pl1-${reply.cid}`} onClick={handleVoidClick} title="Link to this post">c/</Link>
@@ -645,7 +645,7 @@ const Thread = () => {
</span>) : null} </span>) : null}
</span> </span>
<span key={`mob-dt-${comment.cid}`} className="date-time-mobile"> <span key={`mob-dt-${comment.cid}`} className="date-time-mobile">
{getDate(comment.timestamp)} {getDate(comment?.timestamp)}
&nbsp; &nbsp;
<button id="reply-button" style={{ all: 'unset', cursor: 'pointer' }} key={`mob-no-${comment.cid}`} onClick={handleReplyOpen} title="Link to this post">c/</button> <button id="reply-button" style={{ all: 'unset', cursor: 'pointer' }} key={`mob-no-${comment.cid}`} onClick={handleReplyOpen} title="Link to this post">c/</button>
<Link to="" key={`mob-no2-${comment.cid}`} onClick={handleVoidClick} title="Reply to this post">{comment.cid?.slice(0, 8)}</Link> <Link to="" key={`mob-no2-${comment.cid}`} onClick={handleVoidClick} title="Reply to this post">{comment.cid?.slice(0, 8)}</Link>
@@ -738,7 +738,7 @@ const Thread = () => {
<br key={`mob-br-${reply.cid}`} /> <br key={`mob-br-${reply.cid}`} />
</span> </span>
<span key={`mob-dt-${reply.cid}`} className="date-time-mobile"> <span key={`mob-dt-${reply.cid}`} className="date-time-mobile">
{getDate(reply.timestamp)}&nbsp; {getDate(reply?.timestamp)}&nbsp;
<Link to="" key={`mob-pl1-${reply.cid}`} onClick={handleVoidClick} title="Link to this post">c/</Link> <Link to="" key={`mob-pl1-${reply.cid}`} onClick={handleVoidClick} title="Link to this post">c/</Link>
<button id="reply-button" style={{ all: 'unset', cursor: 'pointer' }} key={`mob-pl2-${reply.cid}`} onClick={handleReplyOpen} title="Reply to this post">{reply.cid.slice(0, 8)}</button> <button id="reply-button" style={{ all: 'unset', cursor: 'pointer' }} key={`mob-pl2-${reply.cid}`} onClick={handleReplyOpen} title="Reply to this post">{reply.cid.slice(0, 8)}</button>
</span> </span>
@@ -24,8 +24,6 @@ export const Threads = styled.div`
display: inline; display: inline;
margin: auto; margin: auto;
box-shadow: 0 0 5px rgba(0, 0, 0, .25); box-shadow: 0 0 5px rgba(0, 0, 0, .25);
min-height: 50px;
min-width: 50px;
border: 0; border: 0;
} }
+3
View File
@@ -1,4 +1,7 @@
const getDate = (commentTimestamp) => { const getDate = (commentTimestamp) => {
if (commentTimestamp === undefined || isNaN(commentTimestamp)) {
return "";
}
const locale = Intl.DateTimeFormat().resolvedOptions().locale; const locale = Intl.DateTimeFormat().resolvedOptions().locale;
const string = new Intl.DateTimeFormat(locale, { const string = new Intl.DateTimeFormat(locale, {
hour12: false, hour12: false,
+146 -15
View File
@@ -2417,9 +2417,9 @@
mkdirp "^1.0.4" mkdirp "^1.0.4"
rimraf "^3.0.2" rimraf "^3.0.2"
"@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#bea28433713a50fca9a4d2a44f75fc6cda69c0bf": "@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#f1279f55558db3cdf0459be786217b9bfe845194":
version "0.0.3" version "0.0.3"
resolved "https://github.com/plebbit/plebbit-js.git#bea28433713a50fca9a4d2a44f75fc6cda69c0bf" resolved "https://github.com/plebbit/plebbit-js.git#f1279f55558db3cdf0459be786217b9bfe845194"
dependencies: dependencies:
"@keyv/sqlite" "3.6.2" "@keyv/sqlite" "3.6.2"
"@plebbit/plebbit-logger" "github:plebbit/plebbit-logger" "@plebbit/plebbit-logger" "github:plebbit/plebbit-logger"
@@ -2444,10 +2444,11 @@
libp2p-crypto "0.21.2" libp2p-crypto "0.21.2"
lodash-es "4.17.21" lodash-es "4.17.21"
peer-id "0.16.0" peer-id "0.16.0"
proper-lockfile "github:Rinse12/node-proper-lockfile" proper-lockfile "github:plebbit/node-proper-lockfile"
safe-stable-stringify "2.4.1" safe-stable-stringify "2.4.1"
skia-canvas "1.0.0" skia-canvas "1.0.0"
sqlite3 "5.1.2" sqlite3 "5.1.2"
tiny-typed-emitter "2.1.0"
tinycache "1.1.2" tinycache "1.1.2"
uuid "9.0.0" uuid "9.0.0"
@@ -2464,16 +2465,18 @@
dependencies: dependencies:
debug "4.3.3" debug "4.3.3"
"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#b4428eb09249497c8b9584e9b56941876de76ab7": "@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#e3ad7681a7a2e594ed8f78ef1e5ccf39834f4f88":
version "0.0.1" version "0.0.1"
resolved "https://github.com/plebbit/plebbit-react-hooks.git#b4428eb09249497c8b9584e9b56941876de76ab7" resolved "https://github.com/plebbit/plebbit-react-hooks.git#e3ad7681a7a2e594ed8f78ef1e5ccf39834f4f88"
dependencies: dependencies:
"@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#bea28433713a50fca9a4d2a44f75fc6cda69c0bf" "@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#f1279f55558db3cdf0459be786217b9bfe845194"
"@plebbit/plebbit-logger" "https://github.com/plebbit/plebbit-logger.git" "@plebbit/plebbit-logger" "https://github.com/plebbit/plebbit-logger.git"
"@types/uuid" "8.3.4"
assert "2.0.0" assert "2.0.0"
ethers "5.6.9" ethers "5.6.9"
localforage "1.10.0" localforage "1.10.0"
lodash.isequal "4.5.0"
memoizee "0.4.15"
quick-lru "5.1.1"
uuid "8.3.2" uuid "8.3.2"
zustand "4.0.0" zustand "4.0.0"
@@ -3009,11 +3012,16 @@
"@types/node" "*" "@types/node" "*"
form-data "^3.0.0" form-data "^3.0.0"
"@types/node@*", "@types/node@>=13.7.0", "@types/node@^18.15.5": "@types/node@*", "@types/node@^18.15.5":
version "18.15.5" version "18.15.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.5.tgz#3af577099a99c61479149b716183e70b5239324a" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.5.tgz#3af577099a99c61479149b716183e70b5239324a"
integrity sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew== integrity sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew==
"@types/node@>=13.7.0":
version "18.15.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.7.tgz#33514fca9bdf136f77027358850c0fb9cd93c669"
integrity sha512-LFmUbFunqmBn26wJZgZPYZPrDR1RwGOu2v79Mgcka1ndO6V0/cwjivPTc4yoK6n9kmw4/ls1r8cLrvh2iMibFA==
"@types/normalize-package-data@^2.4.0": "@types/normalize-package-data@^2.4.0":
version "2.4.1" version "2.4.1"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
@@ -4822,6 +4830,14 @@ csstype@^3.0.2:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
d@1, d@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
dependencies:
es5-ext "^0.10.50"
type "^1.0.1"
dag-jose@^1.0.0: dag-jose@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/dag-jose/-/dag-jose-1.0.0.tgz#52e42d70cb5bee31ae4e8e3ab860615568d7ad73" resolved "https://registry.yarnpkg.com/dag-jose/-/dag-jose-1.0.0.tgz#52e42d70cb5bee31ae4e8e3ab860615568d7ad73"
@@ -5383,11 +5399,47 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1" is-date-object "^1.0.1"
is-symbol "^1.0.2" is-symbol "^1.0.2"
es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46:
version "0.10.62"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5"
integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==
dependencies:
es6-iterator "^2.0.3"
es6-symbol "^3.1.3"
next-tick "^1.1.0"
es6-iterator@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==
dependencies:
d "1"
es5-ext "^0.10.35"
es6-symbol "^3.1.1"
es6-object-assign@^1.1.0: es6-object-assign@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
integrity sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw== integrity sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==
es6-symbol@^3.1.1, es6-symbol@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
dependencies:
d "^1.0.1"
ext "^1.1.2"
es6-weak-map@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53"
integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==
dependencies:
d "1"
es5-ext "^0.10.46"
es6-iterator "^2.0.3"
es6-symbol "^3.1.1"
escalade@^3.1.1: escalade@^3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@@ -5765,6 +5817,14 @@ ethers@5.7.2:
"@ethersproject/web" "5.7.1" "@ethersproject/web" "5.7.1"
"@ethersproject/wordlists" "5.7.0" "@ethersproject/wordlists" "5.7.0"
event-emitter@^0.3.5:
version "0.3.5"
resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==
dependencies:
d "1"
es5-ext "~0.10.14"
eventemitter3@^4.0.0: eventemitter3@^4.0.0:
version "4.0.7" version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
@@ -5873,6 +5933,13 @@ ext-name@^5.0.0:
ext-list "^2.0.0" ext-list "^2.0.0"
sort-keys-length "^1.0.0" sort-keys-length "^1.0.0"
ext@^1.1.2:
version "1.7.0"
resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f"
integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==
dependencies:
type "^2.7.2"
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3" version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -7117,6 +7184,11 @@ is-potential-custom-element-name@^1.0.1:
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
is-promise@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1"
integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==
is-regex@^1.1.4: is-regex@^1.1.4:
version "1.1.4" version "1.1.4"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
@@ -8209,6 +8281,11 @@ lodash.debounce@^4.0.8:
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
lodash.isequal@4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
lodash.memoize@^4.1.2: lodash.memoize@^4.1.2:
version "4.1.2" version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@@ -8267,6 +8344,13 @@ lru-cache@^6.0.0:
dependencies: dependencies:
yallist "^4.0.0" yallist "^4.0.0"
lru-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3"
integrity sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==
dependencies:
es5-ext "~0.10.2"
lz-string@^1.5.0: lz-string@^1.5.0:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
@@ -8359,6 +8443,20 @@ memfs@^3.1.2, memfs@^3.4.3:
dependencies: dependencies:
fs-monkey "^1.0.3" fs-monkey "^1.0.3"
memoizee@0.4.15:
version "0.4.15"
resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72"
integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==
dependencies:
d "^1.0.1"
es5-ext "^0.10.53"
es6-weak-map "^2.0.3"
event-emitter "^0.3.5"
is-promise "^2.2.2"
lru-queue "^0.1.0"
next-tick "^1.1.0"
timers-ext "^0.1.7"
meow@^9.0.0: meow@^9.0.0:
version "9.0.0" version "9.0.0"
resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364" resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364"
@@ -8678,6 +8776,11 @@ neo-async@^2.6.2:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
next-tick@1, next-tick@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
no-case@^3.0.4: no-case@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
@@ -9840,9 +9943,9 @@ prop-types@^15.5.8, prop-types@^15.7.2, prop-types@^15.8.1:
object-assign "^4.1.1" object-assign "^4.1.1"
react-is "^16.13.1" react-is "^16.13.1"
"proper-lockfile@github:Rinse12/node-proper-lockfile": "proper-lockfile@github:plebbit/node-proper-lockfile":
version "4.1.2" version "4.1.2"
resolved "https://codeload.github.com/Rinse12/node-proper-lockfile/tar.gz/072ee9e37d52858cf8410ce55c281599cef1fb92" resolved "https://codeload.github.com/plebbit/node-proper-lockfile/tar.gz/072ee9e37d52858cf8410ce55c281599cef1fb92"
dependencies: dependencies:
graceful-fs "^4.2.4" graceful-fs "^4.2.4"
retry "^0.12.0" retry "^0.12.0"
@@ -9907,16 +10010,16 @@ queue-microtask@^1.2.2:
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
quick-lru@5.1.1, quick-lru@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
quick-lru@^4.0.1: quick-lru@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==
quick-lru@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
rabin-wasm@^0.1.4: rabin-wasm@^0.1.4:
version "0.1.5" version "0.1.5"
resolved "https://registry.yarnpkg.com/rabin-wasm/-/rabin-wasm-0.1.5.tgz#5b625ca007d6a2cbc1456c78ae71d550addbc9c9" resolved "https://registry.yarnpkg.com/rabin-wasm/-/rabin-wasm-0.1.5.tgz#5b625ca007d6a2cbc1456c78ae71d550addbc9c9"
@@ -10176,6 +10279,11 @@ react-tooltip@^5.10.0:
"@floating-ui/dom" "1.2.3" "@floating-ui/dom" "1.2.3"
classnames "^2.3.2" classnames "^2.3.2"
react-video-thumbnail@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/react-video-thumbnail/-/react-video-thumbnail-0.1.3.tgz#698dc4f4e7bebcb598425929db231a78cbcdbdcd"
integrity sha512-ZakEVv9RNFcziAnHHtwod5tf0yMVv4aLZ8/QsCxt1HTnKRHrlylvolPKv1hr63GCwnPu6tgPjUTDKRqfl30lOQ==
react@^18.2.0: react@^18.2.0:
version "18.2.0" version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
@@ -11382,6 +11490,19 @@ timeout-abort-controller@^3.0.0:
dependencies: dependencies:
retimer "^3.0.0" retimer "^3.0.0"
timers-ext@^0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6"
integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==
dependencies:
es5-ext "~0.10.46"
next-tick "1"
tiny-typed-emitter@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz#b3b027fdd389ff81a152c8e847ee2f5be9fad7b5"
integrity sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==
tinycache@1.1.2: tinycache@1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/tinycache/-/tinycache-1.1.2.tgz#4a0cde5390105135140c6b1251d377c0fbefd3d5" resolved "https://registry.yarnpkg.com/tinycache/-/tinycache-1.1.2.tgz#4a0cde5390105135140c6b1251d377c0fbefd3d5"
@@ -11561,6 +11682,16 @@ type-is@~1.6.18:
media-typer "0.3.0" media-typer "0.3.0"
mime-types "~2.1.24" mime-types "~2.1.24"
type@^1.0.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
type@^2.7.2:
version "2.7.2"
resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0"
integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==
typed-array-length@^1.0.4: typed-array-length@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"