Merge pull request #201 from plebbit/development

Development
This commit is contained in:
plebeius.eth
2024-01-27 11:38:37 +01:00
committed by GitHub
12 changed files with 212 additions and 207 deletions

View File

@@ -6,7 +6,7 @@
"private": true,
"dependencies": {
"@floating-ui/react": "0.26.1",
"@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#80f19b488013cf386f4926fefb5790c79a3b9b96",
"@plebbit/plebbit-react-hooks": "https://github.com/plebbit/plebbit-react-hooks.git#6b614e0bf6c304de7b184564198a0ce7b98bca1d",
"@testing-library/jest-dom": "5.14.1",
"@testing-library/react": "13.0.0",
"@testing-library/user-event": "13.2.1",

View File

@@ -109,8 +109,8 @@ function App() {
<Route path='/communities/admin' element={<Subplebbits />} />
<Route path='/communities/owner' element={<Subplebbits />} />
<Route path='/communities/vote' element={<Subplebbits />} />
<Route path='/communities/vote/passed' element={<Subplebbits />} />
<Route path='/communities/vote/rejected' element={<Subplebbits />} />
<Route path='/communities/vote/passing' element={<Subplebbits />} />
<Route path='/communities/vote/rejecting' element={<Subplebbits />} />
</Route>
</Route>
</Routes>

View File

@@ -6,12 +6,6 @@ import breaks from 'remark-breaks';
import remarkGfm from 'remark-gfm';
const Markdown = ({ content }: { content: string }) => {
// replace \n with \n\n when it follows a sentence starting with '>'
let preserveNewlineAfterQuote = content?.replace(/(^|\n)(>[^>].*?)(\n)/gm, '$1\n$2\n\n');
// replace \n\n with \n for list items separated by two newlines
let adjustListNewlines = preserveNewlineAfterQuote?.replace(/(\n\n)([*-]|[0-9]+\.) (.+?)(?=\n\n([*-]|[0-9]+\.) )/gms, '\n$2 $3');
const customSchema = useMemo(
() => ({
...defaultSchema,
@@ -41,7 +35,7 @@ const Markdown = ({ content }: { content: string }) => {
return (
<span className={styles.markdown}>
<ReactMarkdown
children={adjustListNewlines}
children={content}
remarkPlugins={[excludeBlockquote, [remarkGfm, { singleTilde: false }], breaks]}
rehypePlugins={[[rehypeSanitize, customSchema]]}
components={{

View File

@@ -6,6 +6,7 @@
line-height: 14px;
padding: 0 4px;
margin: 0 2px 2px 0;
text-transform: uppercase;
}
.black {

View File

@@ -4,35 +4,41 @@ import styles from './label.module.css';
export const DeletedLabel = () => {
const { t } = useTranslation();
return <span className={`${styles.stamp} ${styles.red}`}>{t('deleted').toUpperCase()}</span>;
return <span className={`${styles.stamp} ${styles.red}`}>{t('deleted')}</span>;
};
export const FailedLabel = () => {
const { t } = useTranslation();
return <span className={`${styles.stamp} ${styles.red}`}>{t('failed').toUpperCase()}</span>;
return <span className={`${styles.stamp} ${styles.red}`}>{t('failed')}</span>;
};
export const PendingLabel = () => {
const { t } = useTranslation();
return <span className={`${styles.stamp} ${styles.yellow}`}>{t('pending').toUpperCase()}</span>;
return <span className={`${styles.stamp} ${styles.yellow}`}>{t('pending')}</span>;
};
export const RemovedLabel = () => {
const { t } = useTranslation();
return <span className={`${styles.stamp} ${styles.red}`}>{t('removed').toUpperCase()}</span>;
return <span className={`${styles.stamp} ${styles.red}`}>{t('removed')}</span>;
};
export const SpoilerLabel = () => {
const { t } = useTranslation();
return <span className={`${styles.stamp} ${styles.black}`}>{t('spoiler').toUpperCase()}</span>;
return <span className={`${styles.stamp} ${styles.black}`}>{t('spoiler')}</span>;
};
export const RoleLabel = ({ role }: { role: string }) => {
const { t } = useTranslation();
return <span className={`${styles.stamp} ${styles.green}`}>{t(role).toUpperCase()}</span>;
return <span className={`${styles.stamp} ${styles.green}`}>{t(role)}</span>;
};
export const OfflineLabel = () => {
const { t } = useTranslation();
return <span className={`${styles.stamp} ${styles.red}`}>{t('offline')}</span>;
};

View File

@@ -92,7 +92,70 @@ export const getDefaultChallengeDescription = (challengeType: string) => {
}
};
export const getDefaultOptionInputs = (challengeType: string) => {
export const getDefaultChallengeOptions = (challengeType: string) => {
switch (challengeType) {
case 'text-math':
return {
difficulty: '1',
};
case 'captcha-canvas-v3':
return {
characters: '',
height: '',
width: '',
color: '',
};
case 'fail':
return {
error: "You're not allowed to publish.",
};
case 'blacklist':
return {
blacklist: '',
error: "You're blacklisted.",
};
case 'question':
return {
question: '',
answer: '',
};
case 'evm-contract-call':
return {
chainTicker: 'eth',
address: '',
abi: '',
condition: '',
error: "Contract call response doesn't pass condition.",
};
default:
return {};
}
};
export type OptionInput = {
option: string;
label: string;
default?: string;
description: string;
placeholder?: string;
required?: boolean;
};
export type Exclude = {
postScore?: number;
postReply?: number;
firstCommentTimestamp?: number;
challenges?: number[];
post?: boolean;
reply?: boolean;
vote?: boolean;
role?: string[];
address?: string[];
rateLimit?: number;
rateLimitChallengeSuccess?: boolean;
};
export const getDefaultChallengeSettings = (challengeType: string) => {
switch (challengeType) {
case 'text-math':
return [

View File

@@ -148,10 +148,10 @@ export const isSubplebbitsVoteView = (pathname: string): boolean => {
return pathname === '/communities/vote';
};
export const isSubplebbitsVotePassedView = (pathname: string): boolean => {
return pathname === '/communities/vote/passed';
export const isSubplebbitsVotePassingView = (pathname: string): boolean => {
return pathname === '/communities/vote/passing';
};
export const isSubplebbitsVoteRejectedView = (pathname: string): boolean => {
return pathname === '/communities/vote/rejected';
export const isSubplebbitsVoteRejectingView = (pathname: string): boolean => {
return pathname === '/communities/vote/rejecting';
};

View File

@@ -65,7 +65,7 @@
text-transform: lowercase;
}
.fullSettings textarea {
.JSONSettings textarea {
height: 200px;
}
@@ -114,13 +114,13 @@
}
.challengeOption {
font-size: 15px;
margin-top: 10px !important;
font-size: 14px;
margin-top: 10px;
}
.challengeDescription {
margin: 15px 0 15px 0;
font-size: 15px;
margin: 20px 0 0 0;
font-size: 16px;
}
.challengeOptionDescription {
@@ -137,6 +137,7 @@
width: auto !important;
outline: none;
cursor: pointer;
margin-top: 2px;
}
.challengesArray {

View File

@@ -6,7 +6,14 @@ import { useTranslation } from 'react-i18next';
import { create } from 'zustand';
import styles from './subplebbit-settings.module.css';
import { isValidURL } from '../../../lib/utils/url-utils';
import { getDefaultChallengeDescription, getDefaultExclude, getDefaultOptionInputs } from '../../../lib/utils/challenge-utils';
import {
OptionInput,
Exclude,
getDefaultChallengeDescription,
getDefaultExclude,
getDefaultChallengeOptions,
getDefaultChallengeSettings,
} from '../../../lib/utils/challenge-utils';
import LoadingEllipsis from '../../../components/loading-ellipsis';
import Sidebar from '../../../components/sidebar';
@@ -298,109 +305,110 @@ interface ChallengeSettingsProps {
showSettings: boolean;
}
type OptionInput = {
option: string;
value?: string;
label: string;
default?: string;
description: string;
placeholder?: string;
required?: boolean;
};
const rolesToExclude = ['moderator', 'admin', 'owner'];
const actionsToExclude: Array<'post' | 'reply' | 'vote'> = ['post', 'reply', 'vote'];
const ChallengeSettings = ({ challenge, index, setSubmitStore, settings, showSettings }: ChallengeSettingsProps) => {
const { exclude, name, optionInputs } = challenge || {};
const { exclude, name, options } = challenge || {};
const challengeSettings: OptionInput[] = getDefaultChallengeSettings(name);
const handleOptionChange = (optionName: string, newValue: string) => {
const updatedOptionInputs = optionInputs.map((input: any) => (input.option === optionName ? { ...input, value: newValue } : input));
const updatedChallenges = settings.challenges.map((ch: any, idx: number) => (idx === index ? { ...ch, optionInputs: updatedOptionInputs } : ch));
const updatedOptions = { ...options, [optionName]: newValue };
const updatedChallenges = settings.challenges.map((ch: any, idx: number) => (idx === index ? { ...ch, options: updatedOptions } : ch));
setSubmitStore({ settings: { ...settings, challenges: updatedChallenges } });
};
const handleExcludeChange = (type: 'role' | 'post' | 'reply' | 'vote', value: string | boolean) => {
const updatedExclude = { ...exclude[0] }; // Clone the first exclude object
if (type === 'role') {
if (typeof value === 'string') {
const roleIndex = updatedExclude.role.indexOf(value);
if (roleIndex > -1) {
updatedExclude.role.splice(roleIndex, 1); // Remove role
} else {
updatedExclude.role.push(value); // Add role
}
}
} else {
// Handle post, reply, vote
updatedExclude[type] = value;
}
const updatedChallenges = [...settings.challenges];
updatedChallenges[index] = { ...updatedChallenges[index], exclude: [updatedExclude] };
const handleExcludeChange = (type: keyof Exclude, value: string | boolean | number | string[] | number[]) => {
const updatedExclude = { ...exclude[0], [type]: value };
const updatedChallenges = settings.challenges.map((ch: any, idx: number) => (idx === index ? { ...ch, exclude: [updatedExclude] } : ch));
setSubmitStore({ settings: { ...settings, challenges: updatedChallenges } });
};
const handleExcludeAddress = (value: string) => {
// Split the input by commas, trim spaces, and filter out empty strings
const addresses = value
.split(',')
.map((addr) => addr.trim())
.filter((addr) => addr !== '');
handleExcludeChange('address', addresses);
};
return (
<div className={showSettings ? styles.visible : styles.hidden}>
<div className={styles.challengeDescription}>{getDefaultChallengeDescription(name)}</div>
{challenge?.optionInputs?.map((inputOption: OptionInput) => (
<div key={inputOption.option} className={styles.challengeOption}>
{inputOption.label}
<div className={styles.challengeOptionDescription}>{inputOption.description}</div>
{challengeSettings.map((setting) => (
<div key={setting?.option} className={styles.challengeOption}>
<div className={styles.challengeOptionLabel}>{setting?.label}</div>
<div className={styles.challengeOptionDescription}>{setting?.description}</div>
<input
type='text'
value={inputOption.value || inputOption.default || ''}
placeholder={inputOption.placeholder || ''}
onChange={(e) => handleOptionChange(inputOption.option, e.target.value)}
required={inputOption.required || false}
value={options && (options[setting?.option] || setting?.default || '')}
placeholder={setting?.placeholder || ''}
onChange={(e) => handleOptionChange(setting?.option, e.target.value)}
required={setting?.required || false}
/>
</div>
))}
<div className={styles.challengeDescription}>Exclude from challenge</div>
<div className={styles.challengeOption}>
Users
<div className={styles.challengeOptionDescription}>Exclude specific users by their addresses, separated by a comma</div>
<input
type='text'
placeholder='address1.eth, address2.eth, address3.eth'
value={exclude?.address?.join(', ')}
onChange={(e) => handleExcludeAddress(e.target.value)}
/>
</div>
<div className={styles.challengeOption}>
Users with Karma
<div className={styles.challengeOptionDescription}>Minimum post karma required:</div>
<input type='number' placeholder='3' value={exclude?.postScore || undefined} onChange={(e) => handleExcludeChange('postScore', e.target.value)} />
<div className={styles.challengeOptionDescription}>Minimum comment karma required:</div>
<input type='number' placeholder='3' value={exclude?.postReply || undefined} onChange={(e) => handleExcludeChange('postReply', e.target.value)} />
</div>
<div className={styles.challengeOption}>
Users by account age
<div className={styles.challengeOptionDescription}>Minimum account age in Unix Timestamp (seconds):</div>
<input
type='number'
placeholder='604800'
value={exclude?.firstCommentTimestamp || undefined}
onChange={(e) => handleExcludeChange('firstCommentTimestamp', e.target.value)}
/>
</div>
<div className={styles.challengeOption}>
Moderators
<div className={styles.challengeOptionDescription}>Exclude a specific moderator role</div>
<div>
<label>
<input type='checkbox' checked={exclude[0]?.role.includes('moderator')} onChange={() => handleExcludeChange('role', 'moderator')} />
exclude moderators
</label>
</div>
<div>
<label>
<input type='checkbox' checked={exclude[0]?.role.includes('admin')} onChange={() => handleExcludeChange('role', 'admin')} />
exclude admins
</label>
</div>
<div>
<label>
<input type='checkbox' checked={exclude[0]?.role.includes('owner')} onChange={() => handleExcludeChange('role', 'owner')} />
exclude owners
</label>
</div>
{rolesToExclude.map((role) => (
<div key={role}>
<label>
<input type='checkbox' checked={exclude[0]?.role.includes(role)} onChange={() => handleExcludeChange('role', role)} />
exclude {role}
</label>
</div>
))}
</div>
<div className={styles.challengeOption}>
Actions
<div className={styles.challengeOptionDescription}>Exclude a specific user action</div>
<div>
<label>
<input type='checkbox' checked={exclude[0]?.post} onChange={(e) => handleExcludeChange('post', e.target.checked)} />
exclude posts
</label>
</div>
<div>
<label>
<input type='checkbox' checked={exclude[1]?.post} onChange={(e) => handleExcludeChange('reply', e.target.checked)} />
exclude replies
</label>
</div>
<div>
<label>
<input type='checkbox' checked={exclude[2]?.post} onChange={(e) => handleExcludeChange('vote', e.target.checked)} />
exclude votes
</label>
</div>
{actionsToExclude.map((action) => (
<div key={action}>
<label>
<input type='checkbox' checked={exclude[0]?.[action]} onChange={(e) => handleExcludeChange(action, e.target.checked)} />
exclude {action}
</label>
</div>
))}
</div>
<div className={styles.challengeOption}>
Rate Limit
<div className={styles.challengeOptionDescription}>Number of free user actions per hour:</div>
<input type='number' placeholder='2' value={exclude?.rateLimit || undefined} onChange={(e) => handleExcludeChange('rateLimit', e.target.value)} />
<label>
<input type='checkbox' checked={exclude?.rateLimitChallengeSuccess} onChange={(e) => handleExcludeChange('rateLimitChallengeSuccess', e.target.checked)} />
only rate limit after a challenge is successfully completed
</label>
</div>
</div>
);
@@ -419,9 +427,11 @@ const Challenges = () => {
};
const handleAddChallenge = () => {
const defaultChallenge = 'captcha-canvas-v3';
const options = getDefaultChallengeOptions(defaultChallenge);
const newChallenge = {
name: 'captcha-canvas-v3',
optionInputs: getDefaultOptionInputs('captcha-canvas-v3'),
name: defaultChallenge,
options,
exclude: getDefaultExclude(),
};
const updatedChallenges = [...(settings?.challenges || []), newChallenge];
@@ -437,7 +447,7 @@ const Challenges = () => {
const handleChallengeTypeChange = (index: number, newType: string) => {
const updatedChallenges = [...challenges];
updatedChallenges[index] = { ...updatedChallenges[index], name: newType, optionInputs: getDefaultOptionInputs(newType) };
updatedChallenges[index] = { ...updatedChallenges[index], name: newType, options: getDefaultChallengeOptions(newType) };
setSubmitStore({ settings: { ...settings, challenges: updatedChallenges } });
};
@@ -473,13 +483,13 @@ const Challenges = () => {
);
};
const FullSettings = () => {
const JSONSettings = () => {
const { title, description, address, suggested, rules, roles, settings, subplebbitAddress, setSubmitStore } = useSubplebbitSettingsStore();
const [text, setText] = useState('');
useEffect(() => {
const fullSettings = JSON.stringify({ title, description, address, suggested, rules, roles, settings, subplebbitAddress }, null, 2);
setText(fullSettings);
const JSONSettings = JSON.stringify({ title, description, address, suggested, rules, roles, settings, subplebbitAddress }, null, 2);
setText(JSONSettings);
}, [title, description, address, suggested, rules, roles, settings, subplebbitAddress]);
const handleChange = (newText: string) => {
@@ -494,9 +504,9 @@ const FullSettings = () => {
return (
<div className={styles.box}>
<div className={styles.boxTitle}>full settings data</div>
<div className={styles.boxTitle}>JSON Settings</div>
<div className={styles.boxSubtitle}>quickly copy or paste the community settings</div>
<div className={`${styles.boxInput} ${styles.fullSettings}`}>
<div className={`${styles.boxInput} ${styles.JSONSettings}`}>
<textarea onChange={(e) => handleChange(e.target.value)} autoCorrect='off' autoComplete='off' spellCheck='false' value={text} />
</div>
</div>
@@ -570,7 +580,7 @@ const SubplebbitSettings = () => {
<Moderators />
{/* subplebbit.settings is private, only shows to the sub owner */}
{settings?.challenges && <Challenges />}
<FullSettings />
<JSONSettings />
<div className={styles.saveOptions}>
<button disabled={!isElectron || !isAdmin || showLoading} onClick={saveSubplebbit}>
{t('save_options')}

View File

@@ -20,7 +20,7 @@
}
.subplebbit {
margin-bottom: 10px;
padding-bottom: 1px;
height: auto;
overflow: hidden;
}
@@ -38,7 +38,7 @@
.arrowWrapper {
padding-left: 11px;
padding-top: 2px;
padding-top: 1px;
}
.arrowCommon {
@@ -167,14 +167,14 @@
.subplebbitPreferences {
padding: 0 1px;
line-height: 1.6em;
text-transform: lowercase;
margin-top: 1px;
margin-left: 28px;
}
.subplebbitPreferences a {
color: var(--gray-contrast);
font-weight: bold;
margin-right: 5px;
margin-left: 1px;
}
.subplebbitPreferences a:hover {
@@ -182,20 +182,8 @@
cursor: pointer;
}
.onlineStatus {
margin-right: 5px;
}
.green {
color: var(--green);
}
.red {
color: var(--red);
}
.roleLabel {
padding-right: 2px;
.label {
padding: 0 1px;
}
.subplebbitsTabs {

View File

@@ -5,7 +5,7 @@ import { Subplebbit as SubplebbitType, useAccount, useAccountSubplebbits, useSub
import styles from './subplebbits.module.css';
import Sidebar from '../../components/sidebar';
import SubscribeButton from '../../components/subscribe-button';
import { getFormattedTimeDuration, getFormattedTimeAgo } from '../../lib/utils/time-utils';
import { getFormattedTimeDuration } from '../../lib/utils/time-utils';
import {
isSubplebbitsView,
isSubplebbitsSubscriberView,
@@ -13,12 +13,11 @@ import {
isSubplebbitsAdminView,
isSubplebbitsOwnerView,
isSubplebbitsVoteView,
isSubplebbitsVotePassedView,
isSubplebbitsVoteRejectedView,
isSubplebbitsVotePassingView,
isSubplebbitsVoteRejectingView,
} from '../../lib/utils/view-utils';
import { useDefaultSubplebbitAddresses } from '../../lib/utils/addresses-utils';
import { RoleLabel } from '../../components/post/label/label';
const isMobile = window.innerWidth <= 768;
import { OfflineLabel, RoleLabel } from '../../components/post/label/label';
interface SubplebbitProps {
index?: number;
@@ -58,8 +57,8 @@ const VoteTabs = () => {
const { t } = useTranslation();
const location = useLocation();
const isInSubplebbitsVoteView = isSubplebbitsVoteView(location.pathname);
const isInSubplebbitsVotePassedView = isSubplebbitsVotePassedView(location.pathname);
const isInSubplebbitsVoteRejectedView = isSubplebbitsVoteRejectedView(location.pathname);
const isInSubplebbitsVotePassingView = isSubplebbitsVotePassingView(location.pathname);
const isInSubplebbitsVoteRejectingView = isSubplebbitsVoteRejectingView(location.pathname);
return (
<div className={styles.subplebbitsTabs}>
@@ -67,12 +66,12 @@ const VoteTabs = () => {
{t('all')}
</Link>
<span className={styles.separator}>|</span>
<Link to='/communities/vote/passed' className={isInSubplebbitsVotePassedView ? styles.selected : styles.choice} onClick={(e) => e.preventDefault()}>
{t('passed')}
<Link to='/communities/vote/passing' className={isInSubplebbitsVotePassingView ? styles.selected : styles.choice} onClick={(e) => e.preventDefault()}>
{t('passing')}
</Link>
<span className={styles.separator}>|</span>
<Link to='/communities/vote/rejected' className={isInSubplebbitsVoteRejectedView ? styles.selected : styles.choice} onClick={(e) => e.preventDefault()}>
{t('rejected')}
<Link to='/communities/vote/rejecting' className={isInSubplebbitsVoteRejectingView ? styles.selected : styles.choice} onClick={(e) => e.preventDefault()}>
{t('rejecting')}
</Link>
</div>
);
@@ -106,7 +105,7 @@ const Subplebbit = ({ subplebbit }: SubplebbitProps) => {
const { t } = useTranslation();
const { address, createdAt, description, roles, shortAddress, settings, suggested, title, updatedAt } = subplebbit || {};
const [showDescription, setShowDescription] = useState(isMobile ? false : true);
const [showDescription, setShowDescription] = useState(false);
const buttonType = showDescription ? 'closeButton' : 'textButton';
const toggleExpanded = () => setShowDescription(!showDescription);
@@ -122,11 +121,8 @@ const Subplebbit = ({ subplebbit }: SubplebbitProps) => {
const downvoteCount = 0;
const postScore = upvoteCount === 0 && downvoteCount === 0 ? '•' : upvoteCount - downvoteCount || '•';
const { allActiveUserCount } = useSubplebbitStats({ subplebbitAddress: address });
const isOnline = updatedAt && updatedAt > Date.now() / 1000 - 60 * 30;
const { allActiveUserCount, hourActiveUserCount } = useSubplebbitStats({ subplebbitAddress: address });
const onlineNotice = t('users_online', { count: hourActiveUserCount });
const offlineNotice = updatedAt && t('posts_last_synced', { dateAgo: getFormattedTimeAgo(updatedAt) });
const onlineStatus = isOnline ? onlineNotice : offlineNotice;
useEffect(() => {
document.title = `${t('communities')} - seedit`;
@@ -169,15 +165,13 @@ const Subplebbit = ({ subplebbit }: SubplebbitProps) => {
<span>
{t('members_count', { count: allActiveUserCount })}, {t('community_for', { date: getFormattedTimeDuration(createdAt) })}
<div className={styles.subplebbitPreferences}>
{updatedAt && !isOnline && <OfflineLabel />}
{(userRole || isUserOwner) && (
<span className={styles.roleLabel}>
<span className={styles.label}>
<RoleLabel role={userRole || 'owner'} />
</span>
)}
<Link to={`/p/${address}/settings`}>{t('settings')}</Link>
<span className={styles.onlineLine}>
<span>{onlineStatus}</span>
</span>
</div>
</span>
</div>

View File

@@ -2598,58 +2598,6 @@
uuid "9.0.0"
viem "1.5.2"
"@plebbit/plebbit-js@https://github.com/plebbit/plebbit-js.git#d081a9f4f1a086b5fccee5e9f332e113f61a90f1":
version "0.0.3"
resolved "https://github.com/plebbit/plebbit-js.git#d081a9f4f1a086b5fccee5e9f332e113f61a90f1"
dependencies:
"@ensdomains/eth-ens-namehash" "2.0.15"
"@keyv/sqlite" "3.6.2"
"@plebbit/plebbit-logger" "github:plebbit/plebbit-logger#9525ca9539479918931c3b334e8d490d1c87e507"
"@plebbit/proper-lockfile" "github:plebbit/node-proper-lockfile#7fd6332117340c1d3d98dd0afee2d31cc06f72b8"
"@types/node-fetch" "2.6.2"
"@types/proper-lockfile" "4.1.2"
"@types/uuid" "8.3.4"
assert "2.0.0"
async-wait-until "2.0.12"
better-sqlite3 "9.2.0"
buffer "6.0.3"
captcha-canvas "3.2.1"
cbor "9.0.1"
debounce "1.2.1"
err-code "3.0.1"
ethers "6.7.0"
file-type "16.5.4"
form-data "4.0.0"
hpagent "1.2.0"
ipfs-http-client "56.0.3"
ipfs-only-hash "4.0.0"
is-ipfs "6.0.2"
jose "4.11.0"
js-sha256 "0.9.0"
keyv "4.5.4"
knex "3.0.1"
libp2p-crypto "0.21.2"
limiter "2.1.0"
localforage "1.10.0"
lodash-es "4.17.21"
lru-cache "7.18.3"
open-graph-scraper "4.11.1"
p-limit "3.1.0"
p-timeout "4.1.0"
peer-id "0.16.0"
probe-image-size "^7.2.3"
retry "0.13.1"
rpc-websockets "7.6.0"
safe-stable-stringify "2.4.1"
sha1-uint8array "0.10.3"
skia-canvas "1.0.0"
sqlite3 "5.1.6"
tiny-typed-emitter "2.1.0"
tinycache "1.1.2"
ts-custom-error "3.3.1"
uuid "9.0.0"
viem "1.5.2"
"@plebbit/plebbit-logger@github:plebbit/plebbit-logger#9525ca9539479918931c3b334e8d490d1c87e507":
version "0.0.1"
uid "9525ca9539479918931c3b334e8d490d1c87e507"
@@ -2663,11 +2611,11 @@
dependencies:
debug "4.3.3"
"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#57f8891697134e8aa65f286ffd8bdbda9a6bd512":
"@plebbit/plebbit-react-hooks@https://github.com/plebbit/plebbit-react-hooks.git#6b614e0bf6c304de7b184564198a0ce7b98bca1d":
version "0.0.1"
resolved "https://github.com/plebbit/plebbit-react-hooks.git#57f8891697134e8aa65f286ffd8bdbda9a6bd512"
resolved "https://github.com/plebbit/plebbit-react-hooks.git#6b614e0bf6c304de7b184564198a0ce7b98bca1d"
dependencies:
"@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#d081a9f4f1a086b5fccee5e9f332e113f61a90f1"
"@plebbit/plebbit-js" "https://github.com/plebbit/plebbit-js.git#4a7ced7497fcbf863e9457b7e83a7bb2889bd518"
"@plebbit/plebbit-logger" "https://github.com/plebbit/plebbit-logger.git"
assert "2.0.0"
ethers "5.6.9"
@@ -13045,7 +12993,7 @@ pretty-format@^29.0.0, pretty-format@^29.7.0:
ansi-styles "^5.0.0"
react-is "^18.0.0"
probe-image-size@7.2.3, probe-image-size@^7.2.3:
probe-image-size@7.2.3:
version "7.2.3"
resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-7.2.3.tgz#d49c64be540ec8edea538f6f585f65a9b3ab4309"
integrity sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==