Merge pull request #641 from plebbit/development

Development
This commit is contained in:
plebeius
2025-05-09 23:10:52 +02:00
committed by GitHub
30 changed files with 586 additions and 166 deletions

View File

@@ -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",

View File

@@ -0,0 +1,9 @@
.errorMessage {
color: var(--red);
}
.showFullErrorButton {
font-weight: 700;
cursor: pointer;
padding: 5px 0;
}

View File

@@ -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) && (
<div className={styles.error}>
<br />
<span>
{error?.message && (
<span className={styles.errorMessage}>
{t('error')}: {error.message}
</span>
)}
{(error?.stack || error?.details) && (
<>
{' — '}
<span className={styles.showFullErrorButton} onClick={() => setShowFullError(!showFullError)}>
{showFullError ? 'hide' : 'show'} full error
</span>
</>
)}
</span>
{showFullError && (
<>
{error?.stack && (
<>
<br />
<div className={styles.errorStack}>Stack: {error.stack}</div>
</>
)}
{error?.details && (
<>
<br />
<div className={styles.errorStack}>Details: {JSON.stringify(error?.details, null, 2)}</div>
</>
)}
</>
)}
</div>
)
);
};
export default ErrorDisplay;

View File

@@ -0,0 +1 @@
export { default } from './error-display';

View File

@@ -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 {

View File

@@ -138,7 +138,7 @@
}
.tagline {
font-size: 11px;
font-size: x-small;
}
.tagline a {

View File

@@ -5,6 +5,8 @@
width: 70px;
height: var(--height, 70px);
z-index: 1;
position: relative;
overflow: hidden;
}
.thumbnailHidden {

View File

@@ -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);
}

View File

@@ -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<number>(-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<HTMLInputElement>) => {
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<HTMLInputElement>) => {
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 (
<div ref={wrapperRef} className={`${styles.searchBarWrapper} ${isInHomeAboutView || isInSubplebbitAboutView || isInPostPageAboutView ? styles.mobileInfobar : ''}`}>
<form className={styles.searchBar} ref={searchBarRef} onSubmit={handleSearchSubmit}>
@@ -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)}
/>
<input type='submit' value='' />
</form>
{!isInCommunitySearch && isInputFocused && filteredCommunitySuggestions.length > 0 && (
<ul className={styles.dropdown}>
{filteredCommunitySuggestions.map((address, index) => (
<li
key={address}
className={`${styles.dropdownItem} ${index === activeDropdownIndex ? styles.activeDropdownItem : ''}`}
onClick={() => handleCommunitySelect(address)}
onMouseEnter={() => setActiveDropdownIndex(index)}
>
{address}
</li>
))}
</ul>
)}
<div className={`${styles.infobar} ${showExpando ? styles.slideDown : styles.slideUp} ${!isInFeedView ? styles.lessHeight : ''}`}>
{isInFeedView && (
<label>
<input type='checkbox' checked={isInCommunitySearch} onChange={() => handleCommunitySearchToggle(true)} />
{t('search_feed_post')}
</label>
)}
<label>
<input
type='checkbox'
@@ -168,12 +244,6 @@ const SearchBar = ({ isFocused = false, onExpandoChange }: SearchBarProps) => {
/>
{t('go_to_a_community')}
</label>
{isInFeedView && (
<label>
<input type='checkbox' checked={isInCommunitySearch} onChange={() => handleCommunitySearchToggle(true)} />
{t('search_feed_post')}
</label>
)}
</div>
</div>
);

View File

@@ -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;

View File

@@ -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<number>();
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')}
<div className={styles.nub} />
</div>
<div className={styles.createCommunitySubtitles}>
<div className={styles.communitySubtitles}>
<span className={styles.createCommunityImage}>
<img src='assets/sprout/sprout-2.png' alt='' />
</span>

View File

@@ -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.',
];

View File

