mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
style: run prettier
This commit is contained in:
@@ -4,20 +4,24 @@ import useGeneralStore from '../../hooks/stores/useGeneralStore';
|
||||
import Modal from 'react-modal';
|
||||
import Draggable from 'react-draggable';
|
||||
|
||||
|
||||
const CaptchaModal = () => {
|
||||
const {
|
||||
challengesArray, setChallengesArray,
|
||||
const {
|
||||
challengesArray,
|
||||
setChallengesArray,
|
||||
pendingComment,
|
||||
selectedStyle,
|
||||
setCaptchaResponse,
|
||||
isAuthorDelete, setIsAuthorDelete,
|
||||
isAuthorEdit, setIsAuthorEdit,
|
||||
isCaptchaOpen, setIsCaptchaOpen,
|
||||
isModEdit, setIsModEdit,
|
||||
isAuthorDelete,
|
||||
setIsAuthorDelete,
|
||||
isAuthorEdit,
|
||||
setIsAuthorEdit,
|
||||
isCaptchaOpen,
|
||||
setIsCaptchaOpen,
|
||||
isModEdit,
|
||||
setIsModEdit,
|
||||
resolveCaptchaPromise,
|
||||
selectedShortCid,
|
||||
} = useGeneralStore(state => state);
|
||||
} = useGeneralStore((state) => state);
|
||||
|
||||
const [imageSources, setImageSources] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
@@ -28,7 +32,6 @@ const CaptchaModal = () => {
|
||||
const nodeRef = useRef(null);
|
||||
const [isPromiseResolved, setIsPromiseResolved] = useState(false);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCaptchaOpen) {
|
||||
setIsAuthorDelete(false);
|
||||
@@ -37,7 +40,6 @@ const CaptchaModal = () => {
|
||||
}
|
||||
}, [isCaptchaOpen, setIsAuthorDelete, setIsAuthorEdit, setIsModEdit]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => setIsMobile(window.innerWidth <= 480);
|
||||
window.addEventListener('resize', handleResize);
|
||||
@@ -48,7 +50,6 @@ const CaptchaModal = () => {
|
||||
};
|
||||
}, [setIsMobile]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (isCaptchaOpen && challengesArray) {
|
||||
setIsLoading(true);
|
||||
@@ -67,9 +68,8 @@ const CaptchaModal = () => {
|
||||
}
|
||||
}, [challengesArray, isCaptchaOpen]);
|
||||
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
if (event.key === "Enter") {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
submitCaptcha((response) => {
|
||||
setCaptchaResponse(response);
|
||||
@@ -78,15 +78,13 @@ const CaptchaModal = () => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleReturnKeyDown = () => {
|
||||
submitCaptcha((response) => {
|
||||
submitCaptcha((response) => {
|
||||
setCaptchaResponse(response);
|
||||
resolveCaptchaPromise(response);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const submitCaptcha = (callback) => {
|
||||
if (!isPromiseResolved) {
|
||||
setCaptchaResponse(responseRef.current.value);
|
||||
@@ -102,7 +100,7 @@ const CaptchaModal = () => {
|
||||
callback(responseRef.current.value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCaptchaOpen) {
|
||||
setIsPromiseResolved(false);
|
||||
@@ -116,76 +114,76 @@ const CaptchaModal = () => {
|
||||
setIsCaptchaOpen(false);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<StyledModal
|
||||
isOpen={isCaptchaOpen}
|
||||
onRequestClose={() => {
|
||||
handleCloseModal();
|
||||
submitCaptcha();}}
|
||||
contentLabel="Captcha Modal"
|
||||
shouldCloseOnEsc={false}
|
||||
shouldCloseOnOverlayClick={false}
|
||||
selectedStyle={selectedStyle}
|
||||
overlayClassName="hide-modal-overlay">
|
||||
<Draggable handle=".modal-header" nodeRef={nodeRef} disabled={isMobile}>
|
||||
<div className="modal-content" ref={nodeRef}>
|
||||
<div className="modal-header">
|
||||
{isModEdit ? "Challenge for Moderator Action" :
|
||||
isAuthorEdit ? "Challenge for Editing Post" :
|
||||
isAuthorDelete ? "Challenge for Deleting Post" :
|
||||
pendingComment.parentCid ?
|
||||
("Challenges for Reply to c/" + selectedShortCid) :
|
||||
"Challenges for New Thread"}
|
||||
<button className="icon" onClick={() => handleCloseModal()} title="close" />
|
||||
isOpen={isCaptchaOpen}
|
||||
onRequestClose={() => {
|
||||
handleCloseModal();
|
||||
submitCaptcha();
|
||||
}}
|
||||
contentLabel='Captcha Modal'
|
||||
shouldCloseOnEsc={false}
|
||||
shouldCloseOnOverlayClick={false}
|
||||
selectedStyle={selectedStyle}
|
||||
overlayClassName='hide-modal-overlay'
|
||||
>
|
||||
<Draggable handle='.modal-header' nodeRef={nodeRef} disabled={isMobile}>
|
||||
<div className='modal-content' ref={nodeRef}>
|
||||
<div className='modal-header'>
|
||||
{isModEdit
|
||||
? 'Challenge for Moderator Action'
|
||||
: isAuthorEdit
|
||||
? 'Challenge for Editing Post'
|
||||
: isAuthorDelete
|
||||
? 'Challenge for Deleting Post'
|
||||
: pendingComment.parentCid
|
||||
? 'Challenges for Reply to c/' + selectedShortCid
|
||||
: 'Challenges for New Thread'}
|
||||
<button className='icon' onClick={() => handleCloseModal()} title='close' />
|
||||
</div>
|
||||
<div id="form">
|
||||
<div id='form'>
|
||||
{pendingComment.author?.displayName ? (
|
||||
<div>
|
||||
<input id="field" type="text" placeholder={pendingComment.author?.displayName || ''} disabled />
|
||||
<input id='field' type='text' placeholder={pendingComment.author?.displayName || ''} disabled />
|
||||
</div>
|
||||
) : null}
|
||||
{pendingComment.title ? (
|
||||
<div>
|
||||
<input id="field" type="text" placeholder={pendingComment.title || ''} disabled />
|
||||
<input id='field' type='text' placeholder={pendingComment.title || ''} disabled />
|
||||
</div>
|
||||
) : null}
|
||||
{pendingComment.content ? (
|
||||
<div>
|
||||
<textarea
|
||||
rows="4"
|
||||
placeholder={pendingComment.content || "Comment"}
|
||||
wrap="soft"
|
||||
disabled
|
||||
/>
|
||||
<textarea rows='4' placeholder={pendingComment.content || 'Comment'} wrap='soft' disabled />
|
||||
</div>
|
||||
) : null}
|
||||
{pendingComment.link ? (
|
||||
<div>
|
||||
<input id="field" type="text" placeholder={pendingComment.link || ''} disabled />
|
||||
<input id='field' type='text' placeholder={pendingComment.link || ''} disabled />
|
||||
</div>
|
||||
) : null}
|
||||
<div id="captcha-container">
|
||||
<input
|
||||
id="response"
|
||||
type="text"
|
||||
autoComplete='off'
|
||||
placeholder="TYPE THE CAPTCHA HERE AND PRESS ENTER"
|
||||
ref={responseRef}
|
||||
onKeyDown={handleKeyDown}
|
||||
autoFocus />
|
||||
<div id='captcha-container'>
|
||||
<input
|
||||
id='response'
|
||||
type='text'
|
||||
autoComplete='off'
|
||||
placeholder='TYPE THE CAPTCHA HERE AND PRESS ENTER'
|
||||
ref={responseRef}
|
||||
onKeyDown={handleKeyDown}
|
||||
autoFocus
|
||||
/>
|
||||
{isLoading ? (
|
||||
<img src="" alt="loading..." style={{ visibility: "hidden" }} />
|
||||
<img src='' alt='loading...' style={{ visibility: 'hidden' }} />
|
||||
) : (
|
||||
imageSources[currentChallengeIndex] && <img src={imageSources[currentChallengeIndex]} alt="captcha" />
|
||||
imageSources[currentChallengeIndex] && <img src={imageSources[currentChallengeIndex]} alt='captcha' />
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span style={{lineHeight: '1.7'}}>
|
||||
<span style={{ lineHeight: '1.7' }}>
|
||||
Challenge {currentChallengeIndex + 1} of {totalChallenges}
|
||||
</span>
|
||||
<button
|
||||
id="nav"
|
||||
id='nav'
|
||||
onClick={() => {
|
||||
if (currentChallengeIndex + 1 < totalChallenges) {
|
||||
setCurrentChallengeIndex((currentChallengeIndex + 1) % totalChallenges);
|
||||
@@ -194,7 +192,7 @@ const CaptchaModal = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{currentChallengeIndex + 1 < totalChallenges ? "Next" : "Submit"}
|
||||
{currentChallengeIndex + 1 < totalChallenges ? 'Next' : 'Submit'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -206,4 +204,4 @@ const CaptchaModal = () => {
|
||||
|
||||
Modal.setAppElement('#root');
|
||||
|
||||
export default CaptchaModal;
|
||||
export default CaptchaModal;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAccount, useCreateSubplebbit,
|
||||
import {
|
||||
useAccount,
|
||||
useCreateSubplebbit,
|
||||
// useSubplebbits, useAccountSubplebbits
|
||||
} from '@plebbit/plebbit-react-hooks';
|
||||
} from '@plebbit/plebbit-react-hooks';
|
||||
import { StyledModal } from '../styled/modals/CreateBoardModal.styled';
|
||||
import useGeneralStore from '../../hooks/stores/useGeneralStore';
|
||||
import Modal from 'react-modal';
|
||||
@@ -10,7 +12,7 @@ import useError from '../../hooks/useError';
|
||||
import useSuccess from '../../hooks/useSuccess';
|
||||
|
||||
const CreateBoardModal = ({ isOpen, closeModal }) => {
|
||||
const { selectedStyle } = useGeneralStore(state => state);
|
||||
const { selectedStyle } = useGeneralStore((state) => state);
|
||||
|
||||
const account = useAccount();
|
||||
const navigate = useNavigate();
|
||||
@@ -29,7 +31,6 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
||||
const [rules, setRules] = useState([]);
|
||||
const [editIndex, setEditIndex] = useState(-1);
|
||||
|
||||
|
||||
const handleAddRule = () => {
|
||||
if (rule.trim() === '') {
|
||||
setNewErrorMessage('Rule field is empty');
|
||||
@@ -41,15 +42,14 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if(editIndex > -1){
|
||||
setRules(rules.map((r, i) => i === editIndex ? rule : r));
|
||||
if (editIndex > -1) {
|
||||
setRules(rules.map((r, i) => (i === editIndex ? rule : r)));
|
||||
setEditIndex(-1);
|
||||
} else {
|
||||
setRules([...rules, rule]);
|
||||
}
|
||||
setRule('');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (ruleInputRef.current) {
|
||||
@@ -57,40 +57,35 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
||||
}
|
||||
}, [rules]);
|
||||
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleAddRule();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const handleEditRule = (index) => {
|
||||
setRule(rules[index]);
|
||||
setEditIndex(index);
|
||||
ruleInputRef.current.focus();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const handleDeleteRule = (index) => {
|
||||
if (index === editIndex) {
|
||||
setEditIndex(-1);
|
||||
}
|
||||
setRules(rules.filter((_, i) => i !== index));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const createSubplebbitOptions = {
|
||||
title: title || undefined,
|
||||
description: description || undefined,
|
||||
suggested: {
|
||||
avatarUrl : avatar || undefined,
|
||||
avatarUrl: avatar || undefined,
|
||||
},
|
||||
roles: moderators || undefined,
|
||||
rules: rules || undefined,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const resetFields = () => {
|
||||
setTitle('');
|
||||
setDescription('');
|
||||
@@ -98,23 +93,21 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
||||
setModerators('');
|
||||
setRule('');
|
||||
setRules([]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const { createdSubplebbit, createSubplebbit } = useCreateSubplebbit(createSubplebbitOptions);
|
||||
|
||||
|
||||
const handleCreateBoard = async () => {
|
||||
let moderatorAddresses = moderators.trim() ? moderators.split(',').map(addr => addr.trim()) : [];
|
||||
let invalidAddresses = moderatorAddresses.filter(addr => !(addr.endsWith('.eth') || (addr.startsWith('12D3KooW') && addr.length === 52)));
|
||||
let moderatorAddresses = moderators.trim() ? moderators.split(',').map((addr) => addr.trim()) : [];
|
||||
let invalidAddresses = moderatorAddresses.filter((addr) => !(addr.endsWith('.eth') || (addr.startsWith('12D3KooW') && addr.length === 52)));
|
||||
|
||||
if (invalidAddresses.length > 0) {
|
||||
setNewErrorMessage("Invalid moderator addresses: " + invalidAddresses.join(", "));
|
||||
setNewErrorMessage('Invalid moderator addresses: ' + invalidAddresses.join(', '));
|
||||
return;
|
||||
}
|
||||
|
||||
const roles = {};
|
||||
moderatorAddresses.forEach(addr => {
|
||||
moderatorAddresses.forEach((addr) => {
|
||||
roles[addr] = { role: 'moderator' };
|
||||
});
|
||||
roles[account.author.address] = { role: 'admin' };
|
||||
@@ -133,27 +126,27 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
||||
|
||||
if (avatar) {
|
||||
createSubplebbitOptions.suggested = {
|
||||
avatarUrl : avatar,
|
||||
avatarUrl: avatar,
|
||||
};
|
||||
}
|
||||
|
||||
if (rules.length > 0) {
|
||||
createSubplebbitOptions.rules = rules;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
await createSubplebbit(createSubplebbitOptions);
|
||||
} catch (error) {
|
||||
setNewErrorMessage(error.message);
|
||||
}
|
||||
|
||||
|
||||
if (createdSubplebbit) {
|
||||
resetFields();
|
||||
closeModal();
|
||||
setNewSuccessMessage('Board created successfully, address: ' + createdSubplebbit.address);
|
||||
navigate(`/p/${createdSubplebbit.address}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// remove after testing:
|
||||
// const {accountSubplebbits} = useAccountSubplebbits()
|
||||
@@ -165,114 +158,68 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
||||
<StyledModal
|
||||
isOpen={isOpen}
|
||||
onRequestClose={closeModal}
|
||||
contentLabel="Create Board"
|
||||
contentLabel='Create Board'
|
||||
selectedStyle={selectedStyle}
|
||||
style={{ overlay: { backgroundColor: "rgba(0,0,0,.25)" }}}
|
||||
style={{ overlay: { backgroundColor: 'rgba(0,0,0,.25)' } }}
|
||||
>
|
||||
<div className="modal-content" ref={nodeRef}>
|
||||
<div className="modal-header">
|
||||
<div className='modal-content' ref={nodeRef}>
|
||||
<div className='modal-header'>
|
||||
Create Board
|
||||
<button className="icon" onClick={() => closeModal()} title="close" />
|
||||
<button className='icon' onClick={() => closeModal()} title='close' />
|
||||
</div>
|
||||
<ul id="form">
|
||||
<div id="explaination">NOTE: plebbit is P2P, your board will stay online for as long as you leave the plebchan desktop app (full node) connected to the Internet.</div>
|
||||
<li className='settings-option disc'>
|
||||
Title
|
||||
</li>
|
||||
<div className='settings-tip'>
|
||||
Optional, useful to describe the board next to its p/address.
|
||||
<ul id='form'>
|
||||
<div id='explaination'>
|
||||
NOTE: plebbit is P2P, your board will stay online for as long as you leave the plebchan desktop app (full node) connected to the Internet.
|
||||
</div>
|
||||
<li className='settings-option disc'>Title</li>
|
||||
<div className='settings-tip'>Optional, useful to describe the board next to its p/address.</div>
|
||||
<div className='settings-input'>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
placeholder="Board Title"
|
||||
/>
|
||||
</div>
|
||||
<li className='settings-option disc'>
|
||||
Description
|
||||
</li>
|
||||
<div className='settings-tip'>
|
||||
Optional, displayed as sticky with the board's avatar if set.
|
||||
<input id='name' type='text' value={title} onChange={(e) => setTitle(e.target.value)} placeholder='Board Title' />
|
||||
</div>
|
||||
<li className='settings-option disc'>Description</li>
|
||||
<div className='settings-tip'>Optional, displayed as sticky with the board's avatar if set.</div>
|
||||
<div className='settings-input'>
|
||||
<textarea
|
||||
id="description"
|
||||
type="text"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
placeholder="Add a description"
|
||||
/>
|
||||
</div>
|
||||
<li className='settings-option disc'>
|
||||
Avatar
|
||||
</li>
|
||||
<div className='settings-tip'>
|
||||
Optional, set an image that represents your board.
|
||||
<textarea id='description' type='text' value={description} onChange={(e) => setDescription(e.target.value)} placeholder='Add a description' />
|
||||
</div>
|
||||
<li className='settings-option disc'>Avatar</li>
|
||||
<div className='settings-tip'>Optional, set an image that represents your board.</div>
|
||||
<div className='settings-input'>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
value={avatar}
|
||||
onChange={(e) => setAvatar(e.target.value)}
|
||||
placeholder="https://example.com/image.png"
|
||||
/>
|
||||
</div>
|
||||
<li className='settings-option disc'>
|
||||
Moderators
|
||||
</li>
|
||||
<div className='settings-tip'>
|
||||
Optional, let other users help you moderate your board.
|
||||
<input id='name' type='text' value={avatar} onChange={(e) => setAvatar(e.target.value)} placeholder='https://example.com/image.png' />
|
||||
</div>
|
||||
<li className='settings-option disc'>Moderators</li>
|
||||
<div className='settings-tip'>Optional, let other users help you moderate your board.</div>
|
||||
<div className='settings-input'>
|
||||
<textarea
|
||||
id="name"
|
||||
type="text"
|
||||
value={moderators}
|
||||
onChange={(e) => setModerators(e.target.value)}
|
||||
placeholder='username.eth, 12D3KooW..., username2.eth'
|
||||
/>
|
||||
</div>
|
||||
<li className='settings-option disc'>
|
||||
Rules
|
||||
</li>
|
||||
<div className='settings-tip'>
|
||||
Optional, listed in a sticky on top. Maximum 15 rules.
|
||||
<textarea id='name' type='text' value={moderators} onChange={(e) => setModerators(e.target.value)} placeholder='username.eth, 12D3KooW..., username2.eth' />
|
||||
</div>
|
||||
<li className='settings-option disc'>Rules</li>
|
||||
<div className='settings-tip'>Optional, listed in a sticky on top. Maximum 15 rules.</div>
|
||||
<div className='settings-input'>
|
||||
<input
|
||||
ref={ruleInputRef}
|
||||
id="rule"
|
||||
type="text"
|
||||
value={rule}
|
||||
onChange={(e) => setRule(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder="Add a rule"
|
||||
/>
|
||||
<input ref={ruleInputRef} id='rule' type='text' value={rule} onChange={(e) => setRule(e.target.value)} onKeyDown={handleKeyDown} placeholder='Add a rule' />
|
||||
</div>
|
||||
<button style={{display: rules.length > 0 ? 'none' : 'block'}} id="rule-btn" className={rules.length > 0 ? "relative" : ""} onClick={handleAddRule}>
|
||||
{editIndex > -1 ? "Update Rule" : "Add Rule"}
|
||||
<button style={{ display: rules.length > 0 ? 'none' : 'block' }} id='rule-btn' className={rules.length > 0 ? 'relative' : ''} onClick={handleAddRule}>
|
||||
{editIndex > -1 ? 'Update Rule' : 'Add Rule'}
|
||||
</button>
|
||||
{rules.length > 0 && (
|
||||
<fieldset>
|
||||
<legend>
|
||||
<button className={rules.length > 0 ? "relative" : ""} onClick={handleAddRule}>
|
||||
{editIndex > -1 ? "Update Rule" : "Add Rule"}
|
||||
<button className={rules.length > 0 ? 'relative' : ''} onClick={handleAddRule}>
|
||||
{editIndex > -1 ? 'Update Rule' : 'Add Rule'}
|
||||
</button>
|
||||
</legend>
|
||||
{rules.map((rule, index) => (
|
||||
<div key={index} className="rule-item">
|
||||
<p>{index+1}. {rule}</p>
|
||||
<div key={index} className='rule-item'>
|
||||
<p>
|
||||
{index + 1}. {rule}
|
||||
</p>
|
||||
<button onClick={() => handleEditRule(index)}>Edit</button>
|
||||
<button onClick={() => handleDeleteRule(index)}>Delete</button>
|
||||
</div>
|
||||
))}
|
||||
</fieldset>
|
||||
)}
|
||||
<button onClick={handleCreateBoard} id="create-board-btn">Create Board</button>
|
||||
<button onClick={handleCreateBoard} id='create-board-btn'>
|
||||
Create Board
|
||||
</button>
|
||||
</ul>
|
||||
</div>
|
||||
</StyledModal>
|
||||
@@ -281,4 +228,4 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
||||
|
||||
Modal.setAppElement('#root');
|
||||
|
||||
export default CreateBoardModal;
|
||||
export default CreateBoardModal;
|
||||
|
||||
@@ -4,19 +4,14 @@ import useGeneralStore from '../../hooks/stores/useGeneralStore';
|
||||
import Modal from 'react-modal';
|
||||
import Draggable from 'react-draggable';
|
||||
|
||||
|
||||
const EditModal = ({ isOpen, closeModal, originalCommentContent }) => {
|
||||
const {
|
||||
setEditedComment,
|
||||
selectedStyle,
|
||||
} = useGeneralStore(state => state);
|
||||
const { setEditedComment, selectedStyle } = useGeneralStore((state) => state);
|
||||
|
||||
const nodeRef = useRef(null);
|
||||
const commentRef = useRef();
|
||||
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth <= 480);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => setIsMobile(window.innerWidth <= 480);
|
||||
window.addEventListener('resize', handleResize);
|
||||
@@ -26,40 +21,43 @@ const EditModal = ({ isOpen, closeModal, originalCommentContent }) => {
|
||||
};
|
||||
}, [setIsMobile]);
|
||||
|
||||
|
||||
const handleSaveEdit = () => {
|
||||
setEditedComment(commentRef.current.value);
|
||||
closeModal();
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<StyledModal
|
||||
isOpen={isOpen}
|
||||
onRequestClose={closeModal}
|
||||
contentLabel="Edit Comment"
|
||||
shouldCloseOnEsc={false}
|
||||
shouldCloseOnOverlayClick={isMobile}
|
||||
selectedStyle={selectedStyle}
|
||||
style={isMobile ? ({ overlay: { backgroundColor: "rgba(0,0,0,.25)" }}) : ({ overlay: { backgroundColor: "rgba(0,0,0,0)" }})}>
|
||||
<Draggable handle=".modal-header" nodeRef={nodeRef} disabled={isMobile}>
|
||||
<div className="modal-content" ref={nodeRef}>
|
||||
<div className="modal-header">
|
||||
isOpen={isOpen}
|
||||
onRequestClose={closeModal}
|
||||
contentLabel='Edit Comment'
|
||||
shouldCloseOnEsc={false}
|
||||
shouldCloseOnOverlayClick={isMobile}
|
||||
selectedStyle={selectedStyle}
|
||||
style={isMobile ? { overlay: { backgroundColor: 'rgba(0,0,0,.25)' } } : { overlay: { backgroundColor: 'rgba(0,0,0,0)' } }}
|
||||
>
|
||||
<Draggable handle='.modal-header' nodeRef={nodeRef} disabled={isMobile}>
|
||||
<div className='modal-content' ref={nodeRef}>
|
||||
<div className='modal-header'>
|
||||
Edit Comment
|
||||
<button className="icon" onClick={() => closeModal()} title="close" />
|
||||
<button className='icon' onClick={() => closeModal()} title='close' />
|
||||
</div>
|
||||
<div id="form">
|
||||
<div className="textarea-wrapper">
|
||||
<textarea className="textarea"
|
||||
rows="4"
|
||||
style={{paddingTop: '0'}}
|
||||
placeholder="Comment"
|
||||
defaultValue={originalCommentContent}
|
||||
wrap="soft"
|
||||
ref={commentRef} />
|
||||
<div id='form'>
|
||||
<div className='textarea-wrapper'>
|
||||
<textarea
|
||||
className='textarea'
|
||||
rows='4'
|
||||
style={{ paddingTop: '0' }}
|
||||
placeholder='Comment'
|
||||
defaultValue={originalCommentContent}
|
||||
wrap='soft'
|
||||
ref={commentRef}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<button id="next" onClick={handleSaveEdit}>Save</button>
|
||||
<button id='next' onClick={handleSaveEdit}>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -70,4 +68,4 @@ const EditModal = ({ isOpen, closeModal, originalCommentContent }) => {
|
||||
|
||||
Modal.setAppElement('#root');
|
||||
|
||||
export default EditModal;
|
||||
export default EditModal;
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Modal from "react-modal";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useComment, usePublishCommentEdit } from "@plebbit/plebbit-react-hooks";
|
||||
import { StyledModal } from "../styled/modals/ModerationModal.styled";
|
||||
import useGeneralStore from "../../hooks/stores/useGeneralStore";
|
||||
import useError from "../../hooks/useError";
|
||||
import useSuccess from "../../hooks/useSuccess";
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Modal from 'react-modal';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useComment, usePublishCommentEdit } from '@plebbit/plebbit-react-hooks';
|
||||
import { StyledModal } from '../styled/modals/ModerationModal.styled';
|
||||
import useGeneralStore from '../../hooks/stores/useGeneralStore';
|
||||
import useError from '../../hooks/useError';
|
||||
import useSuccess from '../../hooks/useSuccess';
|
||||
|
||||
const ModerationModal = ({ isOpen, closeModal, deletePost }) => {
|
||||
const {
|
||||
selectedAddress,
|
||||
selectedStyle,
|
||||
setCaptchaResponse,
|
||||
setChallengesArray,
|
||||
setIsCaptchaOpen,
|
||||
setIsModEdit,
|
||||
moderatingCommentCid,
|
||||
setResolveCaptchaPromise,
|
||||
} = useGeneralStore(state => state);
|
||||
const { selectedAddress, selectedStyle, setCaptchaResponse, setChallengesArray, setIsCaptchaOpen, setIsModEdit, moderatingCommentCid, setResolveCaptchaPromise } =
|
||||
useGeneralStore((state) => state);
|
||||
|
||||
const comment = useComment({commentCid: moderatingCommentCid});
|
||||
const comment = useComment({ commentCid: moderatingCommentCid });
|
||||
|
||||
const [pin, setPin] = useState(comment.pinned);
|
||||
const [deleteThread, setDeleteThread] = useState(deletePost);
|
||||
@@ -31,68 +22,62 @@ const ModerationModal = ({ isOpen, closeModal, deletePost }) => {
|
||||
const [, setNewErrorMessage] = useError();
|
||||
const [, setNewSuccessMessage] = useSuccess();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setPin(comment.pinned);
|
||||
setClose(comment.locked);
|
||||
}, [comment]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setDeleteThread(deletePost);
|
||||
}, [deletePost]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
setDeleteThread(deletePost);
|
||||
}
|
||||
}, [isOpen, deletePost]);
|
||||
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setDeleteThread(false);
|
||||
closeModal();
|
||||
};
|
||||
|
||||
|
||||
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 onChallenge = async (challenges, comment) => {
|
||||
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 comment.publishChallengeAnswers(challengeAnswers)
|
||||
await comment.publishChallengeAnswers(challengeAnswers);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const getChallengeAnswersFromUser = async (challenges) => {
|
||||
setChallengesArray(challenges);
|
||||
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const imageString = challenges?.challenges[0].challenge;
|
||||
const imageSource = `data:image/png;base64,${imageString}`;
|
||||
const challengeImg = new Image();
|
||||
challengeImg.src = imageSource;
|
||||
|
||||
|
||||
challengeImg.onload = () => {
|
||||
setIsCaptchaOpen(true);
|
||||
|
||||
|
||||
const handleKeyDown = async (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
const currentCaptchaResponse = useGeneralStore.getState().captchaResponse;
|
||||
@@ -108,26 +93,24 @@ const ModerationModal = ({ isOpen, closeModal, deletePost }) => {
|
||||
|
||||
setResolveCaptchaPromise(resolve);
|
||||
};
|
||||
|
||||
|
||||
challengeImg.onerror = () => {
|
||||
reject(setNewErrorMessage('Could not load challenges'));
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
const [publishCommentEditOptions, setPublishCommentEditOptions] = useState({
|
||||
commentCid: moderatingCommentCid,
|
||||
subplebbitAddress: selectedAddress,
|
||||
onChallenge,
|
||||
onChallengeVerification,
|
||||
onError: (error) => {
|
||||
setNewErrorMessage(error.message); console.log(error);
|
||||
setNewErrorMessage(error.message);
|
||||
console.log(error);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAddress) {
|
||||
setPublishCommentEditOptions((prevOptions) => ({
|
||||
@@ -136,11 +119,9 @@ const ModerationModal = ({ isOpen, closeModal, deletePost }) => {
|
||||
}));
|
||||
}
|
||||
}, [selectedAddress]);
|
||||
|
||||
|
||||
|
||||
const { publishCommentEdit } = usePublishCommentEdit(publishCommentEditOptions);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setPublishCommentEditOptions((prevOptions) => ({
|
||||
...prevOptions,
|
||||
@@ -148,7 +129,6 @@ const ModerationModal = ({ isOpen, closeModal, deletePost }) => {
|
||||
}));
|
||||
}, [moderatingCommentCid]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
let isActive = true;
|
||||
if (publishCommentEditOptions && triggerPublishCommentEdit) {
|
||||
@@ -165,80 +145,64 @@ const ModerationModal = ({ isOpen, closeModal, deletePost }) => {
|
||||
};
|
||||
}, [triggerPublishCommentEdit, publishCommentEdit, publishCommentEditOptions]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<StyledModal
|
||||
isOpen={isOpen}
|
||||
onRequestClose={handleCloseModal}
|
||||
contentLabel="Moderator Tools"
|
||||
style={{ overlay: { backgroundColor: "rgba(0,0,0,.25)" }}}
|
||||
contentLabel='Moderator Tools'
|
||||
style={{ overlay: { backgroundColor: 'rgba(0,0,0,.25)' } }}
|
||||
selectedStyle={selectedStyle}
|
||||
>
|
||||
<div className="panel">
|
||||
<div className="panel-header">
|
||||
<div className='panel'>
|
||||
<div className='panel-header'>
|
||||
Moderator Tools
|
||||
<Link to="" onClick={handleCloseModal}>
|
||||
<span className="icon" title="close" />
|
||||
<Link to='' onClick={handleCloseModal}>
|
||||
<span className='icon' title='close' />
|
||||
</Link>
|
||||
</div>
|
||||
<ul className="settings-cat">
|
||||
<li className="settings-cat-lbl">
|
||||
<ul className='settings-cat'>
|
||||
<li className='settings-cat-lbl'>
|
||||
<label>
|
||||
<input type="checkbox" style={{marginRight: "10px"}}
|
||||
checked={pin} onChange={() => setPin(!pin)} />
|
||||
<input type='checkbox' style={{ marginRight: '10px' }} checked={pin} onChange={() => setPin(!pin)} />
|
||||
Pin post
|
||||
</label>
|
||||
</li>
|
||||
<li className="settings-tip">
|
||||
Pin the post to make it a sticky, showed at the top of the board even as new posts are submitted.
|
||||
</li>
|
||||
</ul>
|
||||
<ul className="settings-cat">
|
||||
<li className="settings-cat-lbl">
|
||||
<label>
|
||||
<input type="checkbox" style={{marginRight: "10px"}}
|
||||
checked={deleteThread} onChange={() => setDeleteThread(!deleteThread)} />
|
||||
Delete post
|
||||
</label>
|
||||
</li>
|
||||
<li className="settings-tip">
|
||||
The post will no longer visible to other users, but the person who posted it can still see it in their own account.
|
||||
</li>
|
||||
<li className='settings-tip'>Pin the post to make it a sticky, showed at the top of the board even as new posts are submitted.</li>
|
||||
</ul>
|
||||
<ul className="settings-cat">
|
||||
<li className="settings-cat-lbl">
|
||||
<label>
|
||||
<input type="checkbox" style={{marginRight: "10px"}}
|
||||
checked={close} onChange={() => setClose(!close)} />
|
||||
<ul className='settings-cat'>
|
||||
<li className='settings-cat-lbl'>
|
||||
<label>
|
||||
<input type='checkbox' style={{ marginRight: '10px' }} checked={deleteThread} onChange={() => setDeleteThread(!deleteThread)} />
|
||||
Delete post
|
||||
</label>
|
||||
</li>
|
||||
<li className='settings-tip'>The post will no longer visible to other users, but the person who posted it can still see it in their own account.</li>
|
||||
</ul>
|
||||
<ul className='settings-cat'>
|
||||
<li className='settings-cat-lbl'>
|
||||
<label>
|
||||
<input type='checkbox' style={{ marginRight: '10px' }} checked={close} onChange={() => setClose(!close)} />
|
||||
Close post
|
||||
</label>
|
||||
</li>
|
||||
<li className="settings-tip">
|
||||
Closing a post allows users to still see the content, but they cannot add any new replies to it.
|
||||
</label>
|
||||
</li>
|
||||
<li className='settings-tip'>Closing a post allows users to still see the content, but they cannot add any new replies to it.</li>
|
||||
</ul>
|
||||
<ul className="settings-cat">
|
||||
<li className="settings-option disc">
|
||||
Reason
|
||||
</li>
|
||||
<li className="settings-tip">
|
||||
Help people become better posters by giving a short reason why their post was removed.
|
||||
</li>
|
||||
<li className="settings-input" style={{marginTop: "-10px"}}>
|
||||
<textarea value={reason} placeholder="Enter reason here..."
|
||||
onChange={e => setReason(e.target.value)}/>
|
||||
<ul className='settings-cat'>
|
||||
<li className='settings-option disc'>Reason</li>
|
||||
<li className='settings-tip'>Help people become better posters by giving a short reason why their post was removed.</li>
|
||||
<li className='settings-input' style={{ marginTop: '-10px' }}>
|
||||
<textarea value={reason} placeholder='Enter reason here...' onChange={(e) => setReason(e.target.value)} />
|
||||
</li>
|
||||
</ul>
|
||||
<button
|
||||
className="save-button"
|
||||
className='save-button'
|
||||
onClick={async () => {
|
||||
setPublishCommentEditOptions(prevOptions => ({
|
||||
setPublishCommentEditOptions((prevOptions) => ({
|
||||
...prevOptions,
|
||||
pinned: pin,
|
||||
removed: deleteThread,
|
||||
locked: close,
|
||||
reason: reason
|
||||
reason: reason,
|
||||
}));
|
||||
setTriggerPublishCommentEdit(true);
|
||||
setIsModEdit(true);
|
||||
@@ -250,8 +214,8 @@ const ModerationModal = ({ isOpen, closeModal, deletePost }) => {
|
||||
</div>
|
||||
</StyledModal>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Modal.setAppElement("#root");
|
||||
Modal.setAppElement('#root');
|
||||
|
||||
export default ModerationModal;
|
||||
export default ModerationModal;
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
import React, {useState, useEffect} from "react";
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { StyledModal } from '../styled/modals/ReplyModal.styled';
|
||||
import useGeneralStore from "../../hooks/stores/useGeneralStore";
|
||||
import Modal from "react-modal";
|
||||
import Draggable from "react-draggable";
|
||||
import getDate from "../../utils/getDate";
|
||||
|
||||
import useGeneralStore from '../../hooks/stores/useGeneralStore';
|
||||
import Modal from 'react-modal';
|
||||
import Draggable from 'react-draggable';
|
||||
import getDate from '../../utils/getDate';
|
||||
|
||||
const OriginalCommentModal = ({ isOpen, closeModal, comment }) => {
|
||||
const {
|
||||
selectedStyle,
|
||||
} = useGeneralStore(state => state);
|
||||
const { selectedStyle } = useGeneralStore((state) => state);
|
||||
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth <= 480);
|
||||
const nodeRef = React.useRef(null);
|
||||
const originalCommentContent = comment.original?.content;
|
||||
const timestamp = getDate(comment.timestamp);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => setIsMobile(window.innerWidth <= 480);
|
||||
window.addEventListener('resize', handleResize);
|
||||
@@ -26,31 +22,33 @@ const OriginalCommentModal = ({ isOpen, closeModal, comment }) => {
|
||||
};
|
||||
}, [setIsMobile]);
|
||||
|
||||
|
||||
return (
|
||||
<StyledModal
|
||||
isOpen={isOpen}
|
||||
onRequestClose={closeModal}
|
||||
contentLabel="Original Comment"
|
||||
contentLabel='Original Comment'
|
||||
shouldCloseOnEsc={false}
|
||||
shouldCloseOnOverlayClick={isMobile}
|
||||
selectedStyle={selectedStyle}
|
||||
style={isMobile ? ({ overlay: { backgroundColor: "rgba(0,0,0,.25)" }}) : ({ overlay: { backgroundColor: "rgba(0,0,0,0)" }})}>
|
||||
<Draggable handle=".modal-header" nodeRef={nodeRef} disabled={isMobile}>
|
||||
<div className="modal-content" ref={nodeRef}>
|
||||
<div className="modal-header">
|
||||
style={isMobile ? { overlay: { backgroundColor: 'rgba(0,0,0,.25)' } } : { overlay: { backgroundColor: 'rgba(0,0,0,0)' } }}
|
||||
>
|
||||
<Draggable handle='.modal-header' nodeRef={nodeRef} disabled={isMobile}>
|
||||
<div className='modal-content' ref={nodeRef}>
|
||||
<div className='modal-header'>
|
||||
Original at {timestamp}
|
||||
<button className="icon" onClick={() => closeModal()} title="close" />
|
||||
<button className='icon' onClick={() => closeModal()} title='close' />
|
||||
</div>
|
||||
<div id="form">
|
||||
<div className="textarea-wrapper">
|
||||
<textarea className="textarea"
|
||||
rows="4"
|
||||
<div id='form'>
|
||||
<div className='textarea-wrapper'>
|
||||
<textarea
|
||||
className='textarea'
|
||||
rows='4'
|
||||
style={{ paddingTop: '0' }}
|
||||
placeholder="Comment"
|
||||
placeholder='Comment'
|
||||
defaultValue={originalCommentContent}
|
||||
wrap="soft"
|
||||
readOnly={true} />
|
||||
wrap='soft'
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -61,4 +59,4 @@ const OriginalCommentModal = ({ isOpen, closeModal, comment }) => {
|
||||
|
||||
Modal.setAppElement('#root');
|
||||
|
||||
export default OriginalCommentModal;
|
||||
export default OriginalCommentModal;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import styled from "styled-components";
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const BoardStatsContainer = styled.div`
|
||||
|
||||
@media (max-width: 480px) {
|
||||
display: none;
|
||||
}
|
||||
@@ -11,14 +10,14 @@ export const BoardStatsContainer = styled.div`
|
||||
margin: auto;
|
||||
line-height: 1;
|
||||
margin-top: -1px;
|
||||
|
||||
|
||||
table {
|
||||
border-spacing: 0px;
|
||||
width: 100%;
|
||||
margin-top: -1px;
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
|
||||
tr {
|
||||
vertical-align: top;
|
||||
font-size: 11px;
|
||||
@@ -94,9 +93,9 @@ export const BoardStatsContainer = styled.div`
|
||||
.hide-button:hover {
|
||||
color: #ff3300 !important;
|
||||
}`;
|
||||
|
||||
|
||||
default:
|
||||
return '';
|
||||
return '';
|
||||
}
|
||||
}}
|
||||
`;
|
||||
`;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ToastContainer } from "react-toastify";
|
||||
import styled from "styled-components";
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Toast = styled(ToastContainer)`
|
||||
.Toastify__toast {
|
||||
@@ -9,4 +9,4 @@ export const Toast = styled(ToastContainer)`
|
||||
border: 1px solid #fee9cd;
|
||||
color: #c5c8c6;
|
||||
}
|
||||
`;
|
||||
`;
|
||||
|
||||
@@ -16,16 +16,13 @@ export const AlertModal = styled.div`
|
||||
& > button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
||||
& > button:first-child {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
${({ selectedStyle }) => {
|
||||
switch (selectedStyle) {
|
||||
case 'Yotsuba':
|
||||
@@ -68,10 +65,10 @@ export const AlertModal = styled.div`
|
||||
.author-delete-alert {
|
||||
background-color: #ddd;
|
||||
border: 1px solid #ccc;
|
||||
}`;
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}`;
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}}
|
||||
`;
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user