diff --git a/package.json b/package.json index 6fe0506e..18c8a7d9 100755 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "@capacitor/app": "6.0.1", "@capacitor/local-notifications": "7.0.1", "@floating-ui/react": "0.26.1", - "@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#41f0cdf14617f5e843311986d66f14f2f8fcbce0", + "@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#d6347601b40a237c26518ab9cc9de7d29bb69b62", "@testing-library/jest-dom": "5.14.1", "@testing-library/react": "13.0.0", "@testing-library/user-event": "13.2.1", diff --git a/src/components/error-display/error-display.module.css b/src/components/error-display/error-display.module.css new file mode 100644 index 00000000..bfd4f307 --- /dev/null +++ b/src/components/error-display/error-display.module.css @@ -0,0 +1,9 @@ +.errorMessage { + color: var(--red); +} + +.showFullErrorButton { + font-weight: 700; + cursor: pointer; + padding: 5px 0; +} diff --git a/src/components/error-display/error-display.tsx b/src/components/error-display/error-display.tsx new file mode 100644 index 00000000..90ae6329 --- /dev/null +++ b/src/components/error-display/error-display.tsx @@ -0,0 +1,49 @@ +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import styles from './error-display.module.css'; + +const ErrorDisplay = ({ error }: { error: any }) => { + const { t } = useTranslation(); + const [showFullError, setShowFullError] = useState(false); + + return ( + (error?.message || error?.stack || error?.details) && ( +
+
+ + {error?.message && ( + + {t('error')}: {error.message} + + )} + {(error?.stack || error?.details) && ( + <> + {' — '} + setShowFullError(!showFullError)}> + {showFullError ? 'hide' : 'show'} full error + + + )} + + {showFullError && ( + <> + {error?.stack && ( + <> +
+
Stack: {error.stack}
+ + )} + {error?.details && ( + <> +
+
Details: {JSON.stringify(error?.details, null, 2)}
+ + )} + + )} +
+ ) + ); +}; + +export default ErrorDisplay; diff --git a/src/components/error-display/index.ts b/src/components/error-display/index.ts new file mode 100644 index 00000000..ea4eb1f3 --- /dev/null +++ b/src/components/error-display/index.ts @@ -0,0 +1 @@ +export { default } from './error-display'; diff --git a/src/components/markdown/markdown.module.css b/src/components/markdown/markdown.module.css index f02a150a..5ec00f81 100644 --- a/src/components/markdown/markdown.module.css +++ b/src/components/markdown/markdown.module.css @@ -20,6 +20,20 @@ border-left: 2px solid var(--markdown-blockquote-border); color: var(--markdown-blockquote); white-space: normal; + display: flow-root; + margin-bottom: 5px; +} + +.markdown blockquote > p:last-child { + margin-bottom: 0; +} + +.markdown blockquote blockquote > p:last-child { + margin-bottom: 5px; +} + +.markdown blockquote > blockquote:last-child { + margin-bottom: 0; } .markdown li { diff --git a/src/components/post/post.module.css b/src/components/post/post.module.css index 20784cd6..f0cda68f 100644 --- a/src/components/post/post.module.css +++ b/src/components/post/post.module.css @@ -138,7 +138,7 @@ } .tagline { - font-size: 11px; + font-size: x-small; } .tagline a { diff --git a/src/components/post/thumbnail/thumbnail.module.css b/src/components/post/thumbnail/thumbnail.module.css index 9d505873..dd4e954d 100644 --- a/src/components/post/thumbnail/thumbnail.module.css +++ b/src/components/post/thumbnail/thumbnail.module.css @@ -5,6 +5,8 @@ width: 70px; height: var(--height, 70px); z-index: 1; + position: relative; + overflow: hidden; } .thumbnailHidden { diff --git a/src/components/search-bar/search-bar.module.css b/src/components/search-bar/search-bar.module.css index eb91adef..88aefdb3 100644 --- a/src/components/search-bar/search-bar.module.css +++ b/src/components/search-bar/search-bar.module.css @@ -105,4 +105,34 @@ position: relative; will-change: transform; transition: transform 0.3s linear; +} + +.dropdown { + font-size: 14px; + position: absolute; + width: calc(100% - 8px); + margin: 0; + border: 1px solid var(--border-text); + background: var(--background); + left: 0; + list-style: none; + z-index: 999999999 !important; +} + +.dropdownItem { + text-decoration: none; + display: block; + padding: 5px; + cursor: pointer; + color: var(--text); + overflow: hidden; +} + +.dropdownItem:hover, .activeDropdownItem { + background-color: var(--text-primary); + color: var(--background); +} + +.dropdownItem:hover { + color: var(--background); } \ No newline at end of file diff --git a/src/components/search-bar/search-bar.tsx b/src/components/search-bar/search-bar.tsx index cd1e3a5d..ff4839c3 100644 --- a/src/components/search-bar/search-bar.tsx +++ b/src/components/search-bar/search-bar.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState, useMemo } from 'react'; import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import styles from './search-bar.module.css'; @@ -13,6 +13,7 @@ import { isSubplebbitAboutView, } from '../../lib/utils/view-utils'; import useFeedFiltersStore from '../../stores/use-feed-filters-store'; +import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits'; import _ from 'lodash'; interface SearchBarProps { @@ -50,6 +51,15 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => { const [inputValue, setInputValue] = useState(currentQuery); const { setIsSearching } = useFeedFiltersStore(); + const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses(); + const [isInputFocused, setIsInputFocused] = useState(false); + const [activeDropdownIndex, setActiveDropdownIndex] = useState(-1); + + const filteredCommunitySuggestions = useMemo(() => { + if (!inputValue || isInCommunitySearch) return []; + return defaultSubplebbitAddresses.filter((address) => address?.toLowerCase()?.includes(inputValue.toLowerCase())).slice(0, 10); + }, [inputValue, defaultSubplebbitAddresses, isInCommunitySearch]); + useEffect(() => { setInputValue(searchParams.get('q') || ''); }, [searchParams]); @@ -118,6 +128,7 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => { const handleSearchChange = (e: React.ChangeEvent) => { const value = e.target.value; setInputValue(value); + setActiveDropdownIndex(-1); if (isInCommunitySearch) { if (value.trim()) { setIsSearching(true); @@ -141,6 +152,46 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => { } }; + const handleCommunitySelect = useCallback( + (address: string) => { + setInputValue(''); + setIsInputFocused(false); + setActiveDropdownIndex(-1); + navigate(`/p/${address}`); + }, + [navigate, setInputValue, setIsInputFocused, setActiveDropdownIndex], + ); + + const handleKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if (!isInputFocused || isInCommunitySearch || filteredCommunitySuggestions.length === 0) { + if (e.key === 'Enter' && !isInCommunitySearch) { + } else { + return; + } + } + + if (e.key === 'ArrowDown') { + e.preventDefault(); + setActiveDropdownIndex((prevIndex) => (prevIndex < filteredCommunitySuggestions.length - 1 ? prevIndex + 1 : prevIndex)); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + setActiveDropdownIndex((prevIndex) => (prevIndex > 0 ? prevIndex - 1 : 0)); + } else if (e.key === 'Enter') { + e.preventDefault(); + if (activeDropdownIndex !== -1 && filteredCommunitySuggestions[activeDropdownIndex]) { + handleCommunitySelect(filteredCommunitySuggestions[activeDropdownIndex]); + } else if (inputValue.trim() && !isInCommunitySearch) { + searchBarRef.current?.requestSubmit(); + } + } else if (e.key === 'Escape') { + setIsInputFocused(false); + setActiveDropdownIndex(-1); + } + }, + [isInputFocused, isInCommunitySearch, filteredCommunitySuggestions, activeDropdownIndex, handleCommunitySelect, inputValue, navigate], + ); + return (
@@ -152,13 +203,38 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => { autoCapitalize='off' placeholder={placeholder} ref={searchInputRef} - onFocus={() => setShowExpando(true)} + onFocus={() => { + setShowExpando(true); + setIsInputFocused(true); + }} onChange={handleSearchChange} value={inputValue} + onKeyDown={handleKeyDown} + onBlur={() => setTimeout(() => setIsInputFocused(false), 150)} />
+ {!isInCommunitySearch && isInputFocused && filteredCommunitySuggestions.length > 0 && ( +
    + {filteredCommunitySuggestions.map((address, index) => ( +
  • handleCommunitySelect(address)} + onMouseEnter={() => setActiveDropdownIndex(index)} + > + {address} +
  • + ))} +
+ )}
+ {isInFeedView && ( + + )} - {isInFeedView && ( - - )}
); diff --git a/src/components/sidebar/sidebar.module.css b/src/components/sidebar/sidebar.module.css index e1cee1db..0ef4e107 100644 --- a/src/components/sidebar/sidebar.module.css +++ b/src/components/sidebar/sidebar.module.css @@ -19,6 +19,8 @@ } .searchBarWrapper { + position: relative; + z-index: 2; margin-right: -5px; } @@ -390,7 +392,7 @@ a { padding-right: 6px; } -.createCommunitySubtitles { +.communitySubtitles { position: relative; margin-top: 10px; padding: 5px 0 0 44px; diff --git a/src/components/sidebar/sidebar.tsx b/src/components/sidebar/sidebar.tsx index 15f2dec1..4f8f4972 100644 --- a/src/components/sidebar/sidebar.tsx +++ b/src/components/sidebar/sidebar.tsx @@ -29,7 +29,7 @@ import SubscribeButton from '../subscribe-button'; import LoadingEllipsis from '../loading-ellipsis'; import Version from '../version'; import { FAQ } from '../../views/about/about'; -import { createCommunitySubtitles } from '../../constants/create-community-subtitles'; +import { communitySubtitles } from '../../constants/community-subtitles'; const isElectron = window.electronApi?.isElectron === true; @@ -236,18 +236,18 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit const [subtitle2, setSubtitle2] = useState(''); useEffect(() => { - if (createCommunitySubtitles.length >= 2) { + if (communitySubtitles.length >= 2) { const indices = new Set(); while (indices.size < 2) { - const randomIndex = Math.floor(Math.random() * createCommunitySubtitles.length); + const randomIndex = Math.floor(Math.random() * communitySubtitles.length); indices.add(randomIndex); } const [index1, index2] = Array.from(indices); - setSubtitle1(createCommunitySubtitles[index1]); - setSubtitle2(createCommunitySubtitles[index2]); - } else if (createCommunitySubtitles.length === 1) { + setSubtitle1(communitySubtitles[index1]); + setSubtitle2(communitySubtitles[index2]); + } else if (communitySubtitles.length === 1) { // Handle case with only one subtitle - setSubtitle1(createCommunitySubtitles[0]); + setSubtitle1(communitySubtitles[0]); setSubtitle2(''); // Or handle as needed } }, []); // Empty dependency array ensures this runs only once on mount @@ -375,7 +375,7 @@ const Sidebar = ({ comment, isSubCreatedButNotYetPublished, settings, subplebbit {t('create_your_community')}
-
+
diff --git a/src/constants/create-community-subtitles.ts b/src/constants/community-subtitles.ts similarity index 79% rename from src/constants/create-community-subtitles.ts rename to src/constants/community-subtitles.ts index b3973e61..f75ced60 100644 --- a/src/constants/create-community-subtitles.ts +++ b/src/constants/community-subtitles.ts @@ -1,4 +1,4 @@ -export const createCommunitySubtitles = [ +export const communitySubtitles = [ '...unstoppable by design.', '...servers are overrated.', '...cryptographic playground.', @@ -23,4 +23,9 @@ export const createCommunitySubtitles = [ '...because you love freedom.', '...decentralized, but for real.', '...for your peace of mind.', + '...no corporation to answer to.', + '...for your peace of mind.', + '...your tokenized sovereignty.', + '...for text-only wonders.', + '...because open source rulez.', ]; diff --git a/src/stores/use-error-store.ts b/src/stores/use-error-store.ts new file mode 100644 index 00000000..d6348550 --- /dev/null +++ b/src/stores/use-error-store.ts @@ -0,0 +1,24 @@ +import { create } from 'zustand'; + +interface ErrorStoreState { + errors: Record; + setError: (source: string, error: Error | null | undefined) => void; + clearAllErrors: () => void; +} + +const useErrorStore = create((set) => ({ + errors: {}, + setError: (source, error) => + set((state) => { + const newErrors = { ...state.errors }; + if (error) { + newErrors[source] = error; + } else { + delete newErrors[source]; + } + return { errors: newErrors }; + }), + clearAllErrors: () => set({ errors: {} }), +})); + +export default useErrorStore; diff --git a/src/views/about/about.tsx b/src/views/about/about.tsx index 526c4776..b13085e8 100644 --- a/src/views/about/about.tsx +++ b/src/views/about/about.tsx @@ -49,11 +49,11 @@ export const FAQ = () => { New Users:

- Welcome! Your account u/{account?.author?.shortAddress} was created automatically and it's stored locally ( - {window.electronApi?.isElectron ? 'on this desktop app' : isAndroid ? 'on this mobile app' : `on ${window.location.hostname}`}, not on a server). You can back - up your account in the preferences. There are no global rules or admins on Seedit, each community has its own rules - and moderators, so please be sure to read the rules of the community you are joining. You can connect peer-to-peer to any community by using the search bar, or - you can check out the default community list. + Welcome! Your account was created automatically and it's stored locally on your device ( + {window.electronApi?.isElectron ? 'on this desktop app' : isAndroid ? 'on this mobile app' : `on this web app, ${window.location.hostname}`}, not on a server). + You can back up your account in the preferences. There are no global rules or admins on Seedit, each community has its + own rules and moderators, so please be sure to read the rules of the community you are joining. You can connect peer-to-peer to any community by using the + search bar, or you can check out the default community list.


What is Seedit and how does it work?

@@ -130,7 +130,7 @@ export const FAQ = () => { is generated randomly from a cryptographic hash of your public key, similarly to how a bitcoin address is generated. You can{' '} change your account address to a unique readable name you own, by resolving it with a decentralized domain name service such as{' '} - + ENS {' '} or{' '} diff --git a/src/views/all/all.tsx b/src/views/all/all.tsx index 612ec7e3..b683b648 100644 --- a/src/views/all/all.tsx +++ b/src/views/all/all.tsx @@ -58,11 +58,13 @@ const All = () => { }; if (searchQuery) { - options.filter = (comment: Comment) => { - if (!searchQuery.trim()) return true; - return commentMatchesPattern(comment, searchQuery); + options.filter = { + filter: (comment: Comment) => { + if (!searchQuery.trim()) return true; + return commentMatchesPattern(comment, searchQuery); + }, + key: `search-filter-${searchQuery}`, }; - options.filterKey = `search-filter-${searchQuery}`; } return options; diff --git a/src/views/author/author.module.css b/src/views/author/author.module.css index 5b27f046..91f02e02 100644 --- a/src/views/author/author.module.css +++ b/src/views/author/author.module.css @@ -30,4 +30,11 @@ div[data-viewport-type="window"] { display: inline-block; padding: 10px 0 0px 5px; } +} + +.error { + padding-left: 10px; + font-size: 12px; + margin-top: -10px; + padding-bottom: 10px; } \ No newline at end of file diff --git a/src/views/author/author.tsx b/src/views/author/author.tsx index f997fe97..5cb12bb9 100644 --- a/src/views/author/author.tsx +++ b/src/views/author/author.tsx @@ -10,6 +10,7 @@ import Post from '../../components/post'; import Reply from '../../components/reply/'; import AuthorSidebar from '../../components/author-sidebar'; import styles from './author.module.css'; +import ErrorDisplay from '../../components/error-display'; const lastVirtuosoStates: { [key: string]: StateSnapshot } = {}; @@ -24,7 +25,13 @@ const Author = () => { const isInAuthorSubmittedView = isAuthorSubmittedView(location.pathname, params); const isMobile = useWindowWidth() < 640; - const { authorComments, lastCommentCid, hasMore, loadMore } = useAuthorComments({ commentCid, authorAddress }); + const { authorComments, error, lastCommentCid, hasMore, loadMore } = useAuthorComments({ commentCid, authorAddress }); + + useEffect(() => { + if (error) { + console.log(error); + } + }, [error]); const replyComments = useMemo(() => authorComments?.filter((comment) => comment && comment.parentCid) || [], [authorComments]); const postComments = useMemo(() => authorComments?.filter((comment) => comment && !comment.parentCid) || [], [authorComments]); @@ -82,6 +89,11 @@ const Author = () => {
+ {error && ( +
+ +
+ )} { }; if (searchQuery) { - options.filter = (comment: Comment) => { - if (!searchQuery.trim()) return true; - return commentMatchesPattern(comment, searchQuery); + options.filter = { + filter: (comment: Comment) => { + if (!searchQuery.trim()) return true; + return commentMatchesPattern(comment, searchQuery); + }, + key: `search-filter-${searchQuery}`, }; - options.filterKey = `search-filter-${searchQuery}`; } return options; diff --git a/src/views/inbox/inbox.tsx b/src/views/inbox/inbox.tsx index aee796cb..3c2f9262 100644 --- a/src/views/inbox/inbox.tsx +++ b/src/views/inbox/inbox.tsx @@ -7,6 +7,7 @@ import Reply from '../../components/reply/reply'; import { isInboxCommentRepliesView, isInboxPostRepliesView, isInboxUnreadView } from '../../lib/utils/view-utils'; import { useTranslation } from 'react-i18next'; import _ from 'lodash'; +import ErrorDisplay from '../../components/error-display'; const lastVirtuosoStates: { [key: string]: StateSnapshot } = {}; @@ -43,7 +44,7 @@ const Inbox = () => { const { t } = useTranslation(); const account = useAccount(); const { unreadNotificationCount } = account || {}; - const { notifications, markAsRead } = useNotifications(); + const { error, notifications, markAsRead } = useNotifications(); const location = useLocation(); const isInInboxCommentRepliesView = isInboxCommentRepliesView(location.pathname); @@ -97,6 +98,12 @@ const Inbox = () => { document.title = documentTitle; }, [documentTitle]); + useEffect(() => { + if (error) { + console.log(error); + } + }, [error]); + return (
@@ -109,6 +116,11 @@ const Inbox = () => {
{t('nothing_found')}
)}
+ {error && ( +
+ +
+ )} { }; if (searchQuery) { - options.filter = (comment: Comment) => { - if (!searchQuery.trim()) return true; - return commentMatchesPattern(comment, searchQuery); + options.filter = { + filter: (comment: Comment) => { + if (!searchQuery.trim()) return true; + return commentMatchesPattern(comment, searchQuery); + }, + key: `search-filter-${searchQuery}`, }; - options.filterKey = `search-filter-${searchQuery}`; } return options; diff --git a/src/views/post-page/post-page.module.css b/src/views/post-page/post-page.module.css index 30e887e3..e3fa24f4 100644 --- a/src/views/post-page/post-page.module.css +++ b/src/views/post-page/post-page.module.css @@ -184,10 +184,16 @@ } } -.noReplies, .error { +.noReplies { color: var(--red); font-size: 13px; text-transform: lowercase; padding-top: 19px; padding-bottom: 10px; } + +.error { + padding-left: 3px; + font-size: 13px; + margin-top: -20px; +} \ No newline at end of file diff --git a/src/views/post-page/post-page.tsx b/src/views/post-page/post-page.tsx index bed37831..1372f10b 100644 --- a/src/views/post-page/post-page.tsx +++ b/src/views/post-page/post-page.tsx @@ -1,23 +1,24 @@ import { useEffect, useState, useRef, useCallback, useMemo } from 'react'; import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; -import { Comment, useAccount, useAccountComment, useAccountComments, useComment, useSubplebbit } from '@plebbit/plebbit-react-hooks'; import { useTranslation } from 'react-i18next'; +import { Comment, useAccount, useAccountComment, useAccountComments, useComment, useSubplebbit } from '@plebbit/plebbit-react-hooks'; import findTopParentCidOfReply from '../../lib/utils/cid-utils'; import { sortRepliesByBest } from '../../lib/utils/post-utils'; import { isPendingPostView, isPostContextView } from '../../lib/utils/view-utils'; +import useContentOptionsStore from '../../stores/use-content-options-store'; +import useFeedResetStore from '../../stores/use-feed-reset-store'; +import { useIsBroadlyNsfwSubplebbit } from '../../hooks/use-is-broadly-nsfw-subplebbit'; import useReplies from '../../hooks/use-replies'; import useStateString from '../../hooks/use-state-string'; +import ErrorDisplay from '../../components/error-display'; import LoadingEllipsis from '../../components/loading-ellipsis'; +import Over18Warning from '../../components/over-18-warning'; +import PostComponent from '../../components/post'; import Reply from '../../components/reply'; import ReplyForm from '../../components/reply-form'; -import PostComponent from '../../components/post'; import Sidebar from '../../components/sidebar'; import styles from './post-page.module.css'; import _ from 'lodash'; -import Over18Warning from '../../components/over-18-warning'; -import { useIsBroadlyNsfwSubplebbit } from '../../hooks/use-is-broadly-nsfw-subplebbit'; -import useContentOptionsStore from '../../stores/use-content-options-store'; -import useFeedResetStore from '../../stores/use-feed-reset-store'; type SortDropdownProps = { sortBy: string; @@ -80,7 +81,7 @@ const Post = ({ post }: { post: Comment }) => { const params = useParams(); const isInPostContextView = isPostContextView(location.pathname, params, location.search); - const { cid, deleted, depth, error, locked, removed, postCid, replyCount, state, subplebbitAddress, timestamp } = post || {}; + const { cid, deleted, depth, locked, removed, postCid, replyCount, state, subplebbitAddress, timestamp } = post || {}; const [sortBy, setSortBy] = useState('best'); const unsortedReplies = useReplies(post); @@ -170,11 +171,6 @@ const Post = ({ post }: { post: Comment }) => { {replies.length === 0 && replyCount !== undefined && !(isInPostContextView || isSingleComment) && (
{t('nothing_found')}
)} - {error && ( -
- {t('error')}: {error.message} -
- )} {isSingleComment ? ( ) : ( @@ -285,6 +281,12 @@ const PostPage = () => { const { hasAcceptedWarning } = useContentOptionsStore(); const isBroadlyNsfwSubplebbit = useIsBroadlyNsfwSubplebbit(subplebbitAddress || ''); + useEffect(() => { + if (post?.error) { + console.log(post.error); + } + }, [post?.error]); + const postTitle = post.title?.slice(0, 40) || post?.content?.slice(0, 40); const subplebbitTitle = subplebbit?.title || subplebbit?.shortAddress; useEffect(() => { @@ -303,6 +305,11 @@ const PostPage = () => {
{isInPendingPostView && params?.accountCommentIndex ? : isInPostContextView ? : } + {post?.error && ( +
+ +
+ )}
); }; diff --git a/src/views/profile/profile.module.css b/src/views/profile/profile.module.css index 0e6e3502..480faa33 100644 --- a/src/views/profile/profile.module.css +++ b/src/views/profile/profile.module.css @@ -147,3 +147,10 @@ div[data-viewport-type="window"] { margin-right: -3px; margin-top: -3px; } + +.error { + padding-left: 10px; + font-size: 12px; + margin-top: -10px; + padding-bottom: 10px; +} \ No newline at end of file diff --git a/src/views/profile/profile.tsx b/src/views/profile/profile.tsx index 61601d96..ea71917e 100644 --- a/src/views/profile/profile.tsx +++ b/src/views/profile/profile.tsx @@ -9,6 +9,7 @@ import AuthorSidebar from '../../components/author-sidebar'; import Post from '../../components/post'; import Reply from '../../components/reply'; import styles from './profile.module.css'; +import ErrorDisplay from '../../components/error-display'; const pageSize = 10; const sortTypes: string[] = ['new', 'old']; @@ -135,15 +136,26 @@ const VirtualizedCommentList = ({ comments }: { comments: any[] }) => { const Overview = () => { const { t } = useTranslation(); - const { accountComments } = useAccountComments(); + const { error, accountComments } = useAccountComments(); const [sortType, setSortType] = useState('new'); const sortedComments = useMemo(() => { return [...accountComments].sort((a, b) => (sortType === 'new' ? b.timestamp - a.timestamp : a.timestamp - b.timestamp)); }, [accountComments, sortType]); + useEffect(() => { + if (error) { + console.log(error); + } + }, [error]); + return (
+ {error && ( +
+ +
+ )} {sortedComments.length === 0 ?
{t('nothing_found')}
: }
@@ -152,7 +164,7 @@ const Overview = () => { const Comments = () => { const { t } = useTranslation(); - const { accountComments } = useAccountComments(); + const { error, accountComments } = useAccountComments(); const [sortType, setSortType] = useState('new'); const replyComments = useMemo(() => accountComments?.filter((comment) => comment.parentCid) || [], [accountComments]); @@ -161,8 +173,19 @@ const Comments = () => { return [...replyComments].sort((a, b) => (sortType === 'new' ? b.timestamp - a.timestamp : a.timestamp - b.timestamp)); }, [replyComments, sortType]); + useEffect(() => { + if (error) { + console.log(error); + } + }, [error]); + return (
+ {error && ( +
+ +
+ )} {sortedComments.length === 0 ?
{t('nothing_found')}
: }
@@ -171,7 +194,7 @@ const Comments = () => { const Submitted = () => { const { t } = useTranslation(); - const { accountComments } = useAccountComments(); + const { error, accountComments } = useAccountComments(); const [sortType, setSortType] = useState('new'); const postComments = useMemo(() => accountComments?.filter((comment) => !comment.parentCid) || [], [accountComments]); @@ -180,8 +203,19 @@ const Submitted = () => { return [...postComments].sort((a, b) => (sortType === 'new' ? b.timestamp - a.timestamp : a.timestamp - b.timestamp)); }, [postComments, sortType]); + useEffect(() => { + if (error) { + console.log(error); + } + }, [error]); + return (
+ {error && ( +
+ +
+ )} {sortedComments.length === 0 ?
{t('nothing_found')}
: }
@@ -190,7 +224,7 @@ const Submitted = () => { const VotedComments = ({ voteType }: { voteType: 1 | -1 }) => { const { t } = useTranslation(); - const { accountVotes } = useAccountVotes(); + const { error, accountVotes } = useAccountVotes(); const [currentPage, setCurrentPage] = useState(1); const [sortType, setSortType] = useState('new'); @@ -203,8 +237,19 @@ const VotedComments = ({ voteType }: { voteType: 1 | -1 }) => { const paginatedCids = votedCommentCids.slice((currentPage - 1) * pageSize, currentPage * pageSize); const hasMore = currentPage * pageSize < votedCommentCids.length; + useEffect(() => { + if (error) { + console.log(error); + } + }, [error]); + return (
+ {error && ( +
+ +
+ )} {paginatedCids.length === 0 ?
{t('nothing_found')}
: paginatedCids.map((cid) => )} diff --git a/src/views/settings/account-settings/account-settings.tsx b/src/views/settings/account-settings/account-settings.tsx index 832c53c6..2bb4dad6 100644 --- a/src/views/settings/account-settings/account-settings.tsx +++ b/src/views/settings/account-settings/account-settings.tsx @@ -166,7 +166,10 @@ const AccountSettings = () => { const [text, setText] = useState(''); const accountJson = useMemo( - () => stringify({ account: { ...account, plebbit: undefined, karma: undefined, plebbitReactOptions: undefined, unreadNotificationCount: undefined } }), + () => + stringify({ + account: { ...account, plebbit: undefined, karma: undefined, plebbitReactOptions: undefined, unreadNotificationCount: undefined, signer: undefined }, + }), [account], ); @@ -192,10 +195,19 @@ const AccountSettings = () => { const saveAccount = async () => { try { - const newAccount = JSON.parse(text).account; - // force keeping the same id, makes it easier to copy paste - await setAccount({ ...newAccount, id: account?.id }); - alert(`Saved ${newAccount.name}`); + const newAccountFromTextarea = JSON.parse(text).account; + // re-attach all original fields that were not meant to be editable in the textarea + const finalAccount = { + ...newAccountFromTextarea, + id: account?.id, // force keeping the same id, makes it easier to copy paste + signer: account?.signer, + plebbit: account?.plebbit, + karma: account?.karma, + plebbitReactOptions: account?.plebbitReactOptions, + unreadNotificationCount: account?.unreadNotificationCount, + }; + await setAccount(finalAccount); + alert(`Saved ${finalAccount.name}`); } catch (error) { if (error instanceof Error) { alert(error.message); diff --git a/src/views/subplebbit/subplebbit.tsx b/src/views/subplebbit/subplebbit.tsx index aa802a49..54fe2ca6 100644 --- a/src/views/subplebbit/subplebbit.tsx +++ b/src/views/subplebbit/subplebbit.tsx @@ -12,6 +12,7 @@ import useFeedResetStore from '../../stores/use-feed-reset-store'; import { usePinnedPostsStore } from '../../stores/use-pinned-posts-store'; import { useIsBroadlyNsfwSubplebbit } from '../../hooks/use-is-broadly-nsfw-subplebbit'; import useTimeFilter, { isValidTimeFilterName } from '../../hooks/use-time-filter'; +import ErrorDisplay from '../../components/error-display'; import LoadingEllipsis from '../../components/loading-ellipsis'; import Over18Warning from '../../components/over-18-warning'; import Post from '../../components/post'; @@ -27,7 +28,6 @@ interface FooterProps { isOnline: boolean; started: boolean; isSubCreatedButNotYetPublished: boolean; - error: Error | null; hasMore: boolean; timeFilterName: string; reset: () => void; @@ -43,7 +43,6 @@ const Footer = ({ isOnline, started, isSubCreatedButNotYetPublished, - error, hasMore, timeFilterName, reset, @@ -85,12 +84,6 @@ const Footer = ({ const loadingString = ( <>
{loadingStateString === 'Failed' ? 'failed' : }
- {error && ( -
-
- {t('error')}: {error.message} -
- )} ); @@ -258,11 +251,13 @@ const Subplebbit = () => { }; if (searchQuery) { - options.filter = (comment: Comment) => { - if (!searchQuery.trim()) return true; - return commentMatchesPattern(comment, searchQuery); + options.filter = { + filter: (comment: Comment) => { + if (!searchQuery.trim()) return true; + return commentMatchesPattern(comment, searchQuery); + }, + key: `search-filter-${searchQuery}`, }; - options.filterKey = `search-filter-${searchQuery}`; } return options; @@ -330,7 +325,6 @@ const Subplebbit = () => { isOnline, started, isSubCreatedButNotYetPublished, - error: error || null, hasMore, timeFilterName: searchQuery ? 'all' : timeFilterName || '', reset, @@ -359,6 +353,12 @@ const Subplebbit = () => { const hasUnhiddenAnyNsfwCommunity = !hideAdultCommunities || !hideGoreCommunities || !hideAntiCommunities || !hideVulgarCommunities; const isBroadlyNsfwSubplebbit = useIsBroadlyNsfwSubplebbit(subplebbitAddress || ''); + useEffect(() => { + if (error) { + console.log(error); + } + }, [error]); + // page title useEffect(() => { document.title = title ? title : shortAddress || subplebbitAddress; @@ -371,6 +371,11 @@ const Subplebbit = () => {
+ {error && ( +
+ +
+ )}
{ const Infobar = () => { const account = useAccount(); - const { accountSubplebbits } = useAccountSubplebbits(); + const { accountSubplebbits, error: accountSubplebbitsError } = useAccountSubplebbits(); + const { setError } = useErrorStore(); + + useEffect(() => { + setError('Infobar_useAccountSubplebbits', accountSubplebbitsError); + }, [accountSubplebbitsError, setError]); + const subscriptions = account?.subscriptions || []; const { t } = useTranslation(); const location = useLocation(); @@ -285,7 +293,12 @@ const Subplebbit = ({ subplebbit, tags, index }: SubplebbitProps) => { const AccountSubplebbits = ({ viewRole }: { viewRole: string }) => { const account = useAccount(); - const { accountSubplebbits } = useAccountSubplebbits(); + const { accountSubplebbits, error: accountSubplebbitsError } = useAccountSubplebbits(); + const { setError } = useErrorStore(); + + useEffect(() => { + setError('AccountSubplebbits_useAccountSubplebbits', accountSubplebbitsError); + }, [accountSubplebbitsError, setError, viewRole]); const filteredSubplebbitsArray = useMemo(() => { return Object.values(accountSubplebbits).filter((subplebbit: any) => { @@ -300,7 +313,13 @@ const AccountSubplebbits = ({ viewRole }: { viewRole: string }) => { const SubscriberSubplebbits = () => { const account = useAccount(); - const { subplebbits } = useSubplebbits({ subplebbitAddresses: account?.subscriptions }); + const { subplebbits, error: subplebbitsError } = useSubplebbits({ subplebbitAddresses: account?.subscriptions }); + const { setError } = useErrorStore(); + + useEffect(() => { + setError('SubscriberSubplebbits_useSubplebbits', subplebbitsError); + }, [subplebbitsError, setError]); + const subplebbitsArray = useMemo(() => Object.values(subplebbits), [subplebbits]); return subplebbitsArray?.map((subplebbit, index) => subplebbit && ).filter(Boolean); }; @@ -314,7 +333,13 @@ const AllDefaultSubplebbits = () => { const urlTag = pathname.includes('/tag/') ? pathname.split('/').pop() : undefined; const currentTag = urlTag && validTags.includes(urlTag) ? urlTag : undefined; - const { subplebbits } = useSubplebbits({ subplebbitAddresses }); + const { subplebbits, error: subplebbitsError } = useSubplebbits({ subplebbitAddresses }); + const { setError } = useErrorStore(); + + useEffect(() => { + setError('AllDefaultSubplebbits_useSubplebbits', subplebbitsError); + }, [subplebbitsError, setError]); + const subplebbitsArray = useMemo(() => Object.values(subplebbits), [subplebbits]); return subplebbitsArray @@ -329,11 +354,21 @@ const AllDefaultSubplebbits = () => { const AllAccountSubplebbits = () => { const account = useAccount(); - const { accountSubplebbits } = useAccountSubplebbits(); + const { accountSubplebbits, error: accountSubplebbitsError } = useAccountSubplebbits(); + const { setError } = useErrorStore(); + + useEffect(() => { + setError('AllAccountSubplebbits_useAccountSubplebbits', accountSubplebbitsError); + }, [accountSubplebbitsError, setError]); + const accountSubplebbitAddresses = Object.keys(accountSubplebbits); const subscriptionsArray = account?.subscriptions ?? []; const uniqueAddresses = Array.from(new Set([...accountSubplebbitAddresses, ...subscriptionsArray])); - const { subplebbits } = useSubplebbits({ subplebbitAddresses: uniqueAddresses }); + const { subplebbits, error: subplebbitsError } = useSubplebbits({ subplebbitAddresses: uniqueAddresses }); + + useEffect(() => { + setError('AllAccountSubplebbits_useSubplebbits', subplebbitsError); + }, [subplebbitsError, setError]); const subplebbitsArray = useMemo(() => Object.values(subplebbits ?? {}), [subplebbits]); return subplebbitsArray?.map((subplebbit, index) => subplebbit && ).filter(Boolean); }; @@ -341,6 +376,24 @@ const AllAccountSubplebbits = () => { const Subplebbits = () => { const { t } = useTranslation(); const location = useLocation(); + const { errors, clearAllErrors } = useErrorStore(); + + // Clear errors on component unmount or location change + useEffect(() => { + return () => { + clearAllErrors(); + }; + }, [location, clearAllErrors]); + + // Console log errors + useEffect(() => { + Object.entries(errors).forEach(([source, errorObj]) => { + if (errorObj) { + console.error(`Error from ${source}:`, errorObj.message, errorObj.stack); + } + }); + }, [errors]); + const isInSubplebbitsSubscriberView = isSubplebbitsSubscriberView(location.pathname); const isInSubplebbitsModeratorView = isSubplebbitsModeratorView(location.pathname); const isInSubplebbitsAdminView = isSubplebbitsAdminView(location.pathname); @@ -385,6 +438,32 @@ const Subplebbits = () => { document.title = documentTitle; }, [documentTitle]); + const renderErrors = () => { + const errorsToDisplay: JSX.Element[] = []; + Object.entries(errors).forEach(([source, errorObj]) => { + if (!errorObj) return; + + if ( + source === 'Infobar_useAccountSubplebbits' && + (isInSubplebbitsView || isInSubplebbitsSubscriberView || isInSubplebbitsModeratorView || isInSubplebbitsAdminView || isInSubplebbitsOwnerView) + ) { + errorsToDisplay.push(); + } else if (source === 'AccountSubplebbits_useAccountSubplebbits' && (isInSubplebbitsModeratorView || isInSubplebbitsAdminView || isInSubplebbitsOwnerView)) { + errorsToDisplay.push(); + } else if (source === 'SubscriberSubplebbits_useSubplebbits' && isInSubplebbitsSubscriberView) { + errorsToDisplay.push(); + } else if (source === 'AllDefaultSubplebbits_useSubplebbits' && isInSubplebbitsVoteView) { + errorsToDisplay.push(); + } else if (source === 'AllAccountSubplebbits_useAccountSubplebbits' && isInSubplebbitsView) { + errorsToDisplay.push(); + } else if (source === 'AllAccountSubplebbits_useSubplebbits' && isInSubplebbitsView) { + // Avoid duplicate key if both errors from AllAccountSubplebbits are present + errorsToDisplay.push(); + } + }); + return errorsToDisplay; + }; + return (
@@ -396,6 +475,7 @@ const Subplebbits = () => { )} +
{renderErrors()}
{isInSubplebbitsVoteView && } {(isInSubplebbitsModeratorView || isInSubplebbitsAdminView || isInSubplebbitsOwnerView) && } {isInSubplebbitsSubscriberView && } diff --git a/yarn.lock b/yarn.lock index 2c94fbbc..fce3d395 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3373,12 +3373,12 @@ dependencies: "@noble/hashes" "1.3.2" -"@noble/curves@1.8.1": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.1.tgz#19bc3970e205c99e4bdb1c64a4785706bce497ff" - integrity sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ== +"@noble/curves@1.8.2", "@noble/curves@~1.8.1": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.2.tgz#8f24c037795e22b90ae29e222a856294c1d9ffc7" + integrity sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g== dependencies: - "@noble/hashes" "1.7.1" + "@noble/hashes" "1.7.2" "@noble/curves@^1.1.0", "@noble/curves@^1.4.0", "@noble/curves@^1.4.2", "@noble/curves@^1.6.0", "@noble/curves@^1.7.0", "@noble/curves@~1.9.0": version "1.9.0" @@ -3387,13 +3387,6 @@ dependencies: "@noble/hashes" "1.8.0" -"@noble/curves@~1.8.1": - version "1.8.2" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.2.tgz#8f24c037795e22b90ae29e222a856294c1d9ffc7" - integrity sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g== - dependencies: - "@noble/hashes" "1.7.2" - "@noble/ed25519@^1.5.1": version "1.7.5" resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.5.tgz#94df8bdb9fec9c4644a56007eecb57b0e9fbd0d7" @@ -3404,11 +3397,6 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== -"@noble/hashes@1.7.1": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.1.tgz#5738f6d765710921e7a751e00c20ae091ed8db0f" - integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ== - "@noble/hashes@1.7.2", "@noble/hashes@~1.7.1": version "1.7.2" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.2.tgz#d53c65a21658fb02f3303e7ee3ba89d6754c64b4" @@ -3623,9 +3611,9 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#26c097afa62a87c9650dcdfad52babbc77744cbc": - version "0.0.6" - resolved "https://github.com/plebbit/plebbit-js.git#26c097afa62a87c9650dcdfad52babbc77744cbc" +"@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#a2cb6ca6462b2e2e3b66f71204375a81ed05cf80": + version "0.0.7" + resolved "https://github.com/plebbit/plebbit-js.git#a2cb6ca6462b2e2e3b66f71204375a81ed05cf80" dependencies: "@bonfida/spl-name-service" "3.0.10" "@chainsafe/libp2p-gossipsub" "14.1.0" @@ -3652,9 +3640,9 @@ cbor "9.0.1" debounce "1.2.1" err-code "3.0.1" - ethers "6.13.5" + ethers "6.14.0" ext-name "5.0.0" - file-type "16.5.4" + file-type "20.5.0" helia "5.2.0" hpagent "1.2.0" it-all "3.0.6" @@ -3686,8 +3674,8 @@ ts-custom-error "3.3.1" typestub-ipfs-only-hash "4.0.0" uuid "11.1.0" - viem "2.23.5" - zod "3.24.3" + viem "2.29.0" + zod "3.24.4" "@plebbit/plebbit-logger@github:plebbit/plebbit-logger#355a96d7659ed820047980049dfa627d30d83a69": version "0.0.1" @@ -3702,11 +3690,11 @@ dependencies: debug "4.3.4" -"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#047a4e01377fba0b5cf1531814446715d7130aa4": +"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#9d0e851a4a571e15053343c66186418958d53550": version "0.0.1" - resolved "https://github.com/plebbit/plebbit-react-hooks.git#047a4e01377fba0b5cf1531814446715d7130aa4" + resolved "https://github.com/plebbit/plebbit-react-hooks.git#9d0e851a4a571e15053343c66186418958d53550" dependencies: - "@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#26c097afa62a87c9650dcdfad52babbc77744cbc" + "@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#a2cb6ca6462b2e2e3b66f71204375a81ed05cf80" "@plebbit/plebbit-logger" "https://github.com/plebbit/plebbit-logger.git" assert "2.0.0" ethers "5.6.9" @@ -3718,11 +3706,11 @@ uuid "8.3.2" zustand "4.0.0" -"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#41f0cdf14617f5e843311986d66f14f2f8fcbce0": +"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#d6347601b40a237c26518ab9cc9de7d29bb69b62": version "0.0.1" - resolved "https://github.com/plebbit/plebbit-react-hooks.git#41f0cdf14617f5e843311986d66f14f2f8fcbce0" + resolved "https://github.com/plebbit/plebbit-react-hooks.git#d6347601b40a237c26518ab9cc9de7d29bb69b62" dependencies: - "@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#26c097afa62a87c9650dcdfad52babbc77744cbc" + "@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#a2cb6ca6462b2e2e3b66f71204375a81ed05cf80" "@plebbit/plebbit-logger" "https://github.com/plebbit/plebbit-logger.git" assert "2.0.0" ethers "5.6.9" @@ -4338,6 +4326,15 @@ dependencies: "@babel/runtime" "^7.12.5" +"@tokenizer/inflate@^0.2.6": + version "0.2.7" + resolved "https://registry.yarnpkg.com/@tokenizer/inflate/-/inflate-0.2.7.tgz#32dd9dfc9abe457c89b3d9b760fc0690c85a103b" + integrity sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg== + dependencies: + debug "^4.4.0" + fflate "^0.8.2" + token-types "^6.0.0" + "@tokenizer/token@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" @@ -6900,7 +6897,7 @@ debug@2: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6: +debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6, debug@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -8109,10 +8106,10 @@ ethers@5.6.9: "@ethersproject/web" "5.6.1" "@ethersproject/wordlists" "5.6.1" -ethers@6.13.5: - version "6.13.5" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.5.tgz#8c1d6ac988ac08abc3c1d8fabbd4b8b602851ac4" - integrity sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ== +ethers@6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.14.0.tgz#b80eca3b60fc97da53f73b77629ce7392568eae0" + integrity sha512-KgHwltNSMdbrGWEyKkM0Rt2s+u1nDH/5BVDQakLinzGEJi4bWindBzZSCC4gKsbZjwDTI6ex/8suR9Ihbmz4IQ== dependencies: "@adraffy/ens-normalize" "1.10.1" "@noble/curves" "1.2.0" @@ -8328,6 +8325,11 @@ fdir@^6.4.4: resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.4.tgz#1cfcf86f875a883e19a8fab53622cfe992e8d2f9" integrity sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg== +fflate@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" + integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== + figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -8349,14 +8351,15 @@ file-selector@^2.1.0: dependencies: tslib "^2.7.0" -file-type@16.5.4: - version "16.5.4" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.4.tgz#474fb4f704bee427681f98dd390058a172a6c2fd" - integrity sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw== +file-type@20.5.0: + version "20.5.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-20.5.0.tgz#616e90564e6ffabab22ad9763e28efcc5c95aee0" + integrity sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg== dependencies: - readable-web-to-node-stream "^3.0.0" - strtok3 "^6.2.4" - token-types "^4.1.1" + "@tokenizer/inflate" "^0.2.6" + strtok3 "^10.2.0" + token-types "^6.0.0" + uint8array-extras "^1.4.0" file-type@^3.8.0: version "3.9.0" @@ -12385,10 +12388,10 @@ own-keys@^1.0.1: object-keys "^1.1.1" safe-push-apply "^1.0.0" -ox@0.6.7: - version "0.6.7" - resolved "https://registry.yarnpkg.com/ox/-/ox-0.6.7.tgz#afd53f2ecef68b8526660e9d29dee6e6b599a832" - integrity sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA== +ox@0.6.9: + version "0.6.9" + resolved "https://registry.yarnpkg.com/ox/-/ox-0.6.9.tgz#da1ee04fa10de30c8d04c15bfb80fe58b1f554bd" + integrity sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug== dependencies: "@adraffy/ens-normalize" "^1.10.1" "@noble/curves" "^1.6.0" @@ -12662,10 +12665,10 @@ pbkdf2@^3.1.2: safe-buffer "^5.0.1" sha.js "^2.4.8" -peek-readable@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72" - integrity sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg== +peek-readable@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-7.0.0.tgz#c6e4e78ec76f7005e5f6b51ffc93fdb91ede6512" + integrity sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ== peer-id@0.16.0: version "0.16.0" @@ -13232,7 +13235,7 @@ readable-stream@3, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@4.7.0, readable-stream@^4.7.0: +readable-stream@4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.7.0.tgz#cedbd8a1146c13dfff8dab14068028d58c15ac91" integrity sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg== @@ -13256,13 +13259,6 @@ readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@^2.3.8: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-web-to-node-stream@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz#392ba37707af5bf62d725c36c1b5d6ef4119eefc" - integrity sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw== - dependencies: - readable-stream "^4.7.0" - rechoir@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" @@ -14399,13 +14395,13 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== -strtok3@^6.2.4: - version "6.3.0" - resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-6.3.0.tgz#358b80ffe6d5d5620e19a073aa78ce947a90f9a0" - integrity sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw== +strtok3@^10.2.0: + version "10.2.2" + resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-10.2.2.tgz#a4c6d78d15db02c5eb20d92af3eedf81edaf09d2" + integrity sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg== dependencies: "@tokenizer/token" "^0.3.0" - peek-readable "^4.1.0" + peek-readable "^7.0.0" style-to-object@^0.4.0: version "0.4.4" @@ -14707,10 +14703,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -token-types@^4.1.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/token-types/-/token-types-4.2.1.tgz#0f897f03665846982806e138977dbe72d44df753" - integrity sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ== +token-types@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/token-types/-/token-types-6.0.0.tgz#1ab26be1ef9c434853500c071acfe5c8dd6544a3" + integrity sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA== dependencies: "@tokenizer/token" "^0.3.0" ieee754 "^1.2.1" @@ -14954,6 +14950,11 @@ uint8-varint@^2.0.1, uint8-varint@^2.0.2, uint8-varint@^2.0.4: uint8arraylist "^2.0.0" uint8arrays "^5.0.0" +uint8array-extras@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/uint8array-extras/-/uint8array-extras-1.4.0.tgz#e42a678a6dd335ec2d21661333ed42f44ae7cc74" + integrity sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ== + uint8arraylist@^2.0.0, uint8arraylist@^2.4.3, uint8arraylist@^2.4.8: version "2.4.8" resolved "https://registry.yarnpkg.com/uint8arraylist/-/uint8arraylist-2.4.8.tgz#5a4d17f4defd77799cb38e93fd5db0f0dceddc12" @@ -15348,19 +15349,19 @@ vfile@^6.0.0: "@types/unist" "^3.0.0" vfile-message "^4.0.0" -viem@2.23.5: - version "2.23.5" - resolved "https://registry.yarnpkg.com/viem/-/viem-2.23.5.tgz#50fb9ea0701d58e6a7a1714ecaa5edfa100bb391" - integrity sha512-cUfBHdFQHmBlPW0loFXda0uZcoU+uJw3NRYQRwYgkrpH6PgovH8iuVqDn6t1jZk82zny4wQL54c9dCX2W9kLMg== +viem@2.29.0: + version "2.29.0" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.29.0.tgz#acaf936e534f61a2dfccd8128acce92bc724aba7" + integrity sha512-N6GeIuuay/spDyw+5FbSuNIkVN0da+jGOjdlC0bdatIN+N0jtOf9Zfj0pbXgpIJGwnM9ocxzTRt0HZVbHBdL2Q== dependencies: - "@noble/curves" "1.8.1" - "@noble/hashes" "1.7.1" + "@noble/curves" "1.8.2" + "@noble/hashes" "1.7.2" "@scure/bip32" "1.6.2" "@scure/bip39" "1.5.4" abitype "1.0.8" isows "1.0.6" - ox "0.6.7" - ws "8.18.0" + ox "0.6.9" + ws "8.18.1" vite-plugin-eslint@1.8.1: version "1.8.1" @@ -15802,21 +15803,16 @@ ws@8.17.1: resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== -ws@8.18.0: - version "8.18.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" - integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== +ws@8.18.1, ws@^8.18.0, ws@^8.4.0, ws@^8.5.0: + version "8.18.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" + integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== ws@^7.5.10: version "7.5.10" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== -ws@^8.18.0, ws@^8.4.0, ws@^8.5.0: - version "8.18.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" - integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== - xml2js@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" @@ -15914,7 +15910,12 @@ zod-validation-error@^3.0.3: resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6" integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ== -zod@3.24.3, zod@^3.22.4: +zod@3.24.4: + version "3.24.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.4.tgz#e2e2cca5faaa012d76e527d0d36622e0a90c315f" + integrity sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg== + +zod@^3.22.4: version "3.24.3" resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.3.tgz#1f40f750a05e477396da64438e0e1c0995dafd87" integrity sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==