mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
prettify
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useSubplebbit } from "@plebbit/plebbit-react-hooks";
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
|
||||
const BoardAvatar = ({ address }) => {
|
||||
const [avatarUrl, setAvatarUrl] = useState("assets/plebchan.png");
|
||||
const [avatarUrl, setAvatarUrl] = useState('assets/plebchan.png');
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress: address });
|
||||
|
||||
useEffect(() => {
|
||||
@@ -12,13 +11,7 @@ const BoardAvatar = ({ address }) => {
|
||||
}
|
||||
}, [subplebbit.suggested?.avatarUrl, subplebbit]);
|
||||
|
||||
return (
|
||||
<img
|
||||
className="board-avatar"
|
||||
alt="board avatar"
|
||||
src={avatarUrl}
|
||||
/>
|
||||
);
|
||||
return <img className='board-avatar' alt='board avatar' src={avatarUrl} />;
|
||||
};
|
||||
|
||||
export default BoardAvatar;
|
||||
@@ -1,20 +1,13 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { usePublishSubplebbitEdit } from "@plebbit/plebbit-react-hooks";
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { usePublishSubplebbitEdit } from '@plebbit/plebbit-react-hooks';
|
||||
import { StyledModal } from './styled/modals/ModerationModal.styled';
|
||||
import useError from "../hooks/useError";
|
||||
import useSuccess from "../hooks/useSuccess";
|
||||
import useError from '../hooks/useError';
|
||||
import useSuccess from '../hooks/useSuccess';
|
||||
import useGeneralStore from '../hooks/stores/useGeneralStore';
|
||||
|
||||
const BoardSettings = ({ subplebbit }) => {
|
||||
const {
|
||||
setCaptchaResponse,
|
||||
setChallengesArray,
|
||||
setIsCaptchaOpen,
|
||||
setResolveCaptchaPromise,
|
||||
selectedAddress,
|
||||
selectedStyle,
|
||||
} = useGeneralStore(state => state);
|
||||
const { setCaptchaResponse, setChallengesArray, setIsCaptchaOpen, setResolveCaptchaPromise, selectedAddress, selectedStyle } = useGeneralStore((state) => state);
|
||||
|
||||
const allowedSettings = {
|
||||
address: subplebbit.address,
|
||||
@@ -68,7 +61,6 @@ const BoardSettings = ({ subplebbit }) => {
|
||||
const [, setNewErrorMessage] = useError();
|
||||
const [, setNewSuccessMessage] = useSuccess();
|
||||
|
||||
|
||||
const getDifferences = (oldObj, newObj) => {
|
||||
let differences = {};
|
||||
|
||||
@@ -92,7 +84,6 @@ const BoardSettings = ({ subplebbit }) => {
|
||||
return differences;
|
||||
};
|
||||
|
||||
|
||||
const isInitialMount = useRef(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -102,17 +93,14 @@ const BoardSettings = ({ subplebbit }) => {
|
||||
}
|
||||
}, [subplebbit]);
|
||||
|
||||
|
||||
function validateSettings(updatedSettings, allowedSettings) {
|
||||
for (let key in updatedSettings) {
|
||||
if (!allowedSettings.hasOwnProperty(key) && !initialSettings.hasOwnProperty(key)) {
|
||||
throw new Error(`Unexpected setting: ${key}`);
|
||||
}
|
||||
|
||||
if (typeof updatedSettings[key] === 'object' && updatedSettings[key] !== null
|
||||
&& !Array.isArray(updatedSettings[key])) {
|
||||
if (typeof allowedSettings[key] !== 'object' || allowedSettings[key] === null
|
||||
|| Array.isArray(allowedSettings[key])) {
|
||||
if (typeof updatedSettings[key] === 'object' && updatedSettings[key] !== null && !Array.isArray(updatedSettings[key])) {
|
||||
if (typeof allowedSettings[key] !== 'object' || allowedSettings[key] === null || Array.isArray(allowedSettings[key])) {
|
||||
throw new Error(`Expected ${key} to be an object in allowedSettings`);
|
||||
}
|
||||
validateSettings(updatedSettings[key], allowedSettings[key]);
|
||||
@@ -120,32 +108,30 @@ const BoardSettings = ({ subplebbit }) => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const onChallenge = async (challenges, subplebbitEdit) => {
|
||||
let challengeAnswers = [];
|
||||
|
||||
try {
|
||||
challengeAnswers = await getChallengeAnswersFromUser(challenges)
|
||||
}
|
||||
catch (error) {
|
||||
setNewErrorMessage(error.message); console.log(error);
|
||||
challengeAnswers = await getChallengeAnswersFromUser(challenges);
|
||||
} catch (error) {
|
||||
setNewErrorMessage(error.message);
|
||||
console.log(error);
|
||||
}
|
||||
if (challengeAnswers) {
|
||||
await subplebbitEdit.publishChallengeAnswers(challengeAnswers)
|
||||
await subplebbitEdit.publishChallengeAnswers(challengeAnswers);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const onChallengeVerification = (challengeVerification) => {
|
||||
if (challengeVerification.challengeSuccess === true) {
|
||||
setNewSuccessMessage('Challenge Success'); console.log('challenge success', challengeVerification);
|
||||
setNewSuccessMessage('Challenge Success');
|
||||
console.log('challenge success', challengeVerification);
|
||||
} else if (challengeVerification.challengeSuccess === false) {
|
||||
setNewErrorMessage(`Challenge Failed, reason: ${challengeVerification.reason}. Errors: ${challengeVerification.errors}`);
|
||||
console.log('challenge failed', challengeVerification);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const getChallengeAnswersFromUser = async (challenges) => {
|
||||
setChallengesArray(challenges);
|
||||
|
||||
@@ -180,20 +166,18 @@ const BoardSettings = ({ subplebbit }) => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const [editSubplebbitOptions, setEditSubplebbitOptions] = useState({
|
||||
subplebbitAddress: selectedAddress,
|
||||
onChallenge,
|
||||
onChallengeVerification,
|
||||
onError: (error) => {
|
||||
setNewErrorMessage(error.message); console.log(error);
|
||||
}
|
||||
setNewErrorMessage(error.message);
|
||||
console.log(error);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const { publishSubplebbitEdit } = usePublishSubplebbitEdit(editSubplebbitOptions);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
let isActive = true;
|
||||
if (editSubplebbitOptions && triggerPublilshSubplebbitEdit) {
|
||||
@@ -210,20 +194,19 @@ const BoardSettings = ({ subplebbit }) => {
|
||||
};
|
||||
}, [editSubplebbitOptions, publishSubplebbitEdit, triggerPublilshSubplebbitEdit]);
|
||||
|
||||
|
||||
const handleSaveChanges = async () => {
|
||||
try {
|
||||
const updatedSettings = JSON.parse(boardSettingsJson);
|
||||
validateSettings(updatedSettings, allowedSettings);
|
||||
const changes = getDifferences(initialSettings, updatedSettings);
|
||||
if (Object.keys(changes).length > 0) {
|
||||
setEditSubplebbitOptions(prevOptions => ({
|
||||
setEditSubplebbitOptions((prevOptions) => ({
|
||||
...prevOptions,
|
||||
...changes
|
||||
...changes,
|
||||
}));
|
||||
setTriggerPublishCommentEdit(true);
|
||||
} else {
|
||||
setNewErrorMessage("No changes detected");
|
||||
setNewErrorMessage('No changes detected');
|
||||
}
|
||||
} catch (error) {
|
||||
setNewErrorMessage(`Error saving changes: ${error}`);
|
||||
@@ -231,12 +214,10 @@ const BoardSettings = ({ subplebbit }) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleResetChanges = () => {
|
||||
setBoardSettingsJson(JSON.stringify(initialSettings, null, 2));
|
||||
};
|
||||
|
||||
|
||||
function generateSettingsList(settingsObj, parentKey = '') {
|
||||
let result = [];
|
||||
|
||||
@@ -256,10 +237,8 @@ const BoardSettings = ({ subplebbit }) => {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
const possibleSettingsList = generateSettingsList(initialSettings);
|
||||
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setIsModalOpen(false);
|
||||
setBoardSettingsJson(JSON.stringify(initialSettings, null, 2));
|
||||
@@ -270,54 +249,58 @@ const BoardSettings = ({ subplebbit }) => {
|
||||
setBoardSettingsJson(JSON.stringify(generateSettingsFromSubplebbit(subplebbit), null, 2));
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledModal
|
||||
isOpen={isModalOpen}
|
||||
onRequestClose={handleCloseModal}
|
||||
contentLabel="Board Settings"
|
||||
style={{ overlay: { backgroundColor: "rgba(0,0,0,.25)" }}}
|
||||
contentLabel='Board Settings'
|
||||
style={{ overlay: { backgroundColor: 'rgba(0,0,0,.25)' } }}
|
||||
selectedStyle={selectedStyle}
|
||||
>
|
||||
<div className="panel-board">
|
||||
<div className="panel-header">
|
||||
<div className='panel-board'>
|
||||
<div className='panel-header'>
|
||||
Board Settings
|
||||
<Link to="" onClick={handleCloseModal}>
|
||||
<span className="icon" title="close" />
|
||||
<Link to='' onClick={handleCloseModal}>
|
||||
<span className='icon' title='close' />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="settings-info">
|
||||
<div className='settings-info'>
|
||||
<div>
|
||||
<strong>Allowed settings: </strong>
|
||||
<span>
|
||||
{`{ ${possibleSettingsList.join(', ')} }`}
|
||||
</span>
|
||||
<span>{`{ ${possibleSettingsList.join(', ')} }`}</span>
|
||||
</div>
|
||||
<strong style={{marginTop: '10px', display: 'inline-block'}}>API docs: </strong><a style={{color: 'inherit'}} href="https://github.com/plebbit/plebbit-js#readme" target="_blank" rel="noreferrer">https://github.com/plebbit/plebbit-js#readme</a>
|
||||
<strong style={{ marginTop: '10px', display: 'inline-block' }}>API docs: </strong>
|
||||
<a style={{ color: 'inherit' }} href='https://github.com/plebbit/plebbit-js#readme' target='_blank' rel='noreferrer'>
|
||||
https://github.com/plebbit/plebbit-js#readme
|
||||
</a>
|
||||
</div>
|
||||
<textarea
|
||||
value={boardSettingsJson}
|
||||
onChange={e => setBoardSettingsJson(e.target.value)}
|
||||
className="board-settings"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
spellCheck="false"
|
||||
onChange={(e) => setBoardSettingsJson(e.target.value)}
|
||||
className='board-settings'
|
||||
autoComplete='off'
|
||||
autoCorrect='off'
|
||||
spellCheck='false'
|
||||
/>
|
||||
<div className="button-group">
|
||||
<button id="reset-board-settings" onClick={handleResetChanges}>Reset</button>
|
||||
<button id="save-board-settings" onClick={handleSaveChanges}>Save Changes</button>
|
||||
<div className='button-group'>
|
||||
<button id='reset-board-settings' onClick={handleResetChanges}>
|
||||
Reset
|
||||
</button>
|
||||
<button id='save-board-settings' onClick={handleSaveChanges}>
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</StyledModal>
|
||||
[
|
||||
<span id="subscribe" style={{ cursor: 'pointer' }}>
|
||||
<span id='subscribe' style={{ cursor: 'pointer' }}>
|
||||
<span
|
||||
onClick={() => {
|
||||
window.electron && window.electron.isElectron
|
||||
? openModal()
|
||||
: alert(
|
||||
'To edit this board you must be using the plebchan desktop app, which is a plebbit full node that seeds the board automatically.\n\nDownload plebchan here:\n\nhttps://github.com/plebbit/plebchan/releases/latest'
|
||||
'To edit this board you must be using the plebchan desktop app, which is a plebbit full node that seeds the board automatically.\n\nDownload plebchan here:\n\nhttps://github.com/plebbit/plebchan/releases/latest',
|
||||
);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import React, { useState } from "react";
|
||||
import { useSubplebbit, useSubplebbitStats } from "@plebbit/plebbit-react-hooks/dist";
|
||||
import { BoardStatsContainer } from "./styled/BoardStats.styled";
|
||||
import { Break } from "./styled/views/Board.styled";
|
||||
import React, { useState } from 'react';
|
||||
import { useSubplebbit, useSubplebbitStats } from '@plebbit/plebbit-react-hooks/dist';
|
||||
import { BoardStatsContainer } from './styled/BoardStats.styled';
|
||||
import { Break } from './styled/views/Board.styled';
|
||||
import useGeneralStore from '../hooks/stores/useGeneralStore';
|
||||
|
||||
|
||||
|
||||
const BoardStats = ({ subplebbitAddress }) => {
|
||||
const { selectedStyle } = useGeneralStore(state => state);
|
||||
const { selectedStyle } = useGeneralStore((state) => state);
|
||||
const stats = useSubplebbitStats({ subplebbitAddress });
|
||||
const [showStats, setShowStats] = useState(true);
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress });
|
||||
@@ -24,22 +22,36 @@ const BoardStats = ({ subplebbitAddress }) => {
|
||||
return month + '/' + day + '/' + year;
|
||||
};
|
||||
|
||||
const pluralize = (count, singular, plural) => count === 1 ? singular : plural;
|
||||
const pluralize = (count, singular, plural) => (count === 1 ? singular : plural);
|
||||
|
||||
return (
|
||||
<BoardStatsContainer selectedStyle={selectedStyle}>
|
||||
<Break selectedStyle={selectedStyle} style={{ width: '468px' }} />
|
||||
<table id="blotter">
|
||||
<table id='blotter'>
|
||||
{showStats && (
|
||||
<tbody id="blotter-msgs">
|
||||
<tbody id='blotter-msgs'>
|
||||
<tr>
|
||||
<td>In the past hour, <span id="stat-number">{stats.hourActiveUserCount}</span> {pluralize(stats.hourActiveUserCount, 'user', 'users')} made <span id="stat-number">{stats.hourPostCount}</span> {pluralize(stats.hourPostCount, 'post', 'posts')} / in the past day, <span id="stat-number">{stats.dayActiveUserCount}</span> {pluralize(stats.dayActiveUserCount, 'user', 'users')} made <span id="stat-number">{stats.dayPostCount}</span> {pluralize(stats.dayPostCount, 'post', 'posts')}</td>
|
||||
<td>
|
||||
In the past hour, <span id='stat-number'>{stats.hourActiveUserCount}</span> {pluralize(stats.hourActiveUserCount, 'user', 'users')} made{' '}
|
||||
<span id='stat-number'>{stats.hourPostCount}</span> {pluralize(stats.hourPostCount, 'post', 'posts')} / in the past day,{' '}
|
||||
<span id='stat-number'>{stats.dayActiveUserCount}</span> {pluralize(stats.dayActiveUserCount, 'user', 'users')} made{' '}
|
||||
<span id='stat-number'>{stats.dayPostCount}</span> {pluralize(stats.dayPostCount, 'post', 'posts')}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>In the past week, <span id="stat-number">{stats.weekActiveUserCount}</span> {pluralize(stats.weekActiveUserCount, 'user', 'users')} made <span id="stat-number">{stats.weekPostCount}</span> {pluralize(stats.weekPostCount, 'post', 'posts')} / in the past month, <span id="stat-number">{stats.monthActiveUserCount}</span> {pluralize(stats.monthActiveUserCount, 'user', 'users')} made <span id="stat-number">{stats.monthPostCount}</span> {pluralize(stats.monthPostCount, 'post', 'posts')}</td>
|
||||
<td>
|
||||
In the past week, <span id='stat-number'>{stats.weekActiveUserCount}</span> {pluralize(stats.weekActiveUserCount, 'user', 'users')} made{' '}
|
||||
<span id='stat-number'>{stats.weekPostCount}</span> {pluralize(stats.weekPostCount, 'post', 'posts')} / in the past month,{' '}
|
||||
<span id='stat-number'>{stats.monthActiveUserCount}</span> {pluralize(stats.monthActiveUserCount, 'user', 'users')} made{' '}
|
||||
<span id='stat-number'>{stats.monthPostCount}</span> {pluralize(stats.monthPostCount, 'post', 'posts')}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{unixToMMDDYYYY(subplebbit.createdAt)} board created / since then, <span id="stat-number">{stats.allActiveUserCount}</span> {pluralize(stats.allActiveUserCount, 'user', 'users')} have made <span id="stat-number">{stats.allPostCount}</span> {pluralize(stats.allPostCount, 'post', 'posts')}</td>
|
||||
<td>
|
||||
{unixToMMDDYYYY(subplebbit.createdAt)} board created / since then, <span id='stat-number'>{stats.allActiveUserCount}</span>{' '}
|
||||
{pluralize(stats.allActiveUserCount, 'user', 'users')} have made <span id='stat-number'>{stats.allPostCount}</span>{' '}
|
||||
{pluralize(stats.allPostCount, 'post', 'posts')}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
)}
|
||||
@@ -47,7 +59,7 @@ const BoardStats = ({ subplebbitAddress }) => {
|
||||
<tr>
|
||||
<td colSpan={2}>
|
||||
[
|
||||
<span id="stat-number" className="hide-button" onClick={handleToggleStats}>
|
||||
<span id='stat-number' className='hide-button' onClick={handleToggleStats}>
|
||||
{showStats ? 'Hide' : 'Show Stats'}
|
||||
</span>
|
||||
]
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import ContentLoader from 'react-content-loader';
|
||||
import useGeneralStore from '../hooks/stores/useGeneralStore';
|
||||
|
||||
|
||||
const SingleRectLoader = () => {
|
||||
const selectedStyle = useGeneralStore((state) => state.selectedStyle);
|
||||
const backgroundColor = selectedStyle === 'Tomorrow' ? '#333' : '#f3f3f3';
|
||||
@@ -13,7 +12,7 @@ const SingleRectLoader = () => {
|
||||
speed={2}
|
||||
width={150}
|
||||
height={215}
|
||||
viewBox="0 0 150 215"
|
||||
viewBox='0 0 150 215'
|
||||
backgroundColor={backgroundColor}
|
||||
foregroundColor={foregroundColor}
|
||||
style={{
|
||||
@@ -23,14 +22,13 @@ const SingleRectLoader = () => {
|
||||
marginBottom: '30px',
|
||||
}}
|
||||
>
|
||||
<rect x={0} y={0} width="150" height="150" />
|
||||
<rect x={0} y={170} width="150" height="18" />
|
||||
<rect x={0} y={195} width="80" height="20" />
|
||||
<rect x={0} y={0} width='150' height='150' />
|
||||
<rect x={0} y={170} width='150' height='18' />
|
||||
<rect x={0} y={195} width='80' height='20' />
|
||||
</ContentLoader>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const CatalogLoader = () => {
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -1,62 +1,58 @@
|
||||
import React, { useState } from "react";
|
||||
import { useComment, useEditedComment } from "@plebbit/plebbit-react-hooks";
|
||||
import { Link } from "react-router-dom";
|
||||
import OriginalCommentModal from "./modals/OriginalCommentModal";
|
||||
import getDate from "../utils/getDate";
|
||||
import useGeneralStore from "../hooks/stores/useGeneralStore";
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { useComment, useEditedComment } from '@plebbit/plebbit-react-hooks';
|
||||
import { Link } from 'react-router-dom';
|
||||
import OriginalCommentModal from './modals/OriginalCommentModal';
|
||||
import getDate from '../utils/getDate';
|
||||
import useGeneralStore from '../hooks/stores/useGeneralStore';
|
||||
|
||||
const EditLabel = ({ commentCid, className }) => {
|
||||
const { editedComments, setEditedComments } = useGeneralStore(state => state);
|
||||
const { editedComments, setEditedComments } = useGeneralStore((state) => state);
|
||||
const [isOriginalCommentModalOpen, setIsOriginalCommentModalOpen] = useState(false);
|
||||
const comment = useComment({ commentCid });
|
||||
const timestamp = getDate(comment.edit?.timestamp);
|
||||
const { state: editedCommentState, editedComment } = useEditedComment({ comment });
|
||||
|
||||
if (editedCommentState === 'pending' && !(commentCid in editedComments)) {
|
||||
setEditedComments({...editedComments, [commentCid]: editedComment})
|
||||
setEditedComments({ ...editedComments, [commentCid]: editedComment });
|
||||
}
|
||||
|
||||
const conditionsCheck = () => {
|
||||
let conditions = [];
|
||||
|
||||
if (editedComment?.removed && !comment.removed) {
|
||||
conditions.push("removal");
|
||||
conditions.push('removal');
|
||||
}
|
||||
if (editedComment?.edit && !comment.edit) {
|
||||
conditions.push("edit");
|
||||
conditions.push('edit');
|
||||
}
|
||||
if (editedComment?.locked && !comment.locked) {
|
||||
conditions.push("lock");
|
||||
conditions.push('lock');
|
||||
}
|
||||
if (editedComment?.pinned && !comment.pinned) {
|
||||
conditions.push("sticky");
|
||||
conditions.push('sticky');
|
||||
}
|
||||
|
||||
return conditions.length > 0 ? conditions.join(', ') : null;
|
||||
};
|
||||
|
||||
|
||||
const conditionsString = conditionsCheck();
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<OriginalCommentModal
|
||||
isOpen={isOriginalCommentModalOpen}
|
||||
closeModal={() => setIsOriginalCommentModalOpen(false)}
|
||||
comment={comment}/>
|
||||
<OriginalCommentModal isOpen={isOriginalCommentModalOpen} closeModal={() => setIsOriginalCommentModalOpen(false)} comment={comment} />
|
||||
{comment.edit && (
|
||||
<>
|
||||
<br />
|
||||
<span className={className}>
|
||||
(Edited at {timestamp}, <Link className="ttl-link" onClick={
|
||||
() => setIsOriginalCommentModalOpen(true)
|
||||
}>show original</Link>)
|
||||
(Edited at {timestamp},{' '}
|
||||
<Link className='ttl-link' onClick={() => setIsOriginalCommentModalOpen(true)}>
|
||||
show original
|
||||
</Link>
|
||||
)
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{((editedCommentState === 'pending' || editedCommentState === 'failed') && conditionsString) ? (
|
||||
{(editedCommentState === 'pending' || editedCommentState === 'failed') && conditionsString ? (
|
||||
<>
|
||||
<br />
|
||||
<span className={className}>
|
||||
|
||||
+93
-68
@@ -39,35 +39,37 @@ const YoutubeEmbed = ({parsedUrl}) => {
|
||||
let youtubeId;
|
||||
if (parsedUrl.host.endsWith('youtu.be')) {
|
||||
youtubeId = parsedUrl.pathname.replaceAll('/', '');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
youtubeId = parsedUrl.searchParams.get('v');
|
||||
}
|
||||
return <iframe
|
||||
return (
|
||||
<iframe
|
||||
className='enlarged youtube-embed'
|
||||
height="100%"
|
||||
width="100%"
|
||||
frameborder="0"
|
||||
height='100%'
|
||||
width='100%'
|
||||
frameborder='0'
|
||||
credentialless
|
||||
referrerpolicy='no-referrer'
|
||||
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
|
||||
allowfullscreen
|
||||
title={parsedUrl.href}
|
||||
src={`https://www.youtube-nocookie.com/embed/${youtubeId}`}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const twitterHosts = new Set(['twitter.com', 'www.twitter.com', 'x.com', 'www.x.com']);
|
||||
|
||||
const TwitterEmbed = ({ parsedUrl }) => {
|
||||
return <iframe
|
||||
return (
|
||||
<iframe
|
||||
className='enlarged twitter-embed'
|
||||
height="100%"
|
||||
width="100%"
|
||||
frameborder="0"
|
||||
height='100%'
|
||||
width='100%'
|
||||
frameborder='0'
|
||||
credentialless
|
||||
referrerpolicy='no-referrer'
|
||||
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
|
||||
title={parsedUrl.href}
|
||||
srcdoc={`
|
||||
<blockquote class="twitter-tweet" data-theme="dark">
|
||||
@@ -75,20 +77,22 @@ const TwitterEmbed = ({parsedUrl}) => {
|
||||
</blockquote>
|
||||
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
`}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const redditHosts = new Set(['reddit.com', 'www.reddit.com', 'old.reddit.com']);
|
||||
|
||||
const RedditEmbed = ({ parsedUrl }) => {
|
||||
return <iframe
|
||||
return (
|
||||
<iframe
|
||||
className='enlarged reddit-embed'
|
||||
height="100%"
|
||||
width="100%"
|
||||
frameborder="0"
|
||||
height='100%'
|
||||
width='100%'
|
||||
frameborder='0'
|
||||
credentialless
|
||||
referrerpolicy='no-referrer'
|
||||
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
|
||||
title={parsedUrl.href}
|
||||
srcdoc={`
|
||||
<style>
|
||||
@@ -102,7 +106,8 @@ const RedditEmbed = ({parsedUrl}) => {
|
||||
</blockquote>
|
||||
<script async src="https://embed.reddit.com/widgets.js" charset="UTF-8"></script>
|
||||
`}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const twitchHosts = new Set(['twitch.tv', 'www.twitch.tv']);
|
||||
@@ -112,37 +117,39 @@ const TwitchEmbed = ({parsedUrl}) => {
|
||||
if (parsedUrl.pathname.startsWith('/videos/')) {
|
||||
const videoId = parsedUrl.pathname.replace('/videos/', '');
|
||||
iframeUrl = `https://player.twitch.tv/?video=${videoId}&parent=${window.location.hostname}`;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
const channel = parsedUrl.pathname.replaceAll('/', '');
|
||||
iframeUrl = `https://player.twitch.tv/?channel=${channel}&parent=${window.location.hostname}`;
|
||||
}
|
||||
return <iframe
|
||||
return (
|
||||
<iframe
|
||||
className='enlarged twitch-embed'
|
||||
height="100%"
|
||||
width="100%"
|
||||
frameborder="0"
|
||||
height='100%'
|
||||
width='100%'
|
||||
frameborder='0'
|
||||
credentialless
|
||||
referrerpolicy='no-referrer'
|
||||
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
|
||||
allowfullscreen
|
||||
title={parsedUrl.href}
|
||||
src={iframeUrl}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const tiktokHosts = new Set(['tiktok.com', 'www.tiktok.com']);
|
||||
|
||||
const TiktokEmbed = ({ parsedUrl }) => {
|
||||
const videoId = parsedUrl.pathname.replace(/.+\/video\//, '').replaceAll('/', '');
|
||||
return <iframe
|
||||
return (
|
||||
<iframe
|
||||
className='enlarged tiktok-embed'
|
||||
height="100%"
|
||||
width="100%"
|
||||
frameborder="0"
|
||||
height='100%'
|
||||
width='100%'
|
||||
frameborder='0'
|
||||
credentialless
|
||||
referrerpolicy='no-referrer'
|
||||
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
|
||||
title={parsedUrl.href}
|
||||
srcdoc={`
|
||||
<blockquote class="tiktok-embed" data-video-id="${videoId}">
|
||||
@@ -150,7 +157,8 @@ const TiktokEmbed = ({parsedUrl}) => {
|
||||
</blockquote>
|
||||
<script async src="https://www.tiktok.com/embed.js"></script>
|
||||
`}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const instagramHosts = new Set(['instagram.com', 'www.instagram.com']);
|
||||
@@ -158,14 +166,15 @@ const instagramHosts = new Set(['instagram.com', 'www.instagram.com']);
|
||||
const InstagramEmbed = ({ parsedUrl }) => {
|
||||
const pathNames = parsedUrl.pathname.replace(/\/+$/, '').split('/');
|
||||
const id = pathNames[pathNames.length - 1];
|
||||
return <iframe
|
||||
return (
|
||||
<iframe
|
||||
className='enlarged instagram-embed'
|
||||
height="100%"
|
||||
width="100%"
|
||||
frameborder="0"
|
||||
height='100%'
|
||||
width='100%'
|
||||
frameborder='0'
|
||||
credentialless
|
||||
referrerpolicy='no-referrer'
|
||||
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
|
||||
title={parsedUrl.href}
|
||||
srcdoc={`
|
||||
<blockquote class="instagram-media">
|
||||
@@ -173,85 +182,101 @@ const InstagramEmbed = ({parsedUrl}) => {
|
||||
</blockquote>
|
||||
<script async src="//www.instagram.com/embed.js"></script>
|
||||
`}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const odyseeHosts = new Set(['odysee.com', 'www.odysee.com']);
|
||||
|
||||
const OdyseeEmbed = ({ parsedUrl }) => {
|
||||
const iframeUrl = `https://odysee.com/$/embed${parsedUrl.pathname}`;
|
||||
return <iframe
|
||||
return (
|
||||
<iframe
|
||||
className='enlarged odysee-embed'
|
||||
height="100%"
|
||||
width="100%"
|
||||
frameborder="0"
|
||||
height='100%'
|
||||
width='100%'
|
||||
frameborder='0'
|
||||
credentialless
|
||||
referrerpolicy='no-referrer'
|
||||
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
|
||||
allowfullscreen
|
||||
title={parsedUrl.href}
|
||||
src={iframeUrl}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const bitchuteHosts = new Set(['bitchute.com', 'www.bitchute.com']);
|
||||
|
||||
const BitchuteEmbed = ({ parsedUrl }) => {
|
||||
const videoId = parsedUrl.pathname.replace(/\/video\//, '').replaceAll('/', '');
|
||||
return <iframe
|
||||
return (
|
||||
<iframe
|
||||
className='enlarged bitchute-embed'
|
||||
height="100%"
|
||||
width="100%"
|
||||
frameborder="0"
|
||||
height='100%'
|
||||
width='100%'
|
||||
frameborder='0'
|
||||
credentialless
|
||||
referrerpolicy='no-referrer'
|
||||
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
|
||||
allowfullscreen
|
||||
title={parsedUrl.href}
|
||||
src={`https://www.bitchute.com/embed/${videoId}/`}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const streamableHosts = new Set(['streamable.com', 'www.streamable.com']);
|
||||
|
||||
const StreamableEmbed = ({ parsedUrl }) => {
|
||||
const videoId = parsedUrl.pathname.replaceAll('/', '');
|
||||
return <iframe
|
||||
return (
|
||||
<iframe
|
||||
className='enlarged streamable-embed'
|
||||
height="100%"
|
||||
width="100%"
|
||||
frameborder="0"
|
||||
height='100%'
|
||||
width='100%'
|
||||
frameborder='0'
|
||||
credentialless
|
||||
referrerpolicy='no-referrer'
|
||||
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
|
||||
allowfullscreen
|
||||
title={parsedUrl.href}
|
||||
src={`https://streamable.com/e/${videoId}`}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const spotifyHosts = new Set(['spotify.com', 'www.spotify.com', 'open.spotify.com']);
|
||||
|
||||
const SpotifyEmbed = ({ parsedUrl }) => {
|
||||
const iframeUrl = `https://open.spotify.com/embed${parsedUrl.pathname}?theme=0`
|
||||
return <iframe
|
||||
const iframeUrl = `https://open.spotify.com/embed${parsedUrl.pathname}?theme=0`;
|
||||
return (
|
||||
<iframe
|
||||
className='enlarged spotify-embed'
|
||||
height="100%"
|
||||
width="100%"
|
||||
frameborder="0"
|
||||
height='100%'
|
||||
width='100%'
|
||||
frameborder='0'
|
||||
credentialless
|
||||
referrerpolicy='no-referrer'
|
||||
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share'
|
||||
allowfullscreen
|
||||
title={parsedUrl.href}
|
||||
src={iframeUrl}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const canEmbedHosts = new Set([
|
||||
...youtubeHosts, ...twitterHosts, ...redditHosts, ...twitchHosts,
|
||||
...tiktokHosts, ...instagramHosts, ...odyseeHosts, ...bitchuteHosts,
|
||||
...streamableHosts, ...spotifyHosts
|
||||
...youtubeHosts,
|
||||
...twitterHosts,
|
||||
...redditHosts,
|
||||
...twitchHosts,
|
||||
...tiktokHosts,
|
||||
...instagramHosts,
|
||||
...odyseeHosts,
|
||||
...bitchuteHosts,
|
||||
...streamableHosts,
|
||||
...spotifyHosts,
|
||||
]);
|
||||
|
||||
export const canEmbed = (parsedUrl) => canEmbedHosts.has(parsedUrl.host);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import React, { useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const ForwardRefLink = React.forwardRef((props, ref) => {
|
||||
const { children, setRefAndCid, onMouseOver, onMouseLeave, onClick, ...otherProps } = props;
|
||||
|
||||
const handleClick = (event) => {
|
||||
if (otherProps.to === "#void") {
|
||||
if (otherProps.to === '#void') {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (onClick) {
|
||||
@@ -20,13 +20,7 @@ const ForwardRefLink = React.forwardRef((props, ref) => {
|
||||
}, [ref, setRefAndCid]);
|
||||
|
||||
return (
|
||||
<Link
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
onMouseOver={onMouseOver}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<Link ref={ref} {...otherProps} onMouseOver={onMouseOver} onMouseLeave={onMouseLeave} onClick={handleClick}>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -31,11 +31,7 @@ const ImageBanner = () => {
|
||||
};
|
||||
}, [parentRoute]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{currentImage && <img id="banner-img" src={`${process.env.PUBLIC_URL}/assets/banners/banner-${currentImage}.jpg`} alt="banner" />}
|
||||
</>
|
||||
);
|
||||
return <>{currentImage && <img id='banner-img' src={`${process.env.PUBLIC_URL}/assets/banners/banner-${currentImage}.jpg`} alt='banner' />}</>;
|
||||
};
|
||||
|
||||
export function importAll(r) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { useSubplebbit } from "@plebbit/plebbit-react-hooks";
|
||||
import React from 'react';
|
||||
import { useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
|
||||
const OfflineIndicator = ({ address, className, tooltipPlace }) => {
|
||||
const subplebbit = useSubplebbit({ subplebbitAddress: address });
|
||||
@@ -9,13 +9,13 @@ const OfflineIndicator = ({ address, className, tooltipPlace }) => {
|
||||
<>
|
||||
{!isOnline && (
|
||||
<>
|
||||
{" "}
|
||||
{' '}
|
||||
<img
|
||||
className={className}
|
||||
alt="offline"
|
||||
src="assets/offline.png"
|
||||
data-tooltip-id="tooltip"
|
||||
data-tooltip-content="Offline"
|
||||
alt='offline'
|
||||
src='assets/offline.png'
|
||||
data-tooltip-id='tooltip'
|
||||
data-tooltip-content='Offline'
|
||||
data-tooltip-place={tooltipPlace}
|
||||
style={{ imageRendering: 'pixelated' }}
|
||||
/>
|
||||
|
||||
@@ -1,28 +1,18 @@
|
||||
import React from "react";
|
||||
import { useAccountComment } from "@plebbit/plebbit-react-hooks";
|
||||
import React from 'react';
|
||||
import { useAccountComment } from '@plebbit/plebbit-react-hooks';
|
||||
|
||||
const PendingLabel = ({ commentIndex }) => {
|
||||
const comment = useAccountComment({ commentIndex: commentIndex });
|
||||
|
||||
if (commentIndex === undefined) return null;
|
||||
|
||||
return (
|
||||
comment.cid ? (
|
||||
return comment.cid ? (
|
||||
<span>{comment.cid.slice(2, 14)}</span>
|
||||
) : (
|
||||
comment.state === "pending" ? (
|
||||
<span style={{color: 'red', fontWeight: '700'}}>
|
||||
Pending
|
||||
</span>
|
||||
) : (
|
||||
comment.state === "failed" ? (
|
||||
<span style={{color: 'red', fontWeight: '700'}}>
|
||||
Failed
|
||||
</span>
|
||||
) : null
|
||||
)
|
||||
)
|
||||
);
|
||||
) : comment.state === 'pending' ? (
|
||||
<span style={{ color: 'red', fontWeight: '700' }}>Pending</span>
|
||||
) : comment.state === 'failed' ? (
|
||||
<span style={{ color: 'red', fontWeight: '700' }}>Failed</span>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export default PendingLabel;
|
||||
+17
-19
@@ -4,19 +4,20 @@ import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
|
||||
import breaks from 'remark-breaks';
|
||||
import ForwardRefLink from './ForwardRefLink';
|
||||
|
||||
|
||||
const Post = ({ content, postQuoteOnClick, postQuoteOnOver, postQuoteOnLeave, postQuoteRef }) => {
|
||||
const doubleNewlineContent = content?.replace(/\n/g, ' \n\n');
|
||||
|
||||
const customSchema = useMemo(() => ({
|
||||
const customSchema = useMemo(
|
||||
() => ({
|
||||
...defaultSchema,
|
||||
tagNames: [...defaultSchema.tagNames, 'div'],
|
||||
attributes: {
|
||||
...defaultSchema.attributes,
|
||||
div: ['className'],
|
||||
},
|
||||
}), []);
|
||||
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const blockquoteToGreentext = () => (tree) => {
|
||||
tree.children.forEach((node) => {
|
||||
@@ -41,12 +42,11 @@ const Post = ({ content, postQuoteOnClick, postQuoteOnOver, postQuoteOnLeave, po
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const createQuotelink = (children, postQuoteOnClick, postQuoteOnOver, postQuoteOnLeave, postQuoteRef) => {
|
||||
const patternC = "(c/[A-Za-z0-9]{46}|c/[A-Za-z0-9]{12})";
|
||||
const patternP = "(p/([A-Za-z0-9]{52}|[A-Za-z0-9-.]*\\.eth))";
|
||||
const patternU = "(u/([A-Za-z0-9]{52}|[A-Za-z0-9-.]*\\.eth))";
|
||||
const patternPC = "(p/([A-Za-z0-9]{52}|[A-Za-z0-9-.]*\\.eth)/c/[A-Za-z0-9]{46})";
|
||||
const patternC = '(c/[A-Za-z0-9]{46}|c/[A-Za-z0-9]{12})';
|
||||
const patternP = '(p/([A-Za-z0-9]{52}|[A-Za-z0-9-.]*\\.eth))';
|
||||
const patternU = '(u/([A-Za-z0-9]{52}|[A-Za-z0-9-.]*\\.eth))';
|
||||
const patternPC = '(p/([A-Za-z0-9]{52}|[A-Za-z0-9-.]*\\.eth)/c/[A-Za-z0-9]{46})';
|
||||
|
||||
const regex = new RegExp(`${patternC}|${patternPC}|${patternU}|${patternP}`, 'g');
|
||||
|
||||
@@ -71,10 +71,10 @@ const Post = ({ content, postQuoteOnClick, postQuoteOnOver, postQuoteOnLeave, po
|
||||
const linkRef = React.createRef();
|
||||
|
||||
let linkTo = () => {};
|
||||
const linkTarget = matchedText.startsWith('u/') ? "_blank" : "_self";
|
||||
const linkTarget = matchedText.startsWith('u/') ? '_blank' : '_self';
|
||||
|
||||
if (matchedText.startsWith('u/')) {
|
||||
linkTo = "#void";
|
||||
linkTo = '#void';
|
||||
} else if (matchedText.startsWith('p/') || matchedText.startsWith('p/')) {
|
||||
linkTo = `/${matchedText}`;
|
||||
}
|
||||
@@ -82,7 +82,7 @@ const Post = ({ content, postQuoteOnClick, postQuoteOnOver, postQuoteOnLeave, po
|
||||
parts.push(
|
||||
<ForwardRefLink
|
||||
key={`link-${i}-${matchedText}`}
|
||||
className="quotelink"
|
||||
className='quotelink'
|
||||
to={linkTo}
|
||||
target={linkTarget}
|
||||
ref={linkRef}
|
||||
@@ -91,7 +91,9 @@ const Post = ({ content, postQuoteOnClick, postQuoteOnOver, postQuoteOnLeave, po
|
||||
postQuoteRef(cid, ref);
|
||||
}
|
||||
}}
|
||||
onClick={() => {postQuoteOnClick(cid)}}
|
||||
onClick={() => {
|
||||
postQuoteOnClick(cid);
|
||||
}}
|
||||
onMouseOver={() => {
|
||||
postQuoteOnOver(cid);
|
||||
}}
|
||||
@@ -100,7 +102,7 @@ const Post = ({ content, postQuoteOnClick, postQuoteOnOver, postQuoteOnLeave, po
|
||||
}}
|
||||
>
|
||||
{matchedText}
|
||||
</ForwardRefLink>
|
||||
</ForwardRefLink>,
|
||||
);
|
||||
|
||||
lastIndex = index + matchedText.length;
|
||||
@@ -114,8 +116,6 @@ const Post = ({ content, postQuoteOnClick, postQuoteOnOver, postQuoteOnLeave, po
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<ReactMarkdown
|
||||
children={doubleNewlineContent}
|
||||
@@ -126,9 +126,7 @@ const Post = ({ content, postQuoteOnClick, postQuoteOnOver, postQuoteOnLeave, po
|
||||
video: ({ src }) => <span>{src}</span>,
|
||||
source: ({ src }) => <span>{src}</span>,
|
||||
gif: ({ src }) => <span>{src}</span>,
|
||||
p: ({ children }) => <div className='custom-paragraph'>
|
||||
{createQuotelink(children, postQuoteOnClick, postQuoteOnOver, postQuoteOnLeave, postQuoteRef)}
|
||||
</div>,
|
||||
p: ({ children }) => <div className='custom-paragraph'>{createQuotelink(children, postQuoteOnClick, postQuoteOnOver, postQuoteOnLeave, postQuoteRef)}</div>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -9,15 +9,10 @@ const SinglePostLoader = () => {
|
||||
|
||||
return (
|
||||
<div style={{ paddingLeft: '30px', paddingRight: '30px', marginBottom: '50px', marginTop: '30px' }}>
|
||||
<ContentLoader
|
||||
width="100%"
|
||||
height={15 * 3 + 30}
|
||||
backgroundColor={backgroundColor}
|
||||
foregroundColor={foregroundColor}
|
||||
>
|
||||
<rect x="0" y="8" width="100%" height="15" />
|
||||
<rect x="0" y="30" width="100%" height="15" />
|
||||
<rect x="0" y="52" width="100%" height="15" />
|
||||
<ContentLoader width='100%' height={15 * 3 + 30} backgroundColor={backgroundColor} foregroundColor={foregroundColor}>
|
||||
<rect x='0' y='8' width='100%' height='15' />
|
||||
<rect x='0' y='30' width='100%' height='15' />
|
||||
<rect x='0' y='52' width='100%' height='15' />
|
||||
</ContentLoader>
|
||||
</div>
|
||||
);
|
||||
|
||||
+168
-193
@@ -1,28 +1,30 @@
|
||||
import React, { Fragment } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useAccount, useComment } from "@plebbit/plebbit-react-hooks";
|
||||
import getDate from "../utils/getDate";
|
||||
import getCommentMediaInfo from "../utils/getCommentMediaInfo";
|
||||
import findShortParentCid from "../utils/findShortParentCid";
|
||||
import Post from "./Post";
|
||||
import EditLabel from "./EditLabel";
|
||||
import useStateString from "../hooks/useStateString";
|
||||
import { BoardForm, Container } from "./styled/views/Board.styled";
|
||||
import useGeneralStore from "../hooks/stores/useGeneralStore";
|
||||
import React, { Fragment } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useAccount, useComment } from '@plebbit/plebbit-react-hooks';
|
||||
import getDate from '../utils/getDate';
|
||||
import getCommentMediaInfo from '../utils/getCommentMediaInfo';
|
||||
import findShortParentCid from '../utils/findShortParentCid';
|
||||
import Post from './Post';
|
||||
import EditLabel from './EditLabel';
|
||||
import useStateString from '../hooks/useStateString';
|
||||
import { BoardForm, Container } from './styled/views/Board.styled';
|
||||
import useGeneralStore from '../hooks/stores/useGeneralStore';
|
||||
|
||||
const PostOnHover = ({ cid, feed }) => {
|
||||
const selectedStyle = useGeneralStore(state => state.selectedStyle);
|
||||
const selectedStyle = useGeneralStore((state) => state.selectedStyle);
|
||||
const account = useAccount();
|
||||
const reply = useComment({ commentCid: cid });
|
||||
const replyMediaInfo = getCommentMediaInfo(reply);
|
||||
const fallbackImgUrl = "assets/filedeleted-res.gif";
|
||||
const fallbackImgUrl = 'assets/filedeleted-res.gif';
|
||||
const selectedFeed = feed;
|
||||
const thread = useComment({ commentCid: reply.parentCid });
|
||||
const shortParentCid = findShortParentCid(reply.parentCid, selectedFeed);
|
||||
const stateString = useStateString(reply);
|
||||
|
||||
return (
|
||||
<Container selectedStyle={selectedStyle} style={{
|
||||
<Container
|
||||
selectedStyle={selectedStyle}
|
||||
style={{
|
||||
margin: '0',
|
||||
padding: '0',
|
||||
whiteSpace: 'normal',
|
||||
@@ -31,284 +33,257 @@ const PostOnHover = ({ cid, feed }) => {
|
||||
wordWrap: 'break-word',
|
||||
wordBreak: 'break-all',
|
||||
boxSizing: 'border-box',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<BoardForm selectedStyle={selectedStyle} style={{ margin: '0', padding: '0' }}>
|
||||
<div className="board" style={{margin: '0', padding: '0'}}>
|
||||
<div className="thread" style={{margin: '0', padding: '0'}}>
|
||||
<div className='board' style={{ margin: '0', padding: '0' }}>
|
||||
<div className='thread' style={{ margin: '0', padding: '0' }}>
|
||||
{reply.state === 'succeeded' ? (
|
||||
<div className="reply-container">
|
||||
<div className="post-reply post-reply-desktop">
|
||||
<div className="post-info">
|
||||
<span className="nameblock">
|
||||
{reply.author?.displayName
|
||||
? reply.author?.displayName.length > 20
|
||||
? <Fragment >
|
||||
<span className="name"
|
||||
data-tooltip-id="tooltip"
|
||||
data-tooltip-content={reply.author?.displayName}
|
||||
data-tooltip-place="top">
|
||||
{reply.author?.displayName.slice(0, 20) + " (...)"}
|
||||
<div className='reply-container'>
|
||||
<div className='post-reply post-reply-desktop'>
|
||||
<div className='post-info'>
|
||||
<span className='nameblock'>
|
||||
{reply.author?.displayName ? (
|
||||
reply.author?.displayName.length > 20 ? (
|
||||
<Fragment>
|
||||
<span className='name' data-tooltip-id='tooltip' data-tooltip-content={reply.author?.displayName} data-tooltip-place='top'>
|
||||
{reply.author?.displayName.slice(0, 20) + ' (...)'}
|
||||
</span>
|
||||
</Fragment>
|
||||
: <span className="name">
|
||||
{reply.author?.displayName}</span>
|
||||
: <span className="name">
|
||||
Anonymous</span>}
|
||||
|
||||
<span className="poster-address address-desktop"
|
||||
id="reply-button" style={{cursor: "pointer"}}
|
||||
>
|
||||
(u/
|
||||
{reply.author?.shortAddress ?
|
||||
(
|
||||
<span >
|
||||
{reply.author?.shortAddress}
|
||||
</span>
|
||||
) : (
|
||||
<span >
|
||||
{account?.author?.shortAddress}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
<span className='name'>{reply.author?.displayName}</span>
|
||||
)
|
||||
) : (
|
||||
<span className='name'>Anonymous</span>
|
||||
)}
|
||||
|
||||
<span className='poster-address address-desktop' id='reply-button' style={{ cursor: 'pointer' }}>
|
||||
(u/
|
||||
{reply.author?.shortAddress ? <span>{reply.author?.shortAddress}</span> : <span>{account?.author?.shortAddress}</span>})
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="date-time" data-utc="data">{getDate(reply.timestamp)}</span>
|
||||
<span className='date-time' data-utc='data'>
|
||||
{getDate(reply.timestamp)}
|
||||
</span>
|
||||
|
||||
<span className="post-number post-number-desktop">
|
||||
<span className='post-number post-number-desktop'>
|
||||
<span>c/</span>
|
||||
<Link to={() => {}} id="reply-button"
|
||||
title="Reply to this post">{reply.shortCid}</Link>
|
||||
</span>
|
||||
<div id="backlink-id" className="backlink">
|
||||
<Link to={() => {}} id='reply-button' title='Reply to this post'>
|
||||
{reply.shortCid}
|
||||
</Link>
|
||||
</span>
|
||||
|
||||
<div id='backlink-id' className='backlink'>
|
||||
{reply.replies?.pages?.topAll.comments
|
||||
.sort((a, b) => a.timestamp - b.timestamp)
|
||||
.map((reply, index) => (
|
||||
<div key={`div-${index}`} style={{ display: 'inline-block' }}>
|
||||
<Link key={`link-${index}`} to={() => {}}
|
||||
className="quote-link">
|
||||
c/{reply.shortCid}</Link>
|
||||
<Link key={`link-${index}`} to={() => {}} className='quote-link'>
|
||||
c/{reply.shortCid}
|
||||
</Link>
|
||||
|
||||
</div>
|
||||
))
|
||||
}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{replyMediaInfo?.url ? (
|
||||
<div className="file"
|
||||
style={{marginBottom: "5px"}}>
|
||||
<div className="reply-file-text">
|
||||
<div className='file' style={{ marginBottom: '5px' }}>
|
||||
<div className='reply-file-text'>
|
||||
Link:
|
||||
<a href={replyMediaInfo.url} target="_blank"
|
||||
rel="noopener noreferrer">{
|
||||
replyMediaInfo?.url.length > 30 ?
|
||||
replyMediaInfo?.url.slice(0, 30) + "(...)" :
|
||||
replyMediaInfo?.url
|
||||
}</a> ({replyMediaInfo?.type})
|
||||
<a href={replyMediaInfo.url} target='_blank' rel='noopener noreferrer'>
|
||||
{replyMediaInfo?.url.length > 30 ? replyMediaInfo?.url.slice(0, 30) + '(...)' : replyMediaInfo?.url}
|
||||
</a>
|
||||
({replyMediaInfo?.type})
|
||||
</div>
|
||||
{replyMediaInfo?.type === "webpage" ? (
|
||||
<div className="img-container">
|
||||
<span className="file-thumb-reply">
|
||||
{replyMediaInfo?.type === 'webpage' ? (
|
||||
<div className='img-container'>
|
||||
<span className='file-thumb-reply'>
|
||||
{reply.thumbnailUrl ? (
|
||||
<img
|
||||
src={replyMediaInfo.thumbnail} alt={replyMediaInfo.type}
|
||||
style={{cursor: "pointer"}}
|
||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||
src={replyMediaInfo.thumbnail}
|
||||
alt={replyMediaInfo.type}
|
||||
style={{ cursor: 'pointer' }}
|
||||
onError={(e) => (e.target.src = fallbackImgUrl)}
|
||||
/>
|
||||
) : null}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
{replyMediaInfo?.type === "image" ? (
|
||||
<div className="img-container">
|
||||
<span className="file-thumb-reply">
|
||||
<img
|
||||
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
||||
style={{cursor: "pointer"}}
|
||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||
{replyMediaInfo?.type === 'image' ? (
|
||||
<div className='img-container'>
|
||||
<span className='file-thumb-reply'>
|
||||
<img src={replyMediaInfo.url} alt={replyMediaInfo.type} style={{ cursor: 'pointer' }} onError={(e) => (e.target.src = fallbackImgUrl)} />
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
{replyMediaInfo?.type === "video" ? (
|
||||
<span className="file-thumb-reply">
|
||||
<video controls
|
||||
|
||||
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||
{replyMediaInfo?.type === 'video' ? (
|
||||
<span className='file-thumb-reply'>
|
||||
<video controls src={replyMediaInfo.url} alt={replyMediaInfo.type} onError={(e) => (e.target.src = fallbackImgUrl)} />
|
||||
</span>
|
||||
) : null}
|
||||
{replyMediaInfo?.type === "audio" ? (
|
||||
<span className="file-thumb-reply">
|
||||
<audio controls
|
||||
|
||||
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||
{replyMediaInfo?.type === 'audio' ? (
|
||||
<span className='file-thumb-reply'>
|
||||
<audio controls src={replyMediaInfo.url} alt={replyMediaInfo.type} onError={(e) => (e.target.src = fallbackImgUrl)} />
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{reply.content ? (
|
||||
reply.content?.length > 500 ?
|
||||
reply.content?.length > 500 ? (
|
||||
<Fragment>
|
||||
<blockquote comment={reply} className="post-message">
|
||||
<blockquote comment={reply} className='post-message'>
|
||||
{shortParentCid ? (
|
||||
<Link to={() => {}} className="quotelink">
|
||||
{`c/${shortParentCid}`}{shortParentCid === thread.shortCid ? " (OP)" : null}
|
||||
<Link to={() => {}} className='quotelink'>
|
||||
{`c/${shortParentCid}`}
|
||||
{shortParentCid === thread.shortCid ? ' (OP)' : null}
|
||||
</Link>
|
||||
) : null}
|
||||
<Post content={reply.content?.slice(0, 500)} />
|
||||
<span className="ttl"> (...)
|
||||
<br /> <EditLabel
|
||||
commentCid={reply.cid}
|
||||
className="ttl"/><br />
|
||||
Comment too long. Click the link to view.</span>
|
||||
<span className='ttl'>
|
||||
{' '}
|
||||
(...)
|
||||
<br /> <EditLabel commentCid={reply.cid} className='ttl' />
|
||||
<br />
|
||||
Comment too long. Click the link to view.
|
||||
</span>
|
||||
</blockquote>
|
||||
</Fragment>
|
||||
: <blockquote className="post-message">
|
||||
) : (
|
||||
<blockquote className='post-message'>
|
||||
{shortParentCid ? (
|
||||
<Link to={() => {}} className="quotelink">
|
||||
{`c/${shortParentCid}`}{shortParentCid === thread.shortCid ? " (OP)" : null}
|
||||
<Link to={() => {}} className='quotelink'>
|
||||
{`c/${shortParentCid}`}
|
||||
{shortParentCid === thread.shortCid ? ' (OP)' : null}
|
||||
</Link>
|
||||
) : null}
|
||||
<Post content={reply.content} comment={reply} />
|
||||
<EditLabel
|
||||
commentCid={reply.cid}
|
||||
className="ttl"/>
|
||||
</blockquote>)
|
||||
: null}
|
||||
<EditLabel commentCid={reply.cid} className='ttl' />
|
||||
</blockquote>
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="reply-container">
|
||||
<span className="ellipsis">{stateString}</span>
|
||||
<div className='reply-container'>
|
||||
<span className='ellipsis'>{stateString}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="thread-mobile">
|
||||
<div className='thread-mobile'>
|
||||
{reply.state === 'succeeded' ? (
|
||||
<div className="reply-container">
|
||||
<div className="post-reply post-reply-mobile">
|
||||
<div className="post-info-mobile">
|
||||
<span className="name-block-mobile">
|
||||
{reply.author?.displayName
|
||||
? reply.author?.displayName.length > 20
|
||||
? <Fragment>
|
||||
<span className="name-mobile">
|
||||
{reply.author?.displayName.slice(0, 20) + " (...)"}
|
||||
</span>
|
||||
<div className='reply-container'>
|
||||
<div className='post-reply post-reply-mobile'>
|
||||
<div className='post-info-mobile'>
|
||||
<span className='name-block-mobile'>
|
||||
{reply.author?.displayName ? (
|
||||
reply.author?.displayName.length > 20 ? (
|
||||
<Fragment>
|
||||
<span className='name-mobile'>{reply.author?.displayName.slice(0, 20) + ' (...)'}</span>
|
||||
</Fragment>
|
||||
: <span className="name-mobile">
|
||||
{reply.author?.displayName}</span>
|
||||
: <span className="name-mobile">
|
||||
Anonymous</span>}
|
||||
|
||||
<span className="poster-address-mobile address-mobile"
|
||||
id="reply-button" style={{cursor: "pointer"}}
|
||||
>
|
||||
(u/
|
||||
{reply.author?.shortAddress ?
|
||||
(
|
||||
<span className="highlight-address-mobile">
|
||||
{reply.author?.shortAddress}
|
||||
</span>
|
||||
) : (
|
||||
<span>
|
||||
{account?.author?.shortAddress}
|
||||
</span>
|
||||
<span className='name-mobile'>{reply.author?.displayName}</span>
|
||||
)
|
||||
}
|
||||
) : (
|
||||
<span className='name-mobile'>Anonymous</span>
|
||||
)}
|
||||
|
||||
<span className='poster-address-mobile address-mobile' id='reply-button' style={{ cursor: 'pointer' }}>
|
||||
(u/
|
||||
{reply.author?.shortAddress ? (
|
||||
<span className='highlight-address-mobile'>{reply.author?.shortAddress}</span>
|
||||
) : (
|
||||
<span>{account?.author?.shortAddress}</span>
|
||||
)}
|
||||
)
|
||||
</span>
|
||||
<br />
|
||||
</span>
|
||||
<span className="date-time-mobile post-number-mobile">
|
||||
<span className='date-time-mobile post-number-mobile'>
|
||||
{getDate(reply.timestamp)}
|
||||
<span>c/</span>
|
||||
<Link to={() => {}} id="reply-button"
|
||||
>{reply.shortCid}
|
||||
<Link to={() => {}} id='reply-button'>
|
||||
{reply.shortCid}
|
||||
</Link>
|
||||
</span>
|
||||
</div>
|
||||
{reply.link ? (
|
||||
<div className="file-mobile">
|
||||
<div className='file-mobile'>
|
||||
{replyMediaInfo?.url ? (
|
||||
replyMediaInfo.type === "webpage" ? (
|
||||
<div className="img-container">
|
||||
<span className="file-thumb-mobile">
|
||||
replyMediaInfo.type === 'webpage' ? (
|
||||
<div className='img-container'>
|
||||
<span className='file-thumb-mobile'>
|
||||
{reply.thumbnailUrl ? (
|
||||
<img
|
||||
src={replyMediaInfo.thumbnail} alt="thumbnail"
|
||||
style={{cursor: "pointer"}}
|
||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||
<img src={replyMediaInfo.thumbnail} alt='thumbnail' style={{ cursor: 'pointer' }} onError={(e) => (e.target.src = fallbackImgUrl)} />
|
||||
) : null}
|
||||
<div className="file-info-mobile">{replyMediaInfo.type}</div>
|
||||
<div className='file-info-mobile'>{replyMediaInfo.type}</div>
|
||||
</span>
|
||||
</div>
|
||||
) : replyMediaInfo.type === "image" ? (
|
||||
<div className="img-container">
|
||||
<span className="file-thumb-mobile">
|
||||
<img
|
||||
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
||||
style={{cursor: "pointer"}}
|
||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||
<div className="file-info-mobile">{replyMediaInfo.type}</div>
|
||||
) : replyMediaInfo.type === 'image' ? (
|
||||
<div className='img-container'>
|
||||
<span className='file-thumb-mobile'>
|
||||
<img src={replyMediaInfo.url} alt={replyMediaInfo.type} style={{ cursor: 'pointer' }} onError={(e) => (e.target.src = fallbackImgUrl)} />
|
||||
<div className='file-info-mobile'>{replyMediaInfo.type}</div>
|
||||
</span>
|
||||
</div>
|
||||
) : replyMediaInfo.type === "video" ? (
|
||||
<span className="file-thumb-mobile">
|
||||
) : replyMediaInfo.type === 'video' ? (
|
||||
<span className='file-thumb-mobile'>
|
||||
<video
|
||||
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
||||
style={{ pointerEvents: "none" }}
|
||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||
<div className="file-info-mobile">{replyMediaInfo.type}</div>
|
||||
src={replyMediaInfo.url}
|
||||
alt={replyMediaInfo.type}
|
||||
style={{ pointerEvents: 'none' }}
|
||||
onError={(e) => (e.target.src = fallbackImgUrl)}
|
||||
/>
|
||||
<div className='file-info-mobile'>{replyMediaInfo.type}</div>
|
||||
</span>
|
||||
) : replyMediaInfo.type === "audio" ? (
|
||||
<span className="file-thumb-mobile">
|
||||
<audio
|
||||
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||
<div className="file-info-mobile">{replyMediaInfo.type}</div>
|
||||
) : replyMediaInfo.type === 'audio' ? (
|
||||
<span className='file-thumb-mobile'>
|
||||
<audio src={replyMediaInfo.url} alt={replyMediaInfo.type} onError={(e) => (e.target.src = fallbackImgUrl)} />
|
||||
<div className='file-info-mobile'>{replyMediaInfo.type}</div>
|
||||
</span>
|
||||
) : null
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{reply.content ? (
|
||||
reply.content?.length > 500 ?
|
||||
reply.content?.length > 500 ? (
|
||||
<Fragment>
|
||||
<blockquote className="post-message">
|
||||
<blockquote className='post-message'>
|
||||
{shortParentCid ? (
|
||||
<Link to={() => {}} className="quotelink">
|
||||
{`c/${shortParentCid}`}{shortParentCid === thread.shortCid ? " (OP)" : null}
|
||||
<Link to={() => {}} className='quotelink'>
|
||||
{`c/${shortParentCid}`}
|
||||
{shortParentCid === thread.shortCid ? ' (OP)' : null}
|
||||
</Link>
|
||||
) : null}
|
||||
<Post content={reply.content?.slice(0, 500)} comment={reply} />
|
||||
<span className="ttl"> (...)
|
||||
<span className='ttl'>
|
||||
{' '}
|
||||
(...)
|
||||
<br />
|
||||
<EditLabel
|
||||
commentCid={reply.cid}
|
||||
className="ttl"/>
|
||||
<EditLabel commentCid={reply.cid} className='ttl' />
|
||||
<br />
|
||||
Comment too long. Click the link to view. </span>
|
||||
Comment too long. Click the link to view.{' '}
|
||||
</span>
|
||||
</blockquote>
|
||||
</Fragment>
|
||||
: <blockquote className="post-message">
|
||||
) : (
|
||||
<blockquote className='post-message'>
|
||||
{shortParentCid ? (
|
||||
<Link to={() => {}} className="quotelink" >
|
||||
{`c/${shortParentCid}`}{shortParentCid === thread.shortCid ? " (OP)" : null}
|
||||
<Link to={() => {}} className='quotelink'>
|
||||
{`c/${shortParentCid}`}
|
||||
{shortParentCid === thread.shortCid ? ' (OP)' : null}
|
||||
</Link>
|
||||
) : null}
|
||||
<Post content={reply.content} comment={reply} />
|
||||
<EditLabel
|
||||
commentCid={reply.cid}
|
||||
className="ttl"/>
|
||||
</blockquote>)
|
||||
: null}
|
||||
<EditLabel commentCid={reply.cid} className='ttl' />
|
||||
</blockquote>
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="reply-container">
|
||||
<span className="ellipsis">{stateString}</span>
|
||||
<div className='reply-container'>
|
||||
<span className='ellipsis'>{stateString}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -316,6 +291,6 @@ const PostOnHover = ({ cid, feed }) => {
|
||||
</BoardForm>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default PostOnHover;
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { useAccountComment } from "@plebbit/plebbit-react-hooks";
|
||||
import useStateString from "../hooks/useStateString";
|
||||
import React from 'react';
|
||||
import { useAccountComment } from '@plebbit/plebbit-react-hooks';
|
||||
import useStateString from '../hooks/useStateString';
|
||||
|
||||
const StateLabel = ({ commentIndex, className }) => {
|
||||
const comment = useAccountComment({ commentIndex: commentIndex });
|
||||
@@ -10,7 +10,7 @@ const StateLabel = ({ commentIndex, className }) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (comment.state === "failed" || comment.state === "succeeded") {
|
||||
if (comment.state === 'failed' || comment.state === 'succeeded') {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -19,13 +19,8 @@ const StateLabel = ({ commentIndex, className }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<span className="ttl">
|
||||
<br />
|
||||
(
|
||||
<span className={className}>
|
||||
{stateString}
|
||||
</span>
|
||||
)
|
||||
<span className='ttl'>
|
||||
<br />(<span className={className}>{stateString}</span>)
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from "react";
|
||||
import { useAccount } from "@plebbit/plebbit-react-hooks";
|
||||
import useAnonModeStore from "./stores/useAnonModeStore";
|
||||
import { useEffect } from 'react';
|
||||
import { useAccount } from '@plebbit/plebbit-react-hooks';
|
||||
import useAnonModeStore from './stores/useAnonModeStore';
|
||||
|
||||
const useAnonMode = (threadCid, execute) => {
|
||||
const account = useAccount();
|
||||
@@ -18,13 +18,12 @@ useEffect(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleAnonMode();
|
||||
}, [threadCid, execute, account, anonymousMode]);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
export default useAnonMode;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from "react";
|
||||
import { useAccount } from "@plebbit/plebbit-react-hooks";
|
||||
import useAnonModeStore from "./stores/useAnonModeStore";
|
||||
import { useEffect } from 'react';
|
||||
import { useAccount } from '@plebbit/plebbit-react-hooks';
|
||||
import useAnonModeStore from './stores/useAnonModeStore';
|
||||
|
||||
const useAnonModeRef = (threadCidRef, execute) => {
|
||||
const account = useAccount();
|
||||
@@ -18,11 +18,11 @@ const useAnonModeRef = (threadCidRef, execute) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleAnonMode();
|
||||
}, [threadCidRef, execute, account, anonymousMode]);
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
export default useAnonModeRef;
|
||||
@@ -1,4 +1,4 @@
|
||||
import useGeneralStore from "./stores/useGeneralStore";
|
||||
import useGeneralStore from './stores/useGeneralStore';
|
||||
|
||||
const useClickForm = () => {
|
||||
const { setShowPostForm, setShowPostFormLink } = useGeneralStore.getState();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
const useError = () => {
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
@@ -9,14 +9,14 @@ const useError = () => {
|
||||
if (errorMessage && errorMessage.length > 0) {
|
||||
const showErrorToast = () => {
|
||||
const toastId = toast.error(errorMessage.toString(), {
|
||||
position: "top-right",
|
||||
position: 'top-right',
|
||||
autoClose: false,
|
||||
hideProgressBar: true,
|
||||
closeOnClick: false,
|
||||
pauseOnHover: false,
|
||||
draggable: false,
|
||||
progress: undefined,
|
||||
theme: "dark",
|
||||
theme: 'dark',
|
||||
});
|
||||
|
||||
return () => {
|
||||
@@ -43,10 +43,9 @@ const useError = () => {
|
||||
}
|
||||
|
||||
setErrorMessage(message);
|
||||
setRenderCount(prevCount => prevCount + 1);
|
||||
setRenderCount((prevCount) => prevCount + 1);
|
||||
};
|
||||
|
||||
|
||||
return [errorMessage, setNewErrorMessage];
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useRef } from "react"
|
||||
import { useMemo, useRef } from 'react';
|
||||
|
||||
const useFeedRows = (feedWithDescriptionAndRules, columnCount) => {
|
||||
const rowsRef = useRef([]);
|
||||
@@ -6,15 +6,14 @@ const useFeedRows = (feedWithDescriptionAndRules, columnCount) => {
|
||||
const rows = [];
|
||||
for (let i = 0; i < feedWithDescriptionAndRules.length; i += columnCount) {
|
||||
if (rowsRef.current?.[rows.length] && rowsRef.current[rows.length].length === columnCount) {
|
||||
rows.push(rowsRef.current[rows.length])
|
||||
}
|
||||
else {
|
||||
rows.push(feedWithDescriptionAndRules.slice(i, i + columnCount))
|
||||
rows.push(rowsRef.current[rows.length]);
|
||||
} else {
|
||||
rows.push(feedWithDescriptionAndRules.slice(i, i + columnCount));
|
||||
}
|
||||
}
|
||||
rowsRef.current = rows
|
||||
rowsRef.current = rows;
|
||||
return rows;
|
||||
}, [feedWithDescriptionAndRules, columnCount]);
|
||||
}
|
||||
};
|
||||
|
||||
export default useFeedRows;
|
||||
@@ -1,27 +1,26 @@
|
||||
import { useMemo } from "react"
|
||||
import { useMemo } from 'react';
|
||||
|
||||
const useFeedStateString = (subplebbits) => {
|
||||
return useMemo(() => {
|
||||
const getClientHost = (clientUrl) => {
|
||||
try {
|
||||
clientUrl = new URL(clientUrl).hostname || clientUrl
|
||||
}
|
||||
catch (e) {}
|
||||
return clientUrl
|
||||
}
|
||||
clientUrl = new URL(clientUrl).hostname || clientUrl;
|
||||
} catch (e) {}
|
||||
return clientUrl;
|
||||
};
|
||||
const getClientUrls = (regex) => {
|
||||
const clientUrls = new Set()
|
||||
const addClientUrl = (client, clientUrl) => client?.state?.match?.(regex) && clientUrls.add(getClientHost(clientUrl))
|
||||
const clientUrls = new Set();
|
||||
const addClientUrl = (client, clientUrl) => client?.state?.match?.(regex) && clientUrls.add(getClientHost(clientUrl));
|
||||
for (const subplebbit of subplebbits) {
|
||||
for (const clientUrl in subplebbit?.clients?.ipfsGateways) {
|
||||
addClientUrl(subplebbit.clients.ipfsGateways[clientUrl], clientUrl)
|
||||
addClientUrl(subplebbit.clients.ipfsGateways[clientUrl], clientUrl);
|
||||
}
|
||||
for (const clientUrl in subplebbit?.clients?.ipfsClients) {
|
||||
addClientUrl(subplebbit.clients.ipfsClients[clientUrl], clientUrl)
|
||||
addClientUrl(subplebbit.clients.ipfsClients[clientUrl], clientUrl);
|
||||
}
|
||||
for (const chainTicker in subplebbit?.clients?.chainProviders) {
|
||||
for (const clientUrl in subplebbit.clients.chainProviders[chainTicker]) {
|
||||
addClientUrl(subplebbit.clients.chainProviders[chainTicker][clientUrl], clientUrl)
|
||||
addClientUrl(subplebbit.clients.chainProviders[chainTicker][clientUrl], clientUrl);
|
||||
}
|
||||
}
|
||||
// find subplebbit pages states
|
||||
@@ -29,68 +28,68 @@ const useFeedStateString = (subplebbits) => {
|
||||
for (const clientType in subplebbit.posts.clients) {
|
||||
for (const sortType in subplebbit.posts.clients[clientType]) {
|
||||
for (const clientUrl in subplebbit.posts.clients[clientType][sortType]) {
|
||||
addClientUrl(subplebbit.posts.clients[clientType][sortType][clientUrl], clientUrl)
|
||||
addClientUrl(subplebbit.posts.clients[clientType][sortType][clientUrl], clientUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...clientUrls]
|
||||
}
|
||||
return [...clientUrls];
|
||||
};
|
||||
|
||||
if (!subplebbits) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const states = {}
|
||||
const states = {};
|
||||
for (const subplebbit of subplebbits) {
|
||||
states[subplebbit?.updatingState] = (states[subplebbit?.updatingState] || 0) + 1
|
||||
states[subplebbit?.updatingState] = (states[subplebbit?.updatingState] || 0) + 1;
|
||||
// find subplebbit pages states
|
||||
for (const clientType in subplebbit?.posts?.clients) {
|
||||
for (const sortType in subplebbit.posts.clients[clientType]) {
|
||||
for (const clientUrl in subplebbit.posts.clients[clientType][sortType]) {
|
||||
const state = subplebbit.posts.clients[clientType][sortType][clientUrl].state
|
||||
states[state] = (states[state] || 0) + 1
|
||||
const state = subplebbit.posts.clients[clientType][sortType][clientUrl].state;
|
||||
states[state] = (states[state] || 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// e.g. Resolving 2 addresses from infura.io, fetching 2 IPNS, 1 IPFS from cloudflare-ipfs.com, ipfs.io
|
||||
let stateString = ''
|
||||
let stateString = '';
|
||||
if (states['resolving-address']) {
|
||||
stateString += `resolving ${states['resolving-address']} addresses`
|
||||
const clientUrls = getClientUrls(/address/)
|
||||
stateString += `resolving ${states['resolving-address']} addresses`;
|
||||
const clientUrls = getClientUrls(/address/);
|
||||
if (clientUrls.length) {
|
||||
stateString += ` from ${clientUrls.join(', ')}`
|
||||
stateString += ` from ${clientUrls.join(', ')}`;
|
||||
}
|
||||
}
|
||||
if (states['fetching-ipns'] || states['fetching-ipfs']) {
|
||||
if (stateString) {
|
||||
stateString += ', '
|
||||
stateString += ', ';
|
||||
}
|
||||
stateString += `fetching `
|
||||
stateString += `fetching `;
|
||||
if (states['fetching-ipns']) {
|
||||
stateString += `${states['fetching-ipns']} IPNS`
|
||||
stateString += `${states['fetching-ipns']} IPNS`;
|
||||
}
|
||||
if (states['fetching-ipfs']) {
|
||||
if (states['fetching-ipns']) {
|
||||
stateString += ', '
|
||||
stateString += ', ';
|
||||
}
|
||||
stateString += `${states['fetching-ipfs']} IPFS`
|
||||
stateString += `${states['fetching-ipfs']} IPFS`;
|
||||
}
|
||||
const clientUrls = getClientUrls(/ipfs|ipns/)
|
||||
const clientUrls = getClientUrls(/ipfs|ipns/);
|
||||
if (clientUrls.length) {
|
||||
stateString += ` from ${clientUrls.join(', ')}`
|
||||
stateString += ` from ${clientUrls.join(', ')}`;
|
||||
}
|
||||
}
|
||||
|
||||
// capitalize first letter
|
||||
stateString = stateString.charAt(0).toUpperCase() + stateString.slice(1)
|
||||
stateString = stateString.charAt(0).toUpperCase() + stateString.slice(1);
|
||||
|
||||
// if string is empty, return undefined instead
|
||||
return stateString || undefined
|
||||
}, [subplebbits])
|
||||
}
|
||||
return stateString || undefined;
|
||||
}, [subplebbits]);
|
||||
};
|
||||
|
||||
export default useFeedStateString
|
||||
export default useFeedStateString;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
const useInfo = () => {
|
||||
const [infoMessage, setInfoMessage] = useState('');
|
||||
@@ -9,14 +9,14 @@ const useInfo = () => {
|
||||
if (infoMessage && infoMessage.length > 0) {
|
||||
const showInfoToast = () => {
|
||||
const toastId = toast.info(infoMessage.toString(), {
|
||||
position: "top-right",
|
||||
position: 'top-right',
|
||||
autoClose: false,
|
||||
hideProgressBar: false,
|
||||
closeOnClick: false,
|
||||
pauseOnHover: false,
|
||||
draggable: false,
|
||||
progress: undefined,
|
||||
theme: "dark",
|
||||
theme: 'dark',
|
||||
});
|
||||
|
||||
return () => {
|
||||
@@ -34,7 +34,7 @@ const useInfo = () => {
|
||||
|
||||
const setNewInfoMessage = (message) => {
|
||||
setInfoMessage(message);
|
||||
setRenderCount(prevCount => prevCount + 1);
|
||||
setRenderCount((prevCount) => prevCount + 1);
|
||||
};
|
||||
|
||||
return [infoMessage, setNewInfoMessage];
|
||||
|
||||
+38
-40
@@ -1,39 +1,39 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useMemo } from 'react';
|
||||
|
||||
const useStateString = (commentOrSubplebbit) => {
|
||||
return useMemo(() => {
|
||||
// dont show state string if the data is already fetched
|
||||
if (commentOrSubplebbit?.updatedAt || commentOrSubplebbit?.state === 'succeeded') {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (!commentOrSubplebbit?.clients) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
const clients = commentOrSubplebbit?.clients
|
||||
const clients = commentOrSubplebbit?.clients;
|
||||
|
||||
const states = {}
|
||||
const states = {};
|
||||
const addState = (state, clientUrl) => {
|
||||
if (!state || state === 'stopped') {
|
||||
return
|
||||
return;
|
||||
}
|
||||
if (!states[state]) {
|
||||
states[state] = []
|
||||
}
|
||||
states[state].push(clientUrl)
|
||||
states[state] = [];
|
||||
}
|
||||
states[state].push(clientUrl);
|
||||
};
|
||||
for (const clientUrl in clients?.ipfsGateways) {
|
||||
addState(clients.ipfsGateways[clientUrl]?.state, clientUrl)
|
||||
addState(clients.ipfsGateways[clientUrl]?.state, clientUrl);
|
||||
}
|
||||
for (const clientUrl in clients?.ipfsClients) {
|
||||
addState(clients.ipfsClients[clientUrl]?.state, clientUrl)
|
||||
addState(clients.ipfsClients[clientUrl]?.state, clientUrl);
|
||||
}
|
||||
for (const clientUrl in clients?.pubsubClients) {
|
||||
addState(clients.pubsubClients[clientUrl]?.state, clientUrl)
|
||||
addState(clients.pubsubClients[clientUrl]?.state, clientUrl);
|
||||
}
|
||||
for (const chainTicker in clients?.chainProviders) {
|
||||
for (const clientUrl in clients.chainProviders[chainTicker]) {
|
||||
addState(clients.chainProviders[chainTicker][clientUrl]?.state, clientUrl)
|
||||
addState(clients.chainProviders[chainTicker][clientUrl]?.state, clientUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,15 +42,15 @@ const useStateString = (commentOrSubplebbit) => {
|
||||
for (const clientType in commentOrSubplebbit.posts.clients) {
|
||||
for (const sortType in commentOrSubplebbit.posts.clients[clientType]) {
|
||||
for (const clientUrl in commentOrSubplebbit.posts.clients[clientType][sortType]) {
|
||||
let state = commentOrSubplebbit.posts.clients[clientType][sortType][clientUrl].state
|
||||
let state = commentOrSubplebbit.posts.clients[clientType][sortType][clientUrl].state;
|
||||
if (state === 'stopped') {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
state += `-page-${sortType}`
|
||||
state += `-page-${sortType}`;
|
||||
if (!states[state]) {
|
||||
states[state] = []
|
||||
states[state] = [];
|
||||
}
|
||||
states[state].push(clientUrl)
|
||||
states[state].push(clientUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,53 +58,51 @@ const useStateString = (commentOrSubplebbit) => {
|
||||
|
||||
const getClientHost = (clientUrl) => {
|
||||
try {
|
||||
clientUrl = new URL(clientUrl).hostname || clientUrl
|
||||
}
|
||||
catch (e) {}
|
||||
return clientUrl
|
||||
}
|
||||
clientUrl = new URL(clientUrl).hostname || clientUrl;
|
||||
} catch (e) {}
|
||||
return clientUrl;
|
||||
};
|
||||
|
||||
let stateString = ''
|
||||
let stateString = '';
|
||||
for (const state in states) {
|
||||
const clientUrls = states[state]
|
||||
const clientHosts = clientUrls.map(clientUrl => getClientHost(clientUrl))
|
||||
const clientUrls = states[state];
|
||||
const clientHosts = clientUrls.map((clientUrl) => getClientHost(clientUrl));
|
||||
|
||||
// if there are no valid hosts, skip this state
|
||||
if (clientHosts.length === 0) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
// separate 2 different states using ', '
|
||||
if (stateString) {
|
||||
stateString += ', '
|
||||
stateString += ', ';
|
||||
}
|
||||
|
||||
// e.g. 'fetching IPFS from cloudflare-ipfs.com, ipfs.io'
|
||||
const formattedState = state.replaceAll('-', ' ').replace('ipfs', 'IPFS').replace('ipns', 'IPNS')
|
||||
stateString += `${formattedState} from ${clientHosts.join(', ')}`
|
||||
const formattedState = state.replaceAll('-', ' ').replace('ipfs', 'IPFS').replace('ipns', 'IPNS');
|
||||
stateString += `${formattedState} from ${clientHosts.join(', ')}`;
|
||||
}
|
||||
|
||||
// fallback to comment or subplebbit state when possible
|
||||
if (!stateString) {
|
||||
if (commentOrSubplebbit?.publishingState && commentOrSubplebbit?.publishingState !== 'stopped' && commentOrSubplebbit?.publishingState !== 'succeeded') {
|
||||
stateString = commentOrSubplebbit.publishingState
|
||||
}
|
||||
else if (commentOrSubplebbit?.updatingState !== 'stopped' && commentOrSubplebbit?.updatingState !== 'succeeded') {
|
||||
stateString = commentOrSubplebbit.updatingState
|
||||
stateString = commentOrSubplebbit.publishingState;
|
||||
} else if (commentOrSubplebbit?.updatingState !== 'stopped' && commentOrSubplebbit?.updatingState !== 'succeeded') {
|
||||
stateString = commentOrSubplebbit.updatingState;
|
||||
}
|
||||
if (stateString) {
|
||||
stateString = stateString.replaceAll('-', ' ').replace('ipfs', 'IPFS').replace('ipns', 'IPNS')
|
||||
stateString = stateString.replaceAll('-', ' ').replace('ipfs', 'IPFS').replace('ipns', 'IPNS');
|
||||
}
|
||||
}
|
||||
|
||||
// capitalize first letter
|
||||
if (stateString) {
|
||||
stateString = stateString.charAt(0).toUpperCase() + stateString.slice(1)
|
||||
stateString = stateString.charAt(0).toUpperCase() + stateString.slice(1);
|
||||
}
|
||||
|
||||
// if string is empty, return undefined instead
|
||||
return stateString === '' ? undefined : stateString
|
||||
}, [commentOrSubplebbit])
|
||||
}
|
||||
return stateString === '' ? undefined : stateString;
|
||||
}, [commentOrSubplebbit]);
|
||||
};
|
||||
|
||||
export default useStateString
|
||||
export default useStateString;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
const useSuccess = () => {
|
||||
const [successMessage, setSuccessMessage] = useState('');
|
||||
@@ -9,14 +9,14 @@ const useSuccess = () => {
|
||||
if (successMessage && successMessage.length > 0) {
|
||||
const showSuccessToast = () => {
|
||||
const toastId = toast.success(successMessage.toString(), {
|
||||
position: "top-right",
|
||||
position: 'top-right',
|
||||
autoClose: 3000,
|
||||
hideProgressBar: false,
|
||||
closeOnClick: false,
|
||||
pauseOnHover: false,
|
||||
draggable: false,
|
||||
progress: undefined,
|
||||
theme: "dark",
|
||||
theme: 'dark',
|
||||
});
|
||||
|
||||
return () => {
|
||||
@@ -34,7 +34,7 @@ const useSuccess = () => {
|
||||
|
||||
const setNewSuccessMessage = (message) => {
|
||||
setSuccessMessage(message);
|
||||
setRenderCount(prevCount => prevCount + 1);
|
||||
setRenderCount((prevCount) => prevCount + 1);
|
||||
};
|
||||
|
||||
return [successMessage, setNewSuccessMessage];
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
export default function useWindowWidth() {
|
||||
const [windowWidth, setWindowWidth] = useState(window.innerWidth)
|
||||
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
|
||||
|
||||
useEffect(() => {
|
||||
function handleResize() {
|
||||
setWindowWidth(window.innerWidth)
|
||||
setWindowWidth(window.innerWidth);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', handleResize)
|
||||
return () => window.removeEventListener('resize', handleResize)
|
||||
}, [])
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, []);
|
||||
|
||||
return windowWidth
|
||||
return windowWidth;
|
||||
}
|
||||
@@ -3,7 +3,6 @@ function countLinks(comment) {
|
||||
|
||||
if (comment.replyCount > 0) {
|
||||
for (let reply of comment.replies.pages.topAll.comments) {
|
||||
|
||||
if (reply.link) {
|
||||
linkCount++;
|
||||
}
|
||||
|
||||
@@ -15,11 +15,9 @@ const getCommentMediaInfo = (comment) => {
|
||||
if (['youtube.com', 'www.youtube.com', 'youtu.be'].includes(host)) {
|
||||
const videoId = host === 'youtu.be' ? url.pathname.slice(1) : url.searchParams.get('v');
|
||||
scrapedThumbnailUrl = `https://img.youtube.com/vi/${videoId}/sddefault.jpg`;
|
||||
|
||||
} else if (host.includes('bitchute.com')) {
|
||||
const videoId = url.pathname.split('/')[2];
|
||||
scrapedThumbnailUrl = `https://static-3.bitchute.com/live/cover_images/F61vWF4shy8s/${videoId}_640x360.jpg`;
|
||||
|
||||
} else if (host.includes('streamable.com')) {
|
||||
const videoId = url.pathname.split('/')[1];
|
||||
scrapedThumbnailUrl = `https://cdn-cf-east.streamable.com/image/${videoId}.jpg`;
|
||||
|
||||
+10
-13
@@ -1,6 +1,6 @@
|
||||
const getDate = (commentTimestamp) => {
|
||||
if (commentTimestamp === undefined || isNaN(commentTimestamp)) {
|
||||
return "";
|
||||
return '';
|
||||
}
|
||||
const locale = Intl.DateTimeFormat().resolvedOptions().locale;
|
||||
const string = new Intl.DateTimeFormat(locale, {
|
||||
@@ -11,28 +11,25 @@ const getDate = (commentTimestamp) => {
|
||||
weekday: 'short',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
second: '2-digit',
|
||||
}).format(new Date(commentTimestamp * 1000));
|
||||
if (locale.startsWith('ar')) {
|
||||
return string
|
||||
return string;
|
||||
}
|
||||
const items = string.split(/,* /)
|
||||
const items = string.split(/,* /);
|
||||
if (items.length === 3) {
|
||||
const itemIsNumber = [
|
||||
items[0][0].match(/[0-9]/),
|
||||
items[1][0].match(/[0-9]/)
|
||||
]
|
||||
const itemIsNumber = [items[0][0].match(/[0-9]/), items[1][0].match(/[0-9]/)];
|
||||
if (itemIsNumber[0] && itemIsNumber[1]) {
|
||||
return `${items[0]}(${items[2]})${items[1]}`
|
||||
return `${items[0]}(${items[2]})${items[1]}`;
|
||||
}
|
||||
if (itemIsNumber[0] && !itemIsNumber[1]) {
|
||||
return `${items[0]}(${items[1]})${items[2]}`
|
||||
return `${items[0]}(${items[1]})${items[2]}`;
|
||||
}
|
||||
if (!itemIsNumber[0] && itemIsNumber[1]) {
|
||||
return `${items[1]}(${items[0]})${items[2]}`
|
||||
return `${items[1]}(${items[0]})${items[2]}`;
|
||||
}
|
||||
}
|
||||
return string
|
||||
}
|
||||
return string;
|
||||
};
|
||||
|
||||
export default getDate;
|
||||
@@ -5,7 +5,7 @@ const pluralize = (unit, value) => {
|
||||
const getFormattedTime = (timestamp) => {
|
||||
try {
|
||||
const currentTime = new Date().getTime();
|
||||
const differenceInMilliseconds = currentTime - (timestamp * 1000);
|
||||
const differenceInMilliseconds = currentTime - timestamp * 1000;
|
||||
|
||||
const years = Math.floor(differenceInMilliseconds / (1000 * 60 * 60 * 24 * 365.25));
|
||||
const months = Math.floor((differenceInMilliseconds % (1000 * 60 * 60 * 24 * 365.25)) / (1000 * 60 * 60 * 24 * 30));
|
||||
@@ -33,12 +33,11 @@ const getFormattedTime = (timestamp) => {
|
||||
} else if (seconds > 30) {
|
||||
return `${pluralize('second', seconds)} ago`;
|
||||
} else {
|
||||
return "just now";
|
||||
return 'just now';
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error("Error in getFormattedTime:", e);
|
||||
return "[error]";
|
||||
} catch (e) {
|
||||
console.error('Error in getFormattedTime:', e);
|
||||
return '[error]';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ function handleAddressClick(shortAddress) {
|
||||
const postReplySelector = isMobile ? '.post-reply-mobile' : '.post-reply-desktop';
|
||||
const opSelector = isMobile ? '.op-mobile' : '.op-desktop';
|
||||
|
||||
const matchingElements = [...document.querySelectorAll(postReplySelector + ',' + opSelector)].filter(el => {
|
||||
const matchingElements = [...document.querySelectorAll(postReplySelector + ',' + opSelector)].filter((el) => {
|
||||
const addressElement = el.querySelector(addressSelector);
|
||||
return addressElement && addressElement.textContent.includes(shortAddress);
|
||||
});
|
||||
@@ -16,18 +16,18 @@ function handleAddressClick(shortAddress) {
|
||||
const highlightedElements = document.querySelectorAll('.highlighted-address');
|
||||
|
||||
let allHighlighted = true;
|
||||
matchingElements.forEach(el => {
|
||||
matchingElements.forEach((el) => {
|
||||
if (!el.classList.contains('highlighted-address')) {
|
||||
allHighlighted = false;
|
||||
}
|
||||
});
|
||||
|
||||
highlightedElements.forEach(el => {
|
||||
highlightedElements.forEach((el) => {
|
||||
el.classList.remove('highlighted-address');
|
||||
});
|
||||
|
||||
if (!allHighlighted) {
|
||||
matchingElements.forEach(el => {
|
||||
matchingElements.forEach((el) => {
|
||||
if (!el.classList.contains('op-mobile') && !el.classList.contains('op-desktop')) {
|
||||
el.classList.add('highlighted-address');
|
||||
}
|
||||
|
||||
@@ -6,20 +6,19 @@ function handleQuoteClick(reply, parentCid, threadCid) {
|
||||
if (threadCid && cid === threadCid) {
|
||||
const highlightedElements = document.querySelectorAll('.highlighted');
|
||||
|
||||
highlightedElements.forEach(el => {
|
||||
highlightedElements.forEach((el) => {
|
||||
el.classList.remove('highlighted');
|
||||
});
|
||||
|
||||
const opElementSelector = isMobile ? '.op-mobile' : '.op-desktop';
|
||||
|
||||
const opElement = [...document.querySelectorAll(opElementSelector)]
|
||||
.find(el => {
|
||||
const opElement = [...document.querySelectorAll(opElementSelector)].find((el) => {
|
||||
const postNumberElement = el.querySelector(postNumberSelector);
|
||||
return postNumberElement && postNumberElement.innerHTML.includes(threadCid);
|
||||
});
|
||||
|
||||
if (opElement) {
|
||||
opElement.scrollIntoView({ behavior: "auto", block: "start" });
|
||||
opElement.scrollIntoView({ behavior: 'auto', block: 'start' });
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@@ -29,8 +28,7 @@ function handleQuoteClick(reply, parentCid, threadCid) {
|
||||
|
||||
const targetElementSelector = isMobile ? '.post-reply-mobile, .op-mobile' : '.post-reply-desktop, .op-desktop';
|
||||
|
||||
const targetElement = [...document.querySelectorAll(targetElementSelector)]
|
||||
.find(el => {
|
||||
const targetElement = [...document.querySelectorAll(targetElementSelector)].find((el) => {
|
||||
const postNumberElement = el.querySelector(postNumberSelector);
|
||||
return postNumberElement && postNumberElement.innerHTML.includes(cid);
|
||||
});
|
||||
@@ -38,11 +36,11 @@ function handleQuoteClick(reply, parentCid, threadCid) {
|
||||
if (targetElement) {
|
||||
const highlightedElements = document.querySelectorAll('.highlighted-click');
|
||||
|
||||
highlightedElements.forEach(el => {
|
||||
highlightedElements.forEach((el) => {
|
||||
el.classList.remove('highlighted-click');
|
||||
});
|
||||
|
||||
targetElement.scrollIntoView({ behavior: "auto", block: "start" });
|
||||
targetElement.scrollIntoView({ behavior: 'auto', block: 'start' });
|
||||
|
||||
if (!targetElement.classList.contains('op-mobile') && !targetElement.classList.contains('op-desktop')) {
|
||||
targetElement.classList.add('highlighted-click');
|
||||
@@ -50,6 +48,6 @@ function handleQuoteClick(reply, parentCid, threadCid) {
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default handleQuoteClick;
|
||||
@@ -5,8 +5,7 @@ function handleQuoteHover(reply, parentCid, onElementOutOfView) {
|
||||
|
||||
const targetElementSelector = isMobile ? '.post-reply-mobile, .op-mobile' : '.post-reply-desktop, .op-desktop';
|
||||
|
||||
const targetElement = [...document.querySelectorAll(targetElementSelector)]
|
||||
.find(el => {
|
||||
const targetElement = [...document.querySelectorAll(targetElementSelector)].find((el) => {
|
||||
const postNumberElement = el.querySelector(postNumberSelector);
|
||||
return postNumberElement && postNumberElement.innerHTML.includes(cid);
|
||||
});
|
||||
@@ -25,7 +24,7 @@ function handleQuoteHover(reply, parentCid, onElementOutOfView) {
|
||||
if (isInViewport(targetElement)) {
|
||||
const highlightedElements = document.querySelectorAll('.highlighted');
|
||||
|
||||
highlightedElements.forEach(el => {
|
||||
highlightedElements.forEach((el) => {
|
||||
el.classList.remove('highlighted');
|
||||
});
|
||||
|
||||
@@ -38,6 +37,6 @@ function handleQuoteHover(reply, parentCid, onElementOutOfView) {
|
||||
} else {
|
||||
onElementOutOfView();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default handleQuoteHover;
|
||||
@@ -1,13 +1,13 @@
|
||||
function handleShareClick(selectedAddress, cid) {
|
||||
let shareLink;
|
||||
|
||||
if (cid === "rules" || cid === "description") {
|
||||
if (cid === 'rules' || cid === 'description') {
|
||||
shareLink = `https://plebchan.eth.limo/#/p/${selectedAddress}`;
|
||||
} else {
|
||||
const plebBzBaseURL = "https://pleb.bz/p/";
|
||||
const plebBzBaseURL = 'https://pleb.bz/p/';
|
||||
shareLink = `${plebBzBaseURL}${selectedAddress}/`;
|
||||
|
||||
if (cid !== "rules" && cid !== "description") {
|
||||
if (cid !== 'rules' && cid !== 'description') {
|
||||
shareLink += `c/${cid}`;
|
||||
}
|
||||
|
||||
@@ -15,9 +15,12 @@ function handleShareClick(selectedAddress, cid) {
|
||||
}
|
||||
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(shareLink).then(() => {
|
||||
console.log("Link copied to clipboard!");
|
||||
}).catch(err => {
|
||||
navigator.clipboard
|
||||
.writeText(shareLink)
|
||||
.then(() => {
|
||||
console.log('Link copied to clipboard!');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Could not copy text: ', err);
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -1,94 +1,92 @@
|
||||
import useGeneralStore from "../hooks/stores/useGeneralStore";
|
||||
import useGeneralStore from '../hooks/stores/useGeneralStore';
|
||||
|
||||
const handleStyleChange = (event) => {
|
||||
const {
|
||||
setBodyStyle, setSelectedStyle
|
||||
} = useGeneralStore.getState();
|
||||
const { setBodyStyle, setSelectedStyle } = useGeneralStore.getState();
|
||||
|
||||
switch (event.target.value) {
|
||||
case "Yotsuba":
|
||||
case 'Yotsuba':
|
||||
const yotsubaBodyStyle = {
|
||||
background: "#ffe url(assets/fade.png) top repeat-x",
|
||||
color: "maroon",
|
||||
fontFamily: "Arial, Helvetica, sans-serif"
|
||||
background: '#ffe url(assets/fade.png) top repeat-x',
|
||||
color: 'maroon',
|
||||
fontFamily: 'Arial, Helvetica, sans-serif',
|
||||
};
|
||||
setBodyStyle(yotsubaBodyStyle);
|
||||
setSelectedStyle("Yotsuba");
|
||||
localStorage.setItem("selectedStyle", "Yotsuba");
|
||||
localStorage.setItem("bodyStyle", JSON.stringify(yotsubaBodyStyle));
|
||||
setSelectedStyle('Yotsuba');
|
||||
localStorage.setItem('selectedStyle', 'Yotsuba');
|
||||
localStorage.setItem('bodyStyle', JSON.stringify(yotsubaBodyStyle));
|
||||
break;
|
||||
|
||||
case "Yotsuba-B":
|
||||
case 'Yotsuba-B':
|
||||
const yotsubaBBodyStyle = {
|
||||
background: "#eef2ff url(assets/fade-blue.png) top center repeat-x",
|
||||
color: "#000",
|
||||
fontFamily: "Arial, Helvetica, sans-serif"
|
||||
background: '#eef2ff url(assets/fade-blue.png) top center repeat-x',
|
||||
color: '#000',
|
||||
fontFamily: 'Arial, Helvetica, sans-serif',
|
||||
};
|
||||
setBodyStyle(yotsubaBBodyStyle);
|
||||
setSelectedStyle("Yotsuba-B");
|
||||
localStorage.setItem("selectedStyle", "Yotsuba-B");
|
||||
localStorage.setItem("bodyStyle", JSON.stringify(yotsubaBBodyStyle));
|
||||
setSelectedStyle('Yotsuba-B');
|
||||
localStorage.setItem('selectedStyle', 'Yotsuba-B');
|
||||
localStorage.setItem('bodyStyle', JSON.stringify(yotsubaBBodyStyle));
|
||||
break;
|
||||
|
||||
case "Futaba":
|
||||
case 'Futaba':
|
||||
const futabaBodyStyle = {
|
||||
background: "#ffe",
|
||||
color: "maroon",
|
||||
fontFamily: "times new roman, serif"
|
||||
background: '#ffe',
|
||||
color: 'maroon',
|
||||
fontFamily: 'times new roman, serif',
|
||||
};
|
||||
setBodyStyle(futabaBodyStyle);
|
||||
setSelectedStyle("Futaba");
|
||||
localStorage.setItem("selectedStyle", "Futaba");
|
||||
localStorage.setItem("bodyStyle", JSON.stringify(futabaBodyStyle));
|
||||
setSelectedStyle('Futaba');
|
||||
localStorage.setItem('selectedStyle', 'Futaba');
|
||||
localStorage.setItem('bodyStyle', JSON.stringify(futabaBodyStyle));
|
||||
break;
|
||||
|
||||
case "Burichan":
|
||||
case 'Burichan':
|
||||
const burichanBodyStyle = {
|
||||
background: "#eef2ff",
|
||||
color: "#000",
|
||||
fontFamily: "times new roman, serif"
|
||||
background: '#eef2ff',
|
||||
color: '#000',
|
||||
fontFamily: 'times new roman, serif',
|
||||
};
|
||||
setBodyStyle(burichanBodyStyle);
|
||||
setSelectedStyle("Burichan");
|
||||
localStorage.setItem("selectedStyle", "Burichan");
|
||||
localStorage.setItem("bodyStyle", JSON.stringify(burichanBodyStyle));
|
||||
setSelectedStyle('Burichan');
|
||||
localStorage.setItem('selectedStyle', 'Burichan');
|
||||
localStorage.setItem('bodyStyle', JSON.stringify(burichanBodyStyle));
|
||||
break;
|
||||
|
||||
case "Tomorrow":
|
||||
case 'Tomorrow':
|
||||
const tomorrowBodyStyle = {
|
||||
background: "#1d1f21 none",
|
||||
color: "#c5c8c6",
|
||||
fontFamily: "Arial, Helvetica, sans-serif"
|
||||
background: '#1d1f21 none',
|
||||
color: '#c5c8c6',
|
||||
fontFamily: 'Arial, Helvetica, sans-serif',
|
||||
};
|
||||
setBodyStyle(tomorrowBodyStyle);
|
||||
setSelectedStyle("Tomorrow");
|
||||
localStorage.setItem("selectedStyle", "Tomorrow");
|
||||
localStorage.setItem("bodyStyle", JSON.stringify(tomorrowBodyStyle));
|
||||
setSelectedStyle('Tomorrow');
|
||||
localStorage.setItem('selectedStyle', 'Tomorrow');
|
||||
localStorage.setItem('bodyStyle', JSON.stringify(tomorrowBodyStyle));
|
||||
break;
|
||||
|
||||
case "Photon":
|
||||
case 'Photon':
|
||||
const photonBodyStyle = {
|
||||
background: "#eee none",
|
||||
color: "#333",
|
||||
fontFamily: "Arial, Helvetica, sans-serif"
|
||||
background: '#eee none',
|
||||
color: '#333',
|
||||
fontFamily: 'Arial, Helvetica, sans-serif',
|
||||
};
|
||||
setBodyStyle(photonBodyStyle);
|
||||
setSelectedStyle("Photon");
|
||||
localStorage.setItem("selectedStyle", "Photon");
|
||||
localStorage.setItem("bodyStyle", JSON.stringify(photonBodyStyle));
|
||||
setSelectedStyle('Photon');
|
||||
localStorage.setItem('selectedStyle', 'Photon');
|
||||
localStorage.setItem('bodyStyle', JSON.stringify(photonBodyStyle));
|
||||
break;
|
||||
|
||||
default:
|
||||
const defaultBodyStyle = {
|
||||
background: "#ffe url(assets/fade.png) top repeat-x",
|
||||
color: "maroon",
|
||||
fontFamily: "Arial, Helvetica, sans-serif"
|
||||
background: '#ffe url(assets/fade.png) top repeat-x',
|
||||
color: 'maroon',
|
||||
fontFamily: 'Arial, Helvetica, sans-serif',
|
||||
};
|
||||
setBodyStyle(defaultBodyStyle);
|
||||
setSelectedStyle("Yotsuba");
|
||||
localStorage.setItem("selectedStyle", "Yotsuba");
|
||||
localStorage.setItem("bodyStyle", JSON.stringify(defaultBodyStyle));
|
||||
}
|
||||
setSelectedStyle('Yotsuba');
|
||||
localStorage.setItem('selectedStyle', 'Yotsuba');
|
||||
localStorage.setItem('bodyStyle', JSON.stringify(defaultBodyStyle));
|
||||
}
|
||||
};
|
||||
|
||||
export default handleStyleChange;
|
||||
@@ -1,9 +1,9 @@
|
||||
function removeHighlight() {
|
||||
const highlightedElements = document.querySelectorAll('.highlighted');
|
||||
|
||||
highlightedElements.forEach(el => {
|
||||
highlightedElements.forEach((el) => {
|
||||
el.classList.remove('highlighted');
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default removeHighlight;
|
||||
Reference in New Issue
Block a user