@@ -0,0 +1,24 @@
import { create } from 'zustand';
interface ErrorStoreState {
errors: Record<string, Error | null>;
setError: (source: string, error: Error | null | undefined) => void;
clearAllErrors: () => void;
}
const useErrorStore = create<ErrorStoreState>((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;

View File

@@ -49,11 +49,11 @@ export const FAQ = () => {
New Users:
</h3>
<p>
Welcome! Your account <Link to='/profile'>u/{account?.author?.shortAddress}</Link> 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 <Link to='/settings#exportAccount'>preferences</Link>. 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 <Link to='/communities/vote'>default community list</Link>.
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 <Link to='/settings#exportAccount'>preferences</Link>. 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 <Link to='/communities/vote'>default community list</Link>.
</p>
<hr />
<h3 id='whatIsSeedit'>What is Seedit and how does it work?</h3>
@@ -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{' '}
<HashLink to='/settings#cryptoAddress'>change your account address</HashLink> to a unique readable name you own, by resolving it with a decentralized domain
name service such as{' '}
<a href='https://sns.id' target='_blank' rel='noopener noreferrer'>
<a href='https://ens.domains/' target='_blank' rel='noopener noreferrer'>
ENS
</a>{' '}
or{' '}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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 = () => {
<div className={isMobile ? styles.sidebarMobile : styles.sidebarDesktop}>
<AuthorSidebar />
</div>
{error && (
<div className={styles.error}>
<ErrorDisplay error={error} />
</div>
)}
<Virtuoso
increaseViewportBy={{ bottom: 1200, top: 600 }}
totalCount={authorComments?.length || 0}

View File

@@ -165,3 +165,10 @@
background-color: var(--button-background-color-hover);
text-decoration: underline;
}
.error {
font-size: 12px;
margin-top: -10px;
padding-left: 5px;
padding-bottom: 10px;
}

View File

@@ -69,11 +69,13 @@ const Home = () => {
};
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;

View File

@@ -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 (
<div className={styles.content}>
<InboxTabs />
@@ -109,6 +116,11 @@ const Inbox = () => {
<div className={styles.noNotifications}>{t('nothing_found')}</div>
)}
</div>
{error && (
<div className={styles.error}>
<ErrorDisplay error={error} />
</div>
)}
<Virtuoso
increaseViewportBy={{ bottom: 1200, top: 600 }}
totalCount={notifications?.length || 0}

View File

@@ -49,11 +49,13 @@ const Mod = () => {
};
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;

View File

@@ -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;
}

View File

@@ -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) && (
<div className={styles.noReplies}>{t('nothing_found')}</div>
)}
{error && (
<div className={styles.error}>
{t('error')}: {error.message}
</div>
)}
{isSingleComment ? (
<Reply key={`singleComment-${cid}`} reply={post} depth={0} isSingleComment={true} />
) : (
@@ -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 = () => {
<Sidebar subplebbit={subplebbit} comment={post} settings={subplebbit?.settings} />
</div>
{isInPendingPostView && params?.accountCommentIndex ? <Post post={pendingPost} /> : isInPostContextView ? <PostWithContext post={post} /> : <Post post={post} />}
{post?.error && (
<div className={styles.error}>
<ErrorDisplay error={post.error} />
</div>
)}
</div>
);
};

View File

@@ -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;
}

View File

