diff --git a/package.json b/package.json index b0e37b4c..371c81cb 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "@babel/plugin-transform-react-jsx": "7.21.0", "@capacitor/android": "3.6.0", "@capacitor/core": "3.6.0", - "@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#97b538693972e26f7c0f0f13ec6afab03b0e2e57", + "@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#ea1a110be2043e2d4dbc9fc369ec7b47d52979d4", "@testing-library/dom": "9.0.1", "@testing-library/jest-dom": "5.14.1", "@testing-library/react": "14.0.0", diff --git a/public/assets/offline.png b/public/assets/offline.png new file mode 100644 index 00000000..a40aa2bb Binary files /dev/null and b/public/assets/offline.png differ diff --git a/src/components/BoardAvatar.jsx b/src/components/BoardAvatar.jsx new file mode 100644 index 00000000..7cbe0f47 --- /dev/null +++ b/src/components/BoardAvatar.jsx @@ -0,0 +1,36 @@ +import React, { useEffect, useState } from "react"; +import { useSubplebbit } from "@plebbit/plebbit-react-hooks"; + + +const BoardAvatar = ({ address }) => { + const [avatarUrl, setAvatarUrl] = useState("assets/plebchan.png"); + const subplebbit = useSubplebbit({ subplebbitAddress: address }); + + const isOnline = subplebbit.updatedAt > Date.now() / 1000 - 60 * 20; + + useEffect(() => { + if (subplebbit.suggested?.avatarUrl) { + setAvatarUrl(subplebbit.suggested.avatarUrl); + } + }, [subplebbit.suggested?.avatarUrl, subplebbit]); + + return ( + <> +
+ board logo + {!isOnline && ( + offline + )} +
+ + ); +}; + +export default BoardAvatar; \ No newline at end of file diff --git a/src/components/OfflineIndicator.jsx b/src/components/OfflineIndicator.jsx new file mode 100644 index 00000000..bae408c8 --- /dev/null +++ b/src/components/OfflineIndicator.jsx @@ -0,0 +1,28 @@ +import React from "react"; +import { useSubplebbit } from "@plebbit/plebbit-react-hooks"; + + +const OfflineIndicator = ({ address, className, tooltipPlace }) => { + const subplebbit = useSubplebbit({ subplebbitAddress: address }); + const isOnline = subplebbit.updatedAt > Date.now() / 1000 - 60 * 20; + + return ( + <> + {!isOnline && ( + <> + {" "} + offline + + )} + + ); +}; + +export default OfflineIndicator; \ No newline at end of file diff --git a/src/components/SettingsModal.jsx b/src/components/SettingsModal.jsx index 5d9c1363..d85fa026 100644 --- a/src/components/SettingsModal.jsx +++ b/src/components/SettingsModal.jsx @@ -108,14 +108,14 @@ const SettingsModal = ({ isOpen, closeModal }) => { const handleCloseModal = () => { - closeModal(); - - if (location.pathname === "/settings") { - navigate(-1, { replace: true }); + if (location.pathname.endsWith("/settings")) { + const newPath = location.pathname.slice(0, -9); + closeModal(); + navigate(newPath, { replace: true }); } else { - navigate("/settings", { state: { from: location } }); + closeModal(); } - }; + }; const toggleExpanded = (index) => { @@ -152,7 +152,7 @@ const SettingsModal = ({ isOpen, closeModal }) => { return ( { switch (selectedStyle) { case 'Yotsuba': @@ -2967,6 +2980,15 @@ export const BoardForm = styled.div` @media (max-width: 480px) { margin-bottom: 335px; + .offline-mobile-sub { + width: 18px; + margin-right: 5px; + } + + .offline-mobile-sub-reply { + width: 15px; + } + .thread { display: none; } diff --git a/src/components/styled/Home.styled.jsx b/src/components/styled/Home.styled.jsx index 3ab9b6cd..a9d0026a 100644 --- a/src/components/styled/Home.styled.jsx +++ b/src/components/styled/Home.styled.jsx @@ -172,6 +172,7 @@ export const Boards = styled.div` padding-bottom: .5em; background: #fff; word-wrap: break-word; + margin-bottom: 6px; #subscriptions { text-align: left; @@ -229,14 +230,26 @@ export const BoardsContent = styled.div` border: 1px solid #800; display: inline-block; overflow: hidden; + position: relative; } - .board-avatar-container img { + + .board-avatar-container img.board-avatar { width: 100%; height: 100%; object-fit: cover; } + .board-avatar-container img.offline { + filter: brightness(50%); + } + + .offline-indicator { + position: absolute; + top: 5px; + right: 5px; + } + .board-text { margin-bottom: 5px; padding: 0 2px; @@ -258,6 +271,43 @@ export const BoardsContent = styled.div` color: #e00 } } + + #view-all { + color: #800; + font-weight: 700; + text-decoration: none; + + :hover { + color: #e00; + } + } + + @media (max-width: 480px) { + .boardlink, #view-all { + font-size: 1.2em; + } + + .disconnected { + width: 15px; + margin-bottom: -1px; + position: absolute; + } + } + + @media (min-width: 480px) { + #mobile-br { + display: none; + } + + .disconnected { + width: 11px; + margin-bottom: -1px; + margin-top: 2px; + position: absolute; + } + } + + `; export const Footer = styled.div` diff --git a/src/components/views/Board.jsx b/src/components/views/Board.jsx index 838c2d04..c8466ef4 100644 --- a/src/components/views/Board.jsx +++ b/src/components/views/Board.jsx @@ -10,6 +10,7 @@ import useGeneralStore from '../../hooks/stores/useGeneralStore'; import { Container, NavBar, Header, Break, PostFormLink, PostFormTable, PostForm, TopBar, BoardForm } from '../styled/Board.styled'; import { Footer } from '../styled/Thread.styled'; import ImageBanner from '../ImageBanner'; +import OfflineIndicator from '../OfflineIndicator'; import Post from '../Post'; import PostLoader from '../PostLoader'; import ReplyModal from '../ReplyModal'; @@ -325,18 +326,14 @@ const Board = () => { try { if (subscribed === false) { await subscribe(selectedAddress); - setSuccessMessage(`Subscribed to ${selectedTitle ?? selectedAddress}`); - } else if (subscribed === true) { await unsubscribe(selectedAddress); - setSuccessMessage(`Unsubscribed from ${selectedTitle ?? selectedAddress}`); } - } catch (error) { setErrorMessage(error); } }; - + return ( <> @@ -357,21 +354,25 @@ const Board = () => { [ Subscriptions ] [ - - {defaultSubplebbits.map((subplebbit, index) => ( - - {index === 0 ? null : "\u00a0"} - handleClickTitle(subplebbit.title, subplebbit.address)} - >{subplebbit.title ? subplebbit.title : subplebbit.address} - {index !== defaultSubplebbits.length - 1 ? " /" : null} - - ))} + {defaultSubplebbits.map((subplebbit, index) => ( + + {index === 0 ? null : "\u00a0"} + handleClickTitle(subplebbit.title, subplebbit.address)} + >{subplebbit.title ? subplebbit.title : subplebbit.address} + + {index !== defaultSubplebbits.length - 1 ? " /" : null} + + ))} ] + [ ] @@ -396,7 +397,7 @@ const Board = () => {   @@ -1198,7 +1199,7 @@ const Board = () => { [ ] diff --git a/src/components/views/Catalog.jsx b/src/components/views/Catalog.jsx index ce1bdc5f..33bfb70a 100644 --- a/src/components/views/Catalog.jsx +++ b/src/components/views/Catalog.jsx @@ -2,7 +2,7 @@ import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react import { Helmet } from 'react-helmet-async'; import InfiniteScroll from 'react-infinite-scroller'; import { Link, useNavigate, useParams } from 'react-router-dom'; -import { useFeed, usePublishComment } from '@plebbit/plebbit-react-hooks'; +import { useFeed, usePublishComment, useSubscribe } from '@plebbit/plebbit-react-hooks'; import { debounce } from 'lodash'; import useGeneralStore from '../../hooks/stores/useGeneralStore'; import { Container, NavBar, Header, Break, PostForm, PostFormLink, PostFormTable } from '../styled/Board.styled'; @@ -10,11 +10,13 @@ import { Threads } from '../styled/Catalog.styled'; import { TopBar, Footer } from '../styled/Thread.styled'; import CatalogLoader from '../CatalogLoader'; import ImageBanner from '../ImageBanner'; +import OfflineIndicator from '../OfflineIndicator'; import SettingsModal from '../SettingsModal'; import getCommentMediaInfo from '../../utils/getCommentMediaInfo'; import handleStyleChange from '../../utils/handleStyleChange'; import useClickForm from '../../hooks/useClickForm'; import useError from '../../hooks/useError'; +import useSuccess from '../../hooks/useSuccess'; import packageJson from '../../../package.json' const {version} = packageJson @@ -48,8 +50,12 @@ const Catalog = () => { const { feed, hasMore, loadMore } = useFeed({subplebbitAddresses: [`${selectedAddress}`], sortType: 'new'}); const { subplebbitAddress } = useParams(); + const { subscribed, subscribe, unsubscribe } = useSubscribe({subplebbitAddress: selectedAddress}); + const [errorMessage, setErrorMessage] = useState(null); + const [successMessage] = useState(null); useError(errorMessage, [errorMessage]); + useSuccess(successMessage, [successMessage]); const [triggerPublishComment, setTriggerPublishComment] = useState(false); @@ -230,6 +236,17 @@ const Catalog = () => { navigate(`/p/${selected}`); }; + const handleSubscribe = async () => { + try { + if (subscribed === false) { + await subscribe(selectedAddress); + } else if (subscribed === true) { + await unsubscribe(selectedAddress); + } + } catch (error) { + setErrorMessage(error); + } + }; return ( <> @@ -244,10 +261,9 @@ const Catalog = () => { <> - [ - Subscriptions - ] [ - + [ + Subscriptions + ] [ {defaultSubplebbits.map((subplebbit, index) => ( {index === 0 ? null : "\u00a0"} @@ -256,15 +272,20 @@ const Catalog = () => { setSelectedAddress(subplebbit.address); }} >{subplebbit.title ? subplebbit.title : subplebbit.address} + {index !== defaultSubplebbits.length - 1 ? " /" : null} ))} - ] + ] + [ ] @@ -289,7 +310,7 @@ const Catalog = () => {   @@ -384,13 +405,17 @@ const Catalog = () => { {feed.length > 0 ? ( <> - - [ - - ] - - - + + [ + + ] + + + ) : (
Fetching IPFS... @@ -521,7 +546,7 @@ const Catalog = () => { [ ] diff --git a/src/components/views/Home.jsx b/src/components/views/Home.jsx index c1fda378..1cb10b23 100644 --- a/src/components/views/Home.jsx +++ b/src/components/views/Home.jsx @@ -1,27 +1,16 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useRef } from 'react'; import { Helmet } from 'react-helmet-async'; -import { useAccount, useSubplebbit } from '@plebbit/plebbit-react-hooks'; +import { useAccount } from '@plebbit/plebbit-react-hooks'; import useGeneralStore from '../../hooks/stores/useGeneralStore'; import { Link, useNavigate } from 'react-router-dom'; import { Container, Header, Logo, Page, Search, About, AboutTitle, AboutContent, Boards, BoardsTitle, BoardsContent, Footer } from '../styled/Home.styled'; +import BoardAvatar from '../BoardAvatar'; +import OfflineIndicator from '../OfflineIndicator'; import packageJson from '../../../package.json' +import { Tooltip } from 'react-tooltip'; const {version} = packageJson -const BoardAvatar = ({ address }) => { - const [avatarUrl, setAvatarUrl] = useState('assets/plebchan.png'); - const subplebbit = useSubplebbit({ subplebbitAddress: address }); - - useEffect(() => { - if (subplebbit.suggested?.avatarUrl) { - setAvatarUrl(subplebbit.suggested.avatarUrl); - } - }, [subplebbit.suggested?.avatarUrl, subplebbit]); - - return board logo; -}; - - const Home = () => { const { setBodyStyle, @@ -52,6 +41,7 @@ const Home = () => { plebchan +
@@ -91,7 +81,7 @@ const Home = () => {

Plebchan is a serverless, adminless, decentralized 4chan alternative that uses the plebbit protocol. Users do not need to register an account before participating in the community; anyone can post comments, share media links, and . Search for any board address above, or feel free to click on a popular board below that interests you and jump right in!


@@ -99,24 +89,31 @@ const Home = () => {
- {account.subscriptions?.length > 0 ? ( - + {account?.subscriptions?.length > 0 ? ( +

Board Subscriptions

- You have subscribed to {account.subscriptions.length} board{account.subscriptions.length > 1 ? "s" : null} -

+ You have subscribed to {account?.subscriptions?.length} board{account?.subscriptions?.length > 1 ? "s" : null} +   + [view all]
- {account.subscriptions.map((subscription, index) => ( + {account?.subscriptions?.map((subscription, index) => ( <> - - {subscription} + +
+ {subscription}  +
))} +
) : null} @@ -134,9 +131,7 @@ const Home = () => { setSelectedTitle(subplebbit.title); setSelectedAddress(subplebbit.address); }} > -
- -
+
{subplebbit.address} diff --git a/src/components/views/Subscriptions.jsx b/src/components/views/Subscriptions.jsx index 8a6a7456..fea6f02a 100644 --- a/src/components/views/Subscriptions.jsx +++ b/src/components/views/Subscriptions.jsx @@ -10,6 +10,7 @@ import useGeneralStore from '../../hooks/stores/useGeneralStore'; import { Container, NavBar, Header, Break, TopBar, BoardForm } from '../styled/Board.styled'; import { Footer } from '../styled/Thread.styled'; import ImageBanner from '../ImageBanner'; +import OfflineIndicator from '../OfflineIndicator'; import Post from '../Post'; import PostLoader from '../PostLoader'; import ReplyModal from '../ReplyModal'; @@ -30,12 +31,12 @@ const Subscriptions = () => { const { defaultSubplebbits, isSettingsOpen, setIsSettingsOpen, - selectedAddress, setSelectedAddress, + setSelectedAddress, setSelectedParentCid, setSelectedShortCid, selectedStyle, setSelectedThread, - selectedTitle, setSelectedTitle, + setSelectedTitle, } = useGeneralStore(state => state); const account = useAccount(); @@ -103,21 +104,6 @@ const Subscriptions = () => { return acc; }, {}); }, [flattenedRepliesByThread, accountComments, selectedFeed]); - - - // temporary title from JSON, gets subplebbitAddress from URL - useEffect(() => { - setSelectedAddress(subplebbitAddress); - const selectedSubplebbit = defaultSubplebbits.find((subplebbit) => subplebbit.address === subplebbitAddress); - if (selectedSubplebbit) { - setSelectedTitle(selectedSubplebbit.title); - } - }, [subplebbitAddress, setSelectedAddress, setSelectedTitle, defaultSubplebbits]); - - // sets useFeed to address from URL - useEffect(() => { - setSelectedFeed(feed); - }, [feed]); // mobile navbar scroll effect useEffect(() => { @@ -165,7 +151,7 @@ const Subscriptions = () => { return ( <> - {((selectedTitle ? selectedTitle : selectedAddress) + " - plebchan")} + Subscriptions - plebchan { isOpen={isSettingsOpen} closeModal={() => setIsSettingsOpen(false)} /> - <> + <> [ - {}}>Subscriptions + Subscriptions ] [ - {defaultSubplebbits.map((subplebbit, index) => ( {index === 0 ? null : "\u00a0"} handleClickTitle(subplebbit.title, subplebbit.address)} >{subplebbit.title ? subplebbit.title : subplebbit.address} + {index !== defaultSubplebbits.length - 1 ? " /" : null} - - ))} + + ))} ] + [ ] [ - setIsSettingsOpen(true)}>Settings + setIsSettingsOpen(true)}>Settings ] [ handleStyleChange({target: {value: "Yotsuba"}} @@ -221,12 +211,12 @@ const Subscriptions = () => {  
- setIsSettingsOpen(true)}>Settings + setIsSettingsOpen(true)}>Settings   handleStyleChange({target: {value: "Yotsuba"}} )}>Home @@ -383,7 +373,7 @@ const Subscriptions = () => {   c/ - { if (e.button === 2) return; e.preventDefault(); @@ -402,9 +392,13 @@ const Subscriptions = () => { >{thread.subplebbitAddress.slice(0, 10) + "(...)"} )} +    [ - setSelectedThread(thread.cid)} className="reply-link" >Reply + setSelectedThread(thread.cid)} className="reply-link" >Reply ]   @@ -432,7 +426,7 @@ const Subscriptions = () => { (...)

Post too long.  - setSelectedThread(thread.cid)} className="ttl-link">Click here + setSelectedThread(thread.cid)} className="ttl-link">Click here  to view.
@@ -448,7 +442,7 @@ const Subscriptions = () => { {omittedCount} post{omittedCount > 1 ? "s" : ""} omitted. Click  - setSelectedThread(thread.cid)} className="ttl-link">here + setSelectedThread(thread.cid)} className="ttl-link">here  to view. ) : null} @@ -507,7 +501,7 @@ const Subscriptions = () => { c/ {reply.shortCid ? ( - { if (e.button === 2) return; e.preventDefault(); @@ -610,7 +604,7 @@ const Subscriptions = () => { (...)

Comment too long.  - setSelectedThread(thread.cid)} className="ttl-link">Click here + setSelectedThread(thread.cid)} className="ttl-link">Click here  to view.
@@ -681,12 +675,27 @@ const Subscriptions = () => { p/ - {thread.subplebbitAddress.includes(".eth") ? thread.subplebbitAddress : ( - {thread.subplebbitAddress.slice(0, 10) + "(...)"} + {thread.subplebbitAddress.includes(".eth") ? + ( + <> + {thread.subplebbitAddress} + + + ) : ( + <> + {thread.subplebbitAddress.slice(0, 10) + "(...)"} + + )} @@ -694,7 +703,7 @@ const Subscriptions = () => { {getDate(thread.timestamp)}   c/ - { if (e.button === 2) return; e.preventDefault(); @@ -759,7 +768,7 @@ const Subscriptions = () => { (...)

Post too long.  - setSelectedThread(thread.cid)} className="ttl-link">Click here + setSelectedThread(thread.cid)} className="ttl-link">Click here  to view.
@@ -832,12 +841,27 @@ const Subscriptions = () => {  p/ - {thread.subplebbitAddress.includes(".eth") ? thread.subplebbitAddress : ( - {thread.subplebbitAddress.slice(0, 10) + "(...)"} + {thread.subplebbitAddress.includes(".eth") ? + ( + <> + {thread.subplebbitAddress} + + + ) : ( + <> + {thread.subplebbitAddress.slice(0, 10) + "(...)"} + + )} @@ -845,7 +869,7 @@ const Subscriptions = () => { {getDate(reply.timestamp)}  c/ {reply.shortCid ? ( - { if (e.button === 2) return; e.preventDefault(); @@ -916,7 +940,7 @@ const Subscriptions = () => { (...)

Comment too long.  - setSelectedThread(thread.cid)} className="ttl-link">Click here + setSelectedThread(thread.cid)} className="ttl-link">Click here  to view.
@@ -999,12 +1023,12 @@ const Subscriptions = () => { [ ] [ - setIsSettingsOpen(true)}>Settings + setIsSettingsOpen(true)}>Settings ] [ handleStyleChange({target: {value: "Yotsuba"}} diff --git a/src/components/views/Thread.jsx b/src/components/views/Thread.jsx index 6e1e282b..74a0b97b 100644 --- a/src/components/views/Thread.jsx +++ b/src/components/views/Thread.jsx @@ -9,6 +9,7 @@ import useGeneralStore from '../../hooks/stores/useGeneralStore'; import { Container, NavBar, Header, Break, PostForm, PostFormTable } from '../styled/Board.styled'; import { ReplyFormLink, TopBar, BottomBar, BoardForm, Footer } from '../styled/Thread.styled'; import ImageBanner from '../ImageBanner'; +import OfflineIndicator from '../OfflineIndicator'; import Post from '../Post'; import PostLoader from '../PostLoader'; import ReplyModal from '../ReplyModal'; @@ -286,10 +287,9 @@ const Thread = () => { <> - [ - Subscriptions - ] [ - + [ + Subscriptions + ] [ {defaultSubplebbits.map((subplebbit, index) => ( {index === 0 ? null : "\u00a0"} @@ -298,15 +298,20 @@ const Thread = () => { setSelectedAddress(subplebbit.address); }} >{subplebbit.title ? subplebbit.title : subplebbit.address} + {index !== defaultSubplebbits.length - 1 ? " /" : null} ))} - ] + ] +
[ ] @@ -331,7 +336,7 @@ const Thread = () => {  
@@ -1154,7 +1159,7 @@ const Thread = () => { [ ] diff --git a/src/hooks/useError.js b/src/hooks/useError.js index 6fa6a26d..eaaf279c 100644 --- a/src/hooks/useError.js +++ b/src/hooks/useError.js @@ -3,20 +3,28 @@ import { toast } from "react-toastify"; const useError = (message) => { useEffect(() => { - if (message) { - const toastId = toast.error(message.toString(), { - position: "top-right", - autoClose: false, - hideProgressBar: false, - closeOnClick: false, - pauseOnHover: true, - draggable: false, - progress: undefined, - theme: "dark", - }); + if (message && message.length > 0) { + const showErrorToast = () => { + const toastId = toast.error(message.toString(), { + position: "top-right", + autoClose: false, + hideProgressBar: true, + closeOnClick: false, + pauseOnHover: false, + draggable: false, + progress: undefined, + theme: "dark", + }); + + return () => { + toast.dismiss(toastId); + }; + }; + + const timeoutId = setTimeout(showErrorToast, 500); return () => { - toast.dismiss(toastId); + clearTimeout(timeoutId); }; } }, [message]); diff --git a/src/hooks/useSuccess.js b/src/hooks/useSuccess.js index 7e182a86..e42aa62d 100644 --- a/src/hooks/useSuccess.js +++ b/src/hooks/useSuccess.js @@ -3,20 +3,28 @@ import { toast } from "react-toastify"; const useSuccess = (message) => { useEffect(() => { - if (message) { - const toastId = toast.success(message.toString(), { - position: "top-right", - autoClose: 3000, - hideProgressBar: false, - closeOnClick: false, - pauseOnHover: false, - draggable: false, - progress: undefined, - theme: "dark", - }); + if (message && message.length > 0) { + const showSuccessToast = () => { + const toastId = toast.success(message.toString(), { + position: "top-right", + autoClose: 3000, + hideProgressBar: false, + closeOnClick: false, + pauseOnHover: false, + draggable: false, + progress: undefined, + theme: "dark", + }); + + return () => { + toast.dismiss(toastId); + }; + }; + + const timeoutId = setTimeout(showSuccessToast, 500); return () => { - toast.dismiss(toastId); + clearTimeout(timeoutId); }; } }, [message]); diff --git a/yarn.lock b/yarn.lock index 2428dcd6..8bf786a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2613,6 +2613,48 @@ mkdirp "^1.0.4" rimraf "^3.0.2" +"@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#84d03c4b622634418ce1561e09abdfe46b7d32b2": + version "0.0.3" + resolved "https://github.com/plebbit/plebbit-js.git#84d03c4b622634418ce1561e09abdfe46b7d32b2" + dependencies: + "@keyv/sqlite" "3.6.2" + "@plebbit/plebbit-logger" "github:plebbit/plebbit-logger" + "@types/node-fetch" "2.6.2" + "@types/proper-lockfile" "4.1.2" + "@types/uuid" "8.3.4" + assert "2.0.0" + async-wait-until "2.0.12" + buffer "6.0.3" + captcha-canvas "3.2.1" + err-code "3.0.1" + ethers "5.7.2" + file-type "16.5.4" + form-data "4.0.0" + hpagent "1.2.0" + ipfs-http-client "56.0.3" + ipfs-only-hash "4.0.0" + is-ipfs "6.0.2" + jose "4.11.0" + js-sha256 "0.9.0" + keyv "4.5.2" + knex "2.3.0" + libp2p-crypto "0.21.2" + limiter "2.1.0" + localforage "1.10.0" + lodash-es "4.17.21" + open-graph-scraper "5.2.3" + p-limit "3.1.0" + peer-id "0.16.0" + proper-lockfile "github:plebbit/node-proper-lockfile" + retry "0.13.1" + safe-stable-stringify "2.4.1" + skia-canvas "1.0.0" + sqlite3 "5.1.2" + tiny-typed-emitter "2.1.0" + tinycache "1.1.2" + ts-custom-error "3.3.1" + uuid "9.0.0" + "@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#b3272c08bd3861322b7a5eef48992e1b73640098": version "0.0.3" resolved "https://github.com/plebbit/plebbit-js.git#b3272c08bd3861322b7a5eef48992e1b73640098" @@ -2683,6 +2725,21 @@ uuid "8.3.2" zustand "4.0.0" +"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#ea1a110be2043e2d4dbc9fc369ec7b47d52979d4": + version "0.0.1" + resolved "https://github.com/plebbit/plebbit-react-hooks.git#ea1a110be2043e2d4dbc9fc369ec7b47d52979d4" + dependencies: + "@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#84d03c4b622634418ce1561e09abdfe46b7d32b2" + "@plebbit/plebbit-logger" "https://github.com/plebbit/plebbit-logger.git" + assert "2.0.0" + ethers "5.6.9" + localforage "1.10.0" + lodash.isequal "4.5.0" + memoizee "0.4.15" + quick-lru "5.1.1" + uuid "8.3.2" + zustand "4.0.0" + "@pmmmwh/react-refresh-webpack-plugin@^0.5.3": version "0.5.10" resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz#2eba163b8e7dbabb4ce3609ab5e32ab63dda3ef8"