mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
@@ -47,6 +47,10 @@
|
|||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mobileCatalogOptionsPadding {
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.desktopBoardButtons {
|
.desktopBoardButtons {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import useFeedResetStore from '../../stores/use-feed-reset-store';
|
|||||||
import useTimeFilter from '../../hooks/use-time-filter';
|
import useTimeFilter from '../../hooks/use-time-filter';
|
||||||
import CatalogFilters from '../../views/catalog/catalog-filters/';
|
import CatalogFilters from '../../views/catalog/catalog-filters/';
|
||||||
import styles from './board-buttons.module.css';
|
import styles from './board-buttons.module.css';
|
||||||
|
import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
|
||||||
|
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
||||||
|
|
||||||
interface BoardButtonsProps {
|
interface BoardButtonsProps {
|
||||||
address?: string | undefined;
|
address?: string | undefined;
|
||||||
@@ -100,6 +102,35 @@ const SortOptions = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ImageSizeOptions = () => {
|
||||||
|
const { imageSize, setImageSize } = useCatalogStyleStore();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span>Image Size:</span>
|
||||||
|
<select value={imageSize} onChange={(e) => setImageSize(e.target.value as 'Small' | 'Large')}>
|
||||||
|
<option value='Small'>Small</option>
|
||||||
|
<option value='Large'>Large</option>
|
||||||
|
</select>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ShowOPCommentOption = () => {
|
||||||
|
const { showOPComment, setShowOPComment } = useCatalogStyleStore();
|
||||||
|
const { showTextOnlyThreads } = useCatalogFiltersStore();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span>Show OP Comment:</span>
|
||||||
|
<select value={showOPComment ? 'On' : 'Off'} onChange={(e) => setShowOPComment(e.target.value === 'On')} disabled={showTextOnlyThreads}>
|
||||||
|
<option value='Off'>Off</option>
|
||||||
|
<option value='On'>On</option>
|
||||||
|
</select>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const TimeFilter = ({ isInAllView, isInCatalogView, isInSubscriptionsView, isTopbar = false }: BoardButtonsProps) => {
|
export const TimeFilter = ({ isInAllView, isInCatalogView, isInSubscriptionsView, isTopbar = false }: BoardButtonsProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -174,7 +205,12 @@ export const MobileBoardButtons = () => {
|
|||||||
<>
|
<>
|
||||||
<hr />
|
<hr />
|
||||||
<div className={styles.options}>
|
<div className={styles.options}>
|
||||||
<SortOptions /> <CatalogFilters />
|
<div>
|
||||||
|
<SortOptions /> <ImageSizeOptions />
|
||||||
|
</div>
|
||||||
|
<div className={styles.mobileCatalogOptionsPadding}>
|
||||||
|
<ShowOPCommentOption /> <CatalogFilters />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -227,7 +263,13 @@ export const DesktopBoardButtons = () => {
|
|||||||
)}
|
)}
|
||||||
[<RefreshButton />]
|
[<RefreshButton />]
|
||||||
<span className={styles.rightSideButtons}>
|
<span className={styles.rightSideButtons}>
|
||||||
{isInCatalogView && <SortOptions />}
|
{isInCatalogView && (
|
||||||
|
<>
|
||||||
|
<SortOptions />
|
||||||
|
<ImageSizeOptions />
|
||||||
|
<ShowOPCommentOption />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{(isInAllView || isInSubscriptionsView) && (
|
{(isInAllView || isInSubscriptionsView) && (
|
||||||
<TimeFilter isInAllView={isInAllView} isInCatalogView={isInCatalogView} isInSubscriptionsView={isInSubscriptionsView} />
|
<TimeFilter isInAllView={isInAllView} isInCatalogView={isInCatalogView} isInSubscriptionsView={isInSubscriptionsView} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -16,20 +16,24 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.large {
|
||||||
|
width: 270px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hiddenThumbnail {
|
.hiddenThumbnail {
|
||||||
width: 150px;
|
width: var(--maxWidth);
|
||||||
height: 150px;
|
height: var(--maxHeight);
|
||||||
background-color: var(--media-thumbnail-background-color);
|
background-color: var(--media-thumbnail-background-color);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spoilerThumbnail {
|
.spoilerThumbnail {
|
||||||
width: 150px;
|
width: var(--maxWidth);
|
||||||
height: 150px;
|
height: var(--maxHeight);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
color: white !important;
|
color: white !important;
|
||||||
@@ -80,8 +84,8 @@
|
|||||||
|
|
||||||
.post img, .post video, .post audio {
|
.post img, .post video, .post audio {
|
||||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.25);
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.25);
|
||||||
max-width: 150px;
|
max-width: var(--maxWidth);
|
||||||
max-height: 150px;
|
max-height: var(--maxHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
.meta {
|
.meta {
|
||||||
@@ -111,8 +115,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.loadingSkeleton {
|
.loadingSkeleton {
|
||||||
width: 150px;
|
width: var(--maxWidth);
|
||||||
height: 150px;
|
height: var(--maxHeight);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useFloating, offset, shift, size, autoUpdate, Placement } from '@floati
|
|||||||
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
||||||
import { getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
import { getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
||||||
import { isAllView } from '../../lib/utils/view-utils';
|
import { isAllView } from '../../lib/utils/view-utils';
|
||||||
|
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
||||||
import useEditCommentPrivileges from '../../hooks/use-author-privileges';
|
import useEditCommentPrivileges from '../../hooks/use-author-privileges';
|
||||||
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
|
||||||
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
|
||||||
@@ -50,7 +51,16 @@ export const CatalogPostMedia = ({ commentMediaInfo, isOutOfFeed, linkWidth, lin
|
|||||||
displayHeight = 'unset';
|
displayHeight = 'unset';
|
||||||
}
|
}
|
||||||
|
|
||||||
const thumbnailDimensions = { '--width': displayWidth, '--height': displayHeight } as React.CSSProperties;
|
const { imageSize } = useCatalogStyleStore();
|
||||||
|
const maxWidth = imageSize === 'Large' ? '250px' : '150px';
|
||||||
|
const maxHeight = imageSize === 'Large' ? '250px' : '150px';
|
||||||
|
|
||||||
|
const CSSProperties = {
|
||||||
|
'--width': displayWidth,
|
||||||
|
'--height': displayHeight,
|
||||||
|
'--maxWidth': maxWidth,
|
||||||
|
'--maxHeight': maxHeight,
|
||||||
|
} as React.CSSProperties;
|
||||||
|
|
||||||
let thumbnailComponent: React.ReactNode = null;
|
let thumbnailComponent: React.ReactNode = null;
|
||||||
|
|
||||||
@@ -74,7 +84,7 @@ export const CatalogPostMedia = ({ commentMediaInfo, isOutOfFeed, linkWidth, lin
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={hasError ? '' : styles.mediaWrapper} style={thumbnailDimensions}>
|
<div className={hasError ? '' : styles.mediaWrapper} style={CSSProperties}>
|
||||||
{!isLoaded && !hasError && type !== 'video' && type !== 'audio' && <span className={styles.loadingSkeleton} />}
|
{!isLoaded && !hasError && type !== 'video' && type !== 'audio' && <span className={styles.loadingSkeleton} />}
|
||||||
{hasError ? <img className={styles.fileDeleted} src='/assets/filedeleted-res.gif' alt='' /> : thumbnailComponent}
|
{hasError ? <img className={styles.fileDeleted} src='/assets/filedeleted-res.gif' alt='' /> : thumbnailComponent}
|
||||||
</div>
|
</div>
|
||||||
@@ -179,9 +189,17 @@ const CatalogPost = ({ post }: { post: Comment }) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { imageSize, showOPComment } = useCatalogStyleStore();
|
||||||
|
const maxWidth = imageSize === 'Large' ? '250px' : '150px';
|
||||||
|
const maxHeight = imageSize === 'Large' ? '250px' : '150px';
|
||||||
|
const CSSProperties = {
|
||||||
|
'--maxWidth': maxWidth,
|
||||||
|
'--maxHeight': maxHeight,
|
||||||
|
} as React.CSSProperties;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.post}>
|
<div className={`${styles.post} ${imageSize === 'Large' ? styles.large : ''}`} style={CSSProperties}>
|
||||||
<div onMouseOver={() => setHoveredCid(isDescription ? 'd' : isRules ? 'r' : cid)} onMouseLeave={() => setHoveredCid(null)}>
|
<div onMouseOver={() => setHoveredCid(isDescription ? 'd' : isRules ? 'r' : cid)} onMouseLeave={() => setHoveredCid(null)}>
|
||||||
{hidden ? (
|
{hidden ? (
|
||||||
<Link to={postLink}>
|
<Link to={postLink}>
|
||||||
@@ -226,7 +244,7 @@ const CatalogPost = ({ post }: { post: Comment }) => {
|
|||||||
<PostMenuDesktop post={post} />
|
<PostMenuDesktop post={post} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{hasThumbnail ? postContent : <Link to={postLink}>{postContent}</Link>}
|
{showOPComment && (hasThumbnail ? postContent : <Link to={postLink}>{postContent}</Link>)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{hoveredCid === cid &&
|
{hoveredCid === cid &&
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react';
|
|||||||
import { useLocation, useParams } from 'react-router-dom';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import Draggable from 'react-draggable';
|
import Draggable from 'react-draggable';
|
||||||
import { setAccount, useAccount, useAuthorAddress, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
import { setAccount, useAccount, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||||
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
||||||
import { getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
import { getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
||||||
import { isValidURL } from '../../lib/utils/url-utils';
|
import { isValidURL } from '../../lib/utils/url-utils';
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
.cryptoAddressInput input[type="text"] {
|
.cryptoAddressInput input[type="text"] {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
|
width: 55%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.infoButton {
|
.infoButton {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from './plebbit-options';
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
.content {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settingTitle {
|
||||||
|
text-transform: capitalize;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryTitle {
|
||||||
|
text-transform: capitalize;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ipfsGatewaysSettings textarea {
|
||||||
|
height: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pubsubProvidersSettings textarea {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blockchainProvidersSettings textarea {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plebbitRpcSettingsInfo {
|
||||||
|
padding: 5px 20px 0 0;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plebbitRpcSettingsInfo ol {
|
||||||
|
padding-bottom: 5px;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saveOptions {
|
||||||
|
float: right;
|
||||||
|
margin-top: -27px;
|
||||||
|
margin-right: 10px;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content textarea {
|
||||||
|
display: block;
|
||||||
|
outline: none;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
margin: 0 2px 0 0;
|
||||||
|
padding: 2px 4px 3px;
|
||||||
|
width: 95%;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content input[type="text"] {
|
||||||
|
outline: none;
|
||||||
|
width: 55%;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settingTip {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.85em;
|
||||||
|
margin: 5px 0 0 0;
|
||||||
|
padding-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plebbitRPCSettings button {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
import { RefObject, useRef, useState } from 'react';
|
||||||
|
import { setAccount, useAccount, usePlebbitRpcSettings } from '@plebbit/plebbit-react-hooks';
|
||||||
|
import styles from './plebbit-options.module.css';
|
||||||
|
|
||||||
|
interface SettingsProps {
|
||||||
|
ipfsGatewayUrlsRef?: RefObject<HTMLTextAreaElement>;
|
||||||
|
mediaIpfsGatewayUrlRef?: RefObject<HTMLInputElement>;
|
||||||
|
pubsubProvidersRef?: RefObject<HTMLTextAreaElement>;
|
||||||
|
ethRpcRef?: RefObject<HTMLTextAreaElement>;
|
||||||
|
solRpcRef?: RefObject<HTMLTextAreaElement>;
|
||||||
|
maticRpcRef?: RefObject<HTMLTextAreaElement>;
|
||||||
|
avaxRpcRef?: RefObject<HTMLTextAreaElement>;
|
||||||
|
plebbitRpcRef?: RefObject<HTMLInputElement>;
|
||||||
|
nodeDataPathRef?: RefObject<HTMLInputElement>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IPFSGatewaysSettings = ({ ipfsGatewayUrlsRef, mediaIpfsGatewayUrlRef }: SettingsProps) => {
|
||||||
|
const account = useAccount();
|
||||||
|
const { plebbitOptions, mediaIpfsGatewayUrl } = account || {};
|
||||||
|
const { ipfsGatewayUrls } = plebbitOptions || {};
|
||||||
|
const plebbitRpc = usePlebbitRpcSettings();
|
||||||
|
const isConnectedToRpc = plebbitRpc?.state === 'succeeded';
|
||||||
|
const ipfsGatewayUrlsDefaultValue = ipfsGatewayUrls?.join('\n');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.ipfsGatewaysSettings}>
|
||||||
|
<div className={styles.ipfsGatewaysSetting}>
|
||||||
|
<textarea
|
||||||
|
defaultValue={ipfsGatewayUrlsDefaultValue}
|
||||||
|
ref={ipfsGatewayUrlsRef}
|
||||||
|
disabled={isConnectedToRpc}
|
||||||
|
autoCorrect='off'
|
||||||
|
autoComplete='off'
|
||||||
|
spellCheck='false'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className={styles.settingTip}>NFT profile pics gateway</span>
|
||||||
|
<div>
|
||||||
|
<input type='text' defaultValue={mediaIpfsGatewayUrl} ref={mediaIpfsGatewayUrlRef} disabled={isConnectedToRpc} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PubsubProvidersSettings = ({ pubsubProvidersRef }: SettingsProps) => {
|
||||||
|
const account = useAccount();
|
||||||
|
const { plebbitOptions } = account || {};
|
||||||
|
const { pubsubHttpClientsOptions } = plebbitOptions || {};
|
||||||
|
const plebbitRpc = usePlebbitRpcSettings();
|
||||||
|
const isConnectedToRpc = plebbitRpc?.state === 'succeeded';
|
||||||
|
const pubsubProvidersDefaultValue = pubsubHttpClientsOptions?.join('\n');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.pubsubProvidersSettings}>
|
||||||
|
<textarea defaultValue={pubsubProvidersDefaultValue} ref={pubsubProvidersRef} disabled={isConnectedToRpc} autoCorrect='off' autoComplete='off' spellCheck='false' />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const BlockchainProvidersSettings = ({ ethRpcRef, solRpcRef, maticRpcRef, avaxRpcRef }: SettingsProps) => {
|
||||||
|
const account = useAccount();
|
||||||
|
const { plebbitOptions } = account || {};
|
||||||
|
const { chainProviders } = plebbitOptions || {};
|
||||||
|
const ethRpcDefaultValue = chainProviders?.['eth']?.urls.join('\n');
|
||||||
|
const solRpcDefaultValue = chainProviders?.['sol']?.urls.join('\n');
|
||||||
|
const maticRpcDefaultValue = chainProviders?.['matic']?.urls.join('\n');
|
||||||
|
const avaxRpcDefaultValue = chainProviders?.['avax']?.urls.join('\n');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.blockchainProvidersSettings}>
|
||||||
|
<span className={styles.settingTip}>Ethereum RPC, for .eth addresses</span>
|
||||||
|
<div>
|
||||||
|
<textarea defaultValue={ethRpcDefaultValue} ref={ethRpcRef} autoCorrect='off' autoComplete='off' spellCheck='false' />
|
||||||
|
</div>
|
||||||
|
<span className={styles.settingTip}>Solana RPC, for .sol addresses</span>
|
||||||
|
<div>
|
||||||
|
<textarea defaultValue={solRpcDefaultValue} ref={solRpcRef} autoCorrect='off' autoComplete='off' spellCheck='false' />
|
||||||
|
</div>
|
||||||
|
<span className={styles.settingTip}>Polygon RPC, for nft profile pics</span>
|
||||||
|
<div>
|
||||||
|
<textarea defaultValue={maticRpcDefaultValue} ref={maticRpcRef} autoCorrect='off' autoComplete='off' spellCheck='false' />
|
||||||
|
</div>
|
||||||
|
<span className={styles.settingTip}>Avalanche RPC</span>
|
||||||
|
<div>
|
||||||
|
<textarea defaultValue={avaxRpcDefaultValue} ref={avaxRpcRef} autoCorrect='off' autoComplete='off' spellCheck='false' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PlebbitRPCSettings = ({ plebbitRpcRef }: SettingsProps) => {
|
||||||
|
const [showInfo, setShowInfo] = useState(false);
|
||||||
|
const account = useAccount();
|
||||||
|
const { plebbitOptions } = account || {};
|
||||||
|
const { plebbitRpcClientsOptions } = plebbitOptions || {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.plebbitRPCSettings}>
|
||||||
|
<div>
|
||||||
|
<input type='text' defaultValue={plebbitRpcClientsOptions} ref={plebbitRpcRef} />
|
||||||
|
<button onClick={() => setShowInfo(!showInfo)}>{showInfo ? 'X' : '?'}</button>
|
||||||
|
</div>
|
||||||
|
{showInfo && (
|
||||||
|
<div className={styles.plebbitRpcSettingsInfo}>
|
||||||
|
use a plebbit full node locally, or remotely with SSL
|
||||||
|
<br />
|
||||||
|
<ol>
|
||||||
|
<li>get secret auth key from the node</li>
|
||||||
|
<li>get IP address and port used by the node</li>
|
||||||
|
<li>
|
||||||
|
enter: <code>{`ws://<IP>:<port>/<secretAuthKey>`}</code>
|
||||||
|
</li>
|
||||||
|
<li>click save to connect</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const NodeDataPathSettings = ({ nodeDataPathRef }: SettingsProps) => {
|
||||||
|
const plebbitRpc = usePlebbitRpcSettings();
|
||||||
|
const { plebbitRpcSettings } = plebbitRpc || {};
|
||||||
|
const isConnectedToRpc = plebbitRpc?.state === 'succeeded';
|
||||||
|
const path = plebbitRpcSettings?.plebbitOptions?.dataPath || '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.nodeDataPathSettings}>
|
||||||
|
<div>
|
||||||
|
<input type='text' defaultValue={path} disabled={!isConnectedToRpc} ref={nodeDataPathRef} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PlebbitOptions = () => {
|
||||||
|
const account = useAccount();
|
||||||
|
const { plebbitOptions } = account || {};
|
||||||
|
|
||||||
|
const ipfsGatewayUrlsRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
const mediaIpfsGatewayUrlRef = useRef<HTMLInputElement>(null);
|
||||||
|
const pubsubProvidersRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
const ethRpcRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
const solRpcRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
const maticRpcRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
const avaxRpcRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
const plebbitRpcRef = useRef<HTMLInputElement>(null);
|
||||||
|
const nodeDataPathRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const handleSave = async () => {
|
||||||
|
const ipfsGatewayUrls = ipfsGatewayUrlsRef.current?.value.split('\n').map((url) => url.trim());
|
||||||
|
const mediaIpfsGatewayUrl = mediaIpfsGatewayUrlRef.current?.value.trim();
|
||||||
|
const pubsubHttpClientsOptions = pubsubProvidersRef.current?.value.split('\n').map((url) => url.trim());
|
||||||
|
const ethRpcUrls = ethRpcRef.current?.value.split('\n').map((url) => url.trim());
|
||||||
|
const solRpcUrls = solRpcRef.current?.value.split('\n').map((url) => url.trim());
|
||||||
|
const maticRpcUrls = maticRpcRef.current?.value.split('\n').map((url) => url.trim());
|
||||||
|
const avaxRpcUrls = avaxRpcRef.current?.value.split('\n').map((url) => url.trim());
|
||||||
|
const plebbitRpcClientsOptions = plebbitRpcRef.current?.value.trim();
|
||||||
|
const dataPath = nodeDataPathRef.current?.value.trim();
|
||||||
|
|
||||||
|
const chainProviders = {
|
||||||
|
eth: {
|
||||||
|
urls: ethRpcUrls,
|
||||||
|
chainId: 1,
|
||||||
|
},
|
||||||
|
sol: {
|
||||||
|
urls: solRpcUrls,
|
||||||
|
chainId: 1,
|
||||||
|
},
|
||||||
|
matic: {
|
||||||
|
urls: maticRpcUrls,
|
||||||
|
chainId: 137,
|
||||||
|
},
|
||||||
|
avax: {
|
||||||
|
urls: avaxRpcUrls,
|
||||||
|
chainId: 43114,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await setAccount({
|
||||||
|
...account,
|
||||||
|
mediaIpfsGatewayUrl,
|
||||||
|
plebbitOptions: {
|
||||||
|
...plebbitOptions,
|
||||||
|
ipfsGatewayUrls,
|
||||||
|
pubsubHttpClientsOptions,
|
||||||
|
chainProviders,
|
||||||
|
plebbitRpcClientsOptions,
|
||||||
|
dataPath,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
alert('Options saved.');
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
alert('Error saving options: ' + e.message);
|
||||||
|
console.log(e);
|
||||||
|
} else {
|
||||||
|
alert('Error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.content}>
|
||||||
|
<button className={styles.saveOptions} onClick={handleSave}>
|
||||||
|
save options
|
||||||
|
</button>
|
||||||
|
<div className={styles.category}>
|
||||||
|
<span className={styles.categoryTitle}>IPFS gateways:</span>
|
||||||
|
<span className={styles.categorySettings}>
|
||||||
|
<IPFSGatewaysSettings ipfsGatewayUrlsRef={ipfsGatewayUrlsRef} mediaIpfsGatewayUrlRef={mediaIpfsGatewayUrlRef} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.category}>
|
||||||
|
<span className={styles.categoryTitle}>pubsub providers:</span>
|
||||||
|
<span className={styles.categorySettings}>
|
||||||
|
<PubsubProvidersSettings pubsubProvidersRef={pubsubProvidersRef} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.category}>
|
||||||
|
<span className={styles.categoryTitle}>blockchain providers:</span>
|
||||||
|
<span className={styles.categorySettings}>
|
||||||
|
<BlockchainProvidersSettings ethRpcRef={ethRpcRef} solRpcRef={solRpcRef} maticRpcRef={maticRpcRef} avaxRpcRef={avaxRpcRef} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.category}>
|
||||||
|
<span className={styles.categoryTitle}>node rpc:</span>
|
||||||
|
<span className={styles.categorySettings}>
|
||||||
|
<PlebbitRPCSettings plebbitRpcRef={plebbitRpcRef} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.category}>
|
||||||
|
<span className={styles.categoryTitle}>node data path:</span>
|
||||||
|
<span className={styles.categorySettings}>
|
||||||
|
<NodeDataPathSettings nodeDataPathRef={nodeDataPathRef} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PlebbitOptions;
|
||||||
@@ -7,6 +7,7 @@ import BlockedAddressesSetting from './blocked-addresses-setting';
|
|||||||
import CryptoAddressSetting from './crypto-address-setting';
|
import CryptoAddressSetting from './crypto-address-setting';
|
||||||
import CryptoWalletsSetting from './crypto-wallets-setting';
|
import CryptoWalletsSetting from './crypto-wallets-setting';
|
||||||
import InterfaceSettings from './interface-settings';
|
import InterfaceSettings from './interface-settings';
|
||||||
|
import PlebbitOptions from './plebbit-options';
|
||||||
|
|
||||||
const SettingsModal = () => {
|
const SettingsModal = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -21,6 +22,7 @@ const SettingsModal = () => {
|
|||||||
const [showCryptoAddressSetting, setShowCryptoAddressSetting] = useState(false);
|
const [showCryptoAddressSetting, setShowCryptoAddressSetting] = useState(false);
|
||||||
const [showCryptoWalletSettings, setShowCryptoWalletSettings] = useState(false);
|
const [showCryptoWalletSettings, setShowCryptoWalletSettings] = useState(false);
|
||||||
const [showBlockedAddressesSetting, setShowBlockedAddressesSetting] = useState(false);
|
const [showBlockedAddressesSetting, setShowBlockedAddressesSetting] = useState(false);
|
||||||
|
const [showPlebbitOptionsSettings, setShowPlebbitOptionsSettings] = useState(false);
|
||||||
const [expandAll, setExpandAll] = useState(false);
|
const [expandAll, setExpandAll] = useState(false);
|
||||||
|
|
||||||
const handleExpandAll = () => {
|
const handleExpandAll = () => {
|
||||||
@@ -31,6 +33,7 @@ const SettingsModal = () => {
|
|||||||
setShowCryptoAddressSetting(newExpandState);
|
setShowCryptoAddressSetting(newExpandState);
|
||||||
setShowCryptoWalletSettings(newExpandState);
|
setShowCryptoWalletSettings(newExpandState);
|
||||||
setShowBlockedAddressesSetting(newExpandState);
|
setShowBlockedAddressesSetting(newExpandState);
|
||||||
|
setShowPlebbitOptionsSettings(newExpandState);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -79,6 +82,13 @@ const SettingsModal = () => {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{showBlockedAddressesSetting && <BlockedAddressesSetting />}
|
{showBlockedAddressesSetting && <BlockedAddressesSetting />}
|
||||||
|
<div className={`${styles.setting} ${styles.category}`}>
|
||||||
|
<label onClick={() => setShowPlebbitOptionsSettings(!showPlebbitOptionsSettings)}>
|
||||||
|
<span className={showPlebbitOptionsSettings ? styles.hideButton : styles.showButton} />
|
||||||
|
{t('plebbit_options')}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{showPlebbitOptionsSettings && <PlebbitOptions />}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const useTheme = (): [string, (theme: string) => void] => {
|
|||||||
return 'yotsuba-b';
|
return 'yotsuba-b';
|
||||||
}
|
}
|
||||||
return 'yotsuba';
|
return 'yotsuba';
|
||||||
}, [location.pathname, subplebbitAddress, subplebbits, getTheme]);
|
}, [subplebbitAddress, subplebbits, getTheme, isInAllView, isInHomeView, isInNotFoundView, isInSubscriptionsView]);
|
||||||
|
|
||||||
const [theme, setLocalTheme] = useState(initialTheme);
|
const [theme, setLocalTheme] = useState(initialTheme);
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ hr {
|
|||||||
text-decoration: var(--button-text-decoration-mobile);
|
text-decoration: var(--button-text-decoration-mobile);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
interface CatalogStyleStore {
|
||||||
|
imageSize: 'Small' | 'Large';
|
||||||
|
setImageSize: (size: 'Small' | 'Large') => void;
|
||||||
|
showOPComment: boolean;
|
||||||
|
setShowOPComment: (value: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useCatalogStyleStore = create<CatalogStyleStore>((set) => ({
|
||||||
|
imageSize: (localStorage.getItem('imageSize') as 'Small' | 'Large') || 'Small',
|
||||||
|
setImageSize: (size: 'Small' | 'Large') => {
|
||||||
|
set({ imageSize: size });
|
||||||
|
localStorage.setItem('imageSize', size);
|
||||||
|
},
|
||||||
|
showOPComment: localStorage.getItem('showOPComment') === 'false' ? false : true,
|
||||||
|
setShowOPComment: (value: boolean) => {
|
||||||
|
set({ showOPComment: value });
|
||||||
|
localStorage.setItem('showOPComment', value.toString());
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default useCatalogStyleStore;
|
||||||
@@ -18,6 +18,7 @@ import SettingsModal from '../../components/settings-modal';
|
|||||||
import styles from './catalog.module.css';
|
import styles from './catalog.module.css';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
|
||||||
|
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
|
||||||
|
|
||||||
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
const lastVirtuosoStates: { [key: string]: StateSnapshot } = {};
|
||||||
|
|
||||||
@@ -77,8 +78,6 @@ const useFeedRows = (columnCount: number, feed: any, isFeedLoaded: boolean, subp
|
|||||||
return rows;
|
return rows;
|
||||||
};
|
};
|
||||||
|
|
||||||
const columnWidth = 180;
|
|
||||||
|
|
||||||
const catalogFilter = (comment: Comment) => {
|
const catalogFilter = (comment: Comment) => {
|
||||||
const commentMediaInfo = getCommentMediaInfo(comment);
|
const commentMediaInfo = getCommentMediaInfo(comment);
|
||||||
const hasThumbnail = getHasThumbnail(commentMediaInfo, comment?.link);
|
const hasThumbnail = getHasThumbnail(commentMediaInfo, comment?.link);
|
||||||
@@ -121,6 +120,9 @@ const Catalog = () => {
|
|||||||
return [subplebbitAddress];
|
return [subplebbitAddress];
|
||||||
}, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbits, subscriptions, showAdultBoards, showGoreBoards]);
|
}, [isInAllView, isInSubscriptionsView, subplebbitAddress, defaultSubplebbits, subscriptions, showAdultBoards, showGoreBoards]);
|
||||||
|
|
||||||
|
const { imageSize } = useCatalogStyleStore();
|
||||||
|
const columnWidth = imageSize === 'Large' ? 270 : 180;
|
||||||
|
|
||||||
const columnCount = Math.floor(useWindowWidth() / columnWidth);
|
const columnCount = Math.floor(useWindowWidth() / columnWidth);
|
||||||
// postPerPage based on columnCount for optimized feed, dont change value after first render
|
// postPerPage based on columnCount for optimized feed, dont change value after first render
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
|||||||
Reference in New Issue
Block a user