@@ -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 (
<div>
{error && (
<div className={styles.error}>
<ErrorDisplay error={error} />
</div>
)}
<SortDropdown onSortChange={setSortType} />
{sortedComments.length === 0 ? <div className={styles.nothingFound}>{t('nothing_found')}</div> : <VirtualizedCommentList comments={sortedComments} />}
</div>
@@ -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 (
<div>
{error && (
<div className={styles.error}>
<ErrorDisplay error={error} />
</div>
)}
<SortDropdown onSortChange={setSortType} />
{sortedComments.length === 0 ? <div className={styles.nothingFound}>{t('nothing_found')}</div> : <VirtualizedCommentList comments={sortedComments} />}
</div>
@@ -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 (
<div>
{error && (
<div className={styles.error}>
<ErrorDisplay error={error} />
</div>
)}
<SortDropdown onSortChange={setSortType} />
{sortedComments.length === 0 ? <div className={styles.nothingFound}>{t('nothing_found')}</div> : <VirtualizedCommentList comments={sortedComments} />}
</div>
@@ -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 (
<div>
{error && (
<div className={styles.error}>
<ErrorDisplay error={error} />
</div>
)}
<SortDropdown onSortChange={setSortType} />
{paginatedCids.length === 0 ? <div className={styles.nothingFound}>{t('nothing_found')}</div> : paginatedCids.map((cid) => <CommentItem key={cid} cid={cid} />)}
<PaginationControls currentPage={currentPage} hasMore={hasMore} onPageChange={setCurrentPage} />

View File

@@ -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);

View File

@@ -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 = (
<>
<div className={styles.stateString}>{loadingStateString === 'Failed' ? 'failed' : <LoadingEllipsis string={loadingStateString} />}</div>
{error && (
<div style={{ color: 'red' }}>
<br />
{t('error')}: {error.message}
</div>
)}
</>
);
@@ -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 = () => {
<div className={styles.sidebar}>
<Sidebar subplebbit={subplebbit} isSubCreatedButNotYetPublished={started && isSubCreatedButNotYetPublished} settings={settings} reset={reset} />
</div>
{error && (
<div className={styles.error}>
<ErrorDisplay error={error} />
</div>
)}
<div className={styles.feed}>
<Virtuoso
increaseViewportBy={{ bottom: 1200, top: 600 }}

View File

@@ -340,3 +340,10 @@
vertical-align: top;
margin: 0 2px;
}
.error {
font-size: 12px;
margin-top: -10px;
margin-bottom: 10px;
margin-left: 10px;
}

View File

@@ -14,10 +14,12 @@ import {
isSubplebbitsVotePassingView,
isSubplebbitsVoteRejectingView,
} from '../../lib/utils/view-utils';
import useErrorStore from '../../stores/use-error-store';
import { useDefaultSubplebbitAddresses, useDefaultSubplebbitTags } from '../../hooks/use-default-subplebbits';
import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
import useIsMobile from '../../hooks/use-is-mobile';
import useIsSubplebbitOffline from '../../hooks/use-is-subplebbit-offline';
import ErrorDisplay from '../../components/error-display';
import Markdown from '../../components/markdown';
import Label from '../../components/post/label';
import Sidebar from '../../components/sidebar';
@@ -108,7 +110,13 @@ const VoteTabs = () => {
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 && <Subplebbit key={index} subplebbit={subplebbit} index={index} />).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 && <Subplebbit key={index} subplebbit={subplebbit} index={index} />).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(<ErrorDisplay key={source} error={errorObj} />);
} else if (source === 'AccountSubplebbits_useAccountSubplebbits' && (isInSubplebbitsModeratorView || isInSubplebbitsAdminView || isInSubplebbitsOwnerView)) {
errorsToDisplay.push(<ErrorDisplay key={source} error={errorObj} />);
} else if (source === 'SubscriberSubplebbits_useSubplebbits' && isInSubplebbitsSubscriberView) {
errorsToDisplay.push(<ErrorDisplay key={source} error={errorObj} />);
} else if (source === 'AllDefaultSubplebbits_useSubplebbits' && isInSubplebbitsVoteView) {
errorsToDisplay.push(<ErrorDisplay key={source} error={errorObj} />);
} else if (source === 'AllAccountSubplebbits_useAccountSubplebbits' && isInSubplebbitsView) {
errorsToDisplay.push(<ErrorDisplay key={source} error={errorObj} />);
} else if (source === 'AllAccountSubplebbits_useSubplebbits' && isInSubplebbitsView) {
// Avoid duplicate key if both errors from AllAccountSubplebbits are present
errorsToDisplay.push(<ErrorDisplay key={`${source}_subplebbits`} error={errorObj} />);
}
});
return errorsToDisplay;
};
return (
<div className={styles.content}>
<div className={styles.sidebar}>
@@ -396,6 +475,7 @@ const Subplebbits = () => {
<VoteTabs />
)}
<Infobar />
<div className={styles.error}>{renderErrors()}</div>
{isInSubplebbitsVoteView && <AllDefaultSubplebbits />}
{(isInSubplebbitsModeratorView || isInSubplebbitsAdminView || isInSubplebbitsOwnerView) && <AccountSubplebbits viewRole={viewRole} />}
{isInSubplebbitsSubscriberView && <SubscriberSubplebbits />}

171
yarn.lock
View File

@@ -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==