mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
Merge branch 'master' of https://github.com/plebbit/plebchan
This commit is contained in:
@@ -21,6 +21,7 @@
|
|||||||
"eslint": "8.36.0",
|
"eslint": "8.36.0",
|
||||||
"eslint-config-react-app": "7.0.1",
|
"eslint-config-react-app": "7.0.1",
|
||||||
"ext-name": "5.0.0",
|
"ext-name": "5.0.0",
|
||||||
|
"json-stringify-pretty-compact": "^4.0.0",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"mock-require": "3.0.3",
|
"mock-require": "3.0.3",
|
||||||
"postcss": "8.4.21",
|
"postcss": "8.4.21",
|
||||||
|
|||||||
@@ -0,0 +1,326 @@
|
|||||||
|
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 useGeneralStore from '../hooks/stores/useGeneralStore';
|
||||||
|
|
||||||
|
const BoardSettings = ({ subplebbit }) => {
|
||||||
|
const {
|
||||||
|
setCaptchaResponse,
|
||||||
|
setChallengesArray,
|
||||||
|
setIsCaptchaOpen,
|
||||||
|
setResolveCaptchaPromise,
|
||||||
|
selectedAddress,
|
||||||
|
selectedStyle,
|
||||||
|
} = useGeneralStore(state => state);
|
||||||
|
|
||||||
|
const allowedSettings = {
|
||||||
|
address: subplebbit.address,
|
||||||
|
apiUrl: subplebbit.apiUrl,
|
||||||
|
description: subplebbit.description,
|
||||||
|
pubsubTopic: subplebbit.pubsubTopic,
|
||||||
|
settings: {
|
||||||
|
fetchThumbnailUrls: subplebbit.settings?.fetchThumbnailUrls,
|
||||||
|
fetchThumbnailUrlsProxyUrl: subplebbit.settings?.fetchThumbnailUrlsProxyUrl,
|
||||||
|
},
|
||||||
|
roles: subplebbit.roles,
|
||||||
|
rules: subplebbit.rules,
|
||||||
|
suggested: {
|
||||||
|
avatarUrl: subplebbit.suggested?.avatarUrl,
|
||||||
|
backgroundUrl: subplebbit.suggested?.backgroundUrl,
|
||||||
|
bannerUrl: subplebbit.suggested?.bannerUrl,
|
||||||
|
language: subplebbit.suggested?.language,
|
||||||
|
primaryColor: subplebbit.suggested?.primaryColor,
|
||||||
|
secondaryColor: subplebbit.suggested?.secondaryColor,
|
||||||
|
},
|
||||||
|
title: subplebbit.title,
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateSettingsFromSubplebbit = (subplebbitData) => ({
|
||||||
|
address: subplebbitData.address,
|
||||||
|
apiUrl: subplebbitData.apiUrl,
|
||||||
|
description: subplebbitData.description,
|
||||||
|
pubsubTopic: subplebbitData.pubsubTopic,
|
||||||
|
settings: {
|
||||||
|
fetchThumbnailUrls: subplebbitData.settings?.fetchThumbnailUrls,
|
||||||
|
fetchThumbnailUrlsProxyUrl: subplebbitData.settings?.fetchThumbnailUrlsProxyUrl,
|
||||||
|
},
|
||||||
|
roles: subplebbitData.roles,
|
||||||
|
rules: subplebbitData.rules,
|
||||||
|
suggested: {
|
||||||
|
avatarUrl: subplebbitData.suggested?.avatarUrl,
|
||||||
|
backgroundUrl: subplebbitData.suggested?.backgroundUrl,
|
||||||
|
bannerUrl: subplebbitData.suggested?.bannerUrl,
|
||||||
|
language: subplebbitData.suggested?.language,
|
||||||
|
primaryColor: subplebbitData.suggested?.primaryColor,
|
||||||
|
secondaryColor: subplebbitData.suggested?.secondaryColor,
|
||||||
|
},
|
||||||
|
title: subplebbitData.title,
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialSettings = generateSettingsFromSubplebbit(subplebbit);
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [boardSettingsJson, setBoardSettingsJson] = useState(JSON.stringify(initialSettings, null, 2));
|
||||||
|
const [triggerPublilshSubplebbitEdit, setTriggerPublishCommentEdit] = useState(false);
|
||||||
|
|
||||||
|
const [, setNewErrorMessage] = useError();
|
||||||
|
const [, setNewSuccessMessage] = useSuccess();
|
||||||
|
|
||||||
|
|
||||||
|
const getDifferences = (oldObj, newObj) => {
|
||||||
|
let differences = {};
|
||||||
|
|
||||||
|
for (let key in oldObj) {
|
||||||
|
if (typeof oldObj[key] === 'object' && oldObj[key] !== null) {
|
||||||
|
const nestedDifferences = getDifferences(oldObj[key], newObj[key] || {});
|
||||||
|
if (Object.keys(nestedDifferences).length > 0) {
|
||||||
|
differences[key] = nestedDifferences;
|
||||||
|
}
|
||||||
|
} else if (oldObj[key] !== newObj[key]) {
|
||||||
|
differences[key] = newObj[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let key in newObj) {
|
||||||
|
if (!oldObj.hasOwnProperty(key)) {
|
||||||
|
differences[key] = newObj[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return differences;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const isInitialMount = useRef(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isInitialMount.current) {
|
||||||
|
setBoardSettingsJson(JSON.stringify(generateSettingsFromSubplebbit(subplebbit), null, 2));
|
||||||
|
isInitialMount.current = false;
|
||||||
|
}
|
||||||
|
}, [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])) {
|
||||||
|
throw new Error(`Expected ${key} to be an object in allowedSettings`);
|
||||||
|
}
|
||||||
|
validateSettings(updatedSettings[key], allowedSettings[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const onChallenge = async (challenges, subplebbitEdit) => {
|
||||||
|
let challengeAnswers = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
challengeAnswers = await getChallengeAnswersFromUser(challenges)
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
setNewErrorMessage(error.message); console.log(error);
|
||||||
|
}
|
||||||
|
if (challengeAnswers) {
|
||||||
|
await subplebbitEdit.publishChallengeAnswers(challengeAnswers)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const onChallengeVerification = (challengeVerification) => {
|
||||||
|
if (challengeVerification.challengeSuccess === true) {
|
||||||
|
setNewSuccessMessage('Challenge Success');
|
||||||
|
} 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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
resolve(currentCaptchaResponse);
|
||||||
|
setIsCaptchaOpen(false);
|
||||||
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setCaptchaResponse('');
|
||||||
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
|
|
||||||
|
setResolveCaptchaPromise(resolve);
|
||||||
|
};
|
||||||
|
|
||||||
|
challengeImg.onerror = () => {
|
||||||
|
reject(setNewErrorMessage('Could not load challenges'));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const [editSubplebbitOptions, setEditSubplebbitOptions] = useState({
|
||||||
|
subplebbitAddress: selectedAddress,
|
||||||
|
onChallenge,
|
||||||
|
onChallengeVerification,
|
||||||
|
onError: (error) => {
|
||||||
|
setNewErrorMessage(error.message); console.log(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const { publishSubplebbitEdit } = usePublishSubplebbitEdit(editSubplebbitOptions);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let isActive = true;
|
||||||
|
if (editSubplebbitOptions && triggerPublilshSubplebbitEdit) {
|
||||||
|
(async () => {
|
||||||
|
await publishSubplebbitEdit(editSubplebbitOptions);
|
||||||
|
if (isActive) {
|
||||||
|
setTriggerPublishCommentEdit(false);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isActive = false;
|
||||||
|
};
|
||||||
|
}, [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 => ({
|
||||||
|
...prevOptions,
|
||||||
|
...changes
|
||||||
|
}));
|
||||||
|
setTriggerPublishCommentEdit(true);
|
||||||
|
} else {
|
||||||
|
setNewErrorMessage("No changes detected");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setNewErrorMessage(`Error saving changes: ${error}`);
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handleResetChanges = () => {
|
||||||
|
setBoardSettingsJson(JSON.stringify(initialSettings, null, 2));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function generateSettingsList(settingsObj, parentKey = '') {
|
||||||
|
let result = [];
|
||||||
|
|
||||||
|
for (let key in settingsObj) {
|
||||||
|
if (typeof settingsObj[key] === 'object' && settingsObj[key] !== null) {
|
||||||
|
const nestedItems = generateSettingsList(settingsObj[key], `${parentKey}${key}.`);
|
||||||
|
if (nestedItems.length > 1) {
|
||||||
|
result.push(`${parentKey}${key}: { ${nestedItems.join(', ')} }`);
|
||||||
|
} else {
|
||||||
|
result.push(...nestedItems);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.push(`${parentKey}${key}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const possibleSettingsList = generateSettingsList(initialSettings);
|
||||||
|
|
||||||
|
|
||||||
|
const handleCloseModal = () => {
|
||||||
|
setIsModalOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StyledModal
|
||||||
|
isOpen={isModalOpen}
|
||||||
|
onRequestClose={handleCloseModal}
|
||||||
|
contentLabel="Board Settings"
|
||||||
|
style={{ overlay: { backgroundColor: "rgba(0,0,0,.25)" }}}
|
||||||
|
selectedStyle={selectedStyle}
|
||||||
|
>
|
||||||
|
<div className="panel-board">
|
||||||
|
<div className="panel-header">
|
||||||
|
Board Settings
|
||||||
|
<Link to="" onClick={handleCloseModal}>
|
||||||
|
<span className="icon" title="close" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div className="settings-info">
|
||||||
|
<div>
|
||||||
|
<strong>Allowed settings: </strong>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<textarea
|
||||||
|
value={boardSettingsJson}
|
||||||
|
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>
|
||||||
|
</div>
|
||||||
|
</StyledModal>
|
||||||
|
[
|
||||||
|
<span id="subscribe" style={{ cursor: 'pointer' }}>
|
||||||
|
<span
|
||||||
|
onClick={() => {
|
||||||
|
window.electron && window.electron.isElectron
|
||||||
|
? setIsModalOpen(true)
|
||||||
|
: 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'
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Settings
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
]
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BoardSettings;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React from "react";
|
||||||
import { useAccountComment } from "@plebbit/plebbit-react-hooks";
|
import { useAccountComment } from "@plebbit/plebbit-react-hooks";
|
||||||
import useStateString from "../hooks/useStateString";
|
import useStateString from "../hooks/useStateString";
|
||||||
|
|
||||||
@@ -6,31 +6,27 @@ const StateLabel = ({ commentIndex, className }) => {
|
|||||||
const comment = useAccountComment({commentIndex: commentIndex});
|
const comment = useAccountComment({commentIndex: commentIndex});
|
||||||
const stateString = useStateString(comment);
|
const stateString = useStateString(comment);
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
if (comment.updatedAt !== undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
if (comment.state === "failed") {
|
||||||
const timer = setTimeout(() => setIsLoading(false), 2000);
|
return null;
|
||||||
return () => clearTimeout(timer);
|
}
|
||||||
}, [commentIndex]);
|
|
||||||
|
if (!stateString) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
commentIndex !== undefined && stateString !== "Succeeded" ? (
|
<span className="ttl">
|
||||||
(comment.state === "failed") ? (
|
<br />
|
||||||
null
|
(
|
||||||
) : (
|
<span className={className}>
|
||||||
stateString === undefined && !isLoading && comment.cid === undefined ? (
|
{stateString}
|
||||||
null
|
|
||||||
) : (
|
|
||||||
<span className="ttl">
|
|
||||||
<br />
|
|
||||||
(
|
|
||||||
<span className={className}>
|
|
||||||
{stateString}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
</span>
|
</span>
|
||||||
))
|
)
|
||||||
) : null
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ const ModerationModal = ({ isOpen, closeModal, deletePost }) => {
|
|||||||
|
|
||||||
const comment = useComment({commentCid: moderatingCommentCid});
|
const comment = useComment({commentCid: moderatingCommentCid});
|
||||||
|
|
||||||
const [pin, setPin] = useState(comment?.pinned);
|
const [pin, setPin] = useState(comment.pinned);
|
||||||
const [deleteThread, setDeleteThread] = useState(deletePost);
|
const [deleteThread, setDeleteThread] = useState(deletePost);
|
||||||
const [close, setClose] = useState(comment?.locked);
|
const [close, setClose] = useState(comment.locked);
|
||||||
const [reason, setReason] = useState('');
|
const [reason, setReason] = useState('');
|
||||||
const [triggerPublishCommentEdit, setTriggerPublishCommentEdit] = useState(false);
|
const [triggerPublishCommentEdit, setTriggerPublishCommentEdit] = useState(false);
|
||||||
|
|
||||||
@@ -32,16 +32,24 @@ const ModerationModal = ({ isOpen, closeModal, deletePost }) => {
|
|||||||
const [, setNewSuccessMessage] = useSuccess();
|
const [, setNewSuccessMessage] = useSuccess();
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setPin(comment.pinned);
|
||||||
|
setClose(comment.locked);
|
||||||
|
}, [comment]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDeleteThread(deletePost);
|
setDeleteThread(deletePost);
|
||||||
}, [deletePost]);
|
}, [deletePost]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
setDeleteThread(deletePost);
|
setDeleteThread(deletePost);
|
||||||
}
|
}
|
||||||
}, [isOpen, deletePost]);
|
}, [isOpen, deletePost]);
|
||||||
|
|
||||||
|
|
||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
setDeleteThread(false);
|
setDeleteThread(false);
|
||||||
closeModal();
|
closeModal();
|
||||||
@@ -59,7 +67,6 @@ const ModerationModal = ({ isOpen, closeModal, deletePost }) => {
|
|||||||
|
|
||||||
|
|
||||||
const onChallenge = async (challenges, comment) => {
|
const onChallenge = async (challenges, comment) => {
|
||||||
|
|
||||||
let challengeAnswers = [];
|
let challengeAnswers = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from "react";
|
|||||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||||
import Modal from "react-modal";
|
import Modal from "react-modal";
|
||||||
import { deleteAccount, deleteCaches, exportAccount, importAccount, setAccount, setActiveAccount, useAccount, useAccounts, useResolvedAuthorAddress } from "@plebbit/plebbit-react-hooks";
|
import { deleteAccount, deleteCaches, exportAccount, importAccount, setAccount, setActiveAccount, useAccount, useAccounts, useResolvedAuthorAddress } from "@plebbit/plebbit-react-hooks";
|
||||||
|
import stringify from "json-stringify-pretty-compact"
|
||||||
import { StyledModal } from "../styled/modals/SettingsModal.styled";
|
import { StyledModal } from "../styled/modals/SettingsModal.styled";
|
||||||
import useError from "../../hooks/useError";
|
import useError from "../../hooks/useError";
|
||||||
import useSuccess from "../../hooks/useSuccess";
|
import useSuccess from "../../hooks/useSuccess";
|
||||||
@@ -74,18 +75,31 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
if (account) {
|
if (account) {
|
||||||
const fetchAccountData = async () => {
|
const fetchAccountData = async () => {
|
||||||
const data = await exportAccount();
|
const data = await exportAccount();
|
||||||
setAccountJson(data);
|
try {
|
||||||
|
const parsedData = JSON.parse(data);
|
||||||
|
setAccountJson(stringify(parsedData));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to pretty-print the JSON:", error);
|
||||||
|
setAccountJson(data);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchAccountData();
|
fetchAccountData();
|
||||||
}
|
}
|
||||||
}, [account]);
|
}, [account]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleAccountJsonChange = (e) => {
|
const handleAccountJsonChange = (e) => {
|
||||||
setAccountJson(e.target.value);
|
try {
|
||||||
|
const parsedData = JSON.parse(e.target.value);
|
||||||
|
setAccountJson(stringify(parsedData));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to pretty-print the JSON:", error);
|
||||||
|
setAccountJson(e.target.value);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (checkedENS && resolvedAddress && state === 'succeeded') {
|
if (checkedENS && resolvedAddress && state === 'succeeded') {
|
||||||
@@ -375,7 +389,6 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
try {
|
try {
|
||||||
const data = await exportAccount();
|
const data = await exportAccount();
|
||||||
setAccountJson(data);
|
setAccountJson(data);
|
||||||
setNewSuccessMessage("Account Data Reset Successfully");
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setNewErrorMessage("Error resetting account data: " + error.message);
|
setNewErrorMessage("Error resetting account data: " + error.message);
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -503,7 +516,10 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
<textarea id="account-data-text"
|
<textarea id="account-data-text"
|
||||||
value={accountJson}
|
value={accountJson}
|
||||||
ref={importRef}
|
ref={importRef}
|
||||||
onChange={handleAccountJsonChange} />
|
onChange={handleAccountJsonChange}
|
||||||
|
autoComplete="off"
|
||||||
|
autoCorrect="off"
|
||||||
|
spellCheck="false" />
|
||||||
<div className="account-buttons">
|
<div className="account-buttons">
|
||||||
<button onClick={handleSaveAccount}>
|
<button onClick={handleSaveAccount}>
|
||||||
Save
|
Save
|
||||||
|
|||||||
@@ -22,6 +22,31 @@ export const StyledModal = styled(Modal)`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.panel-board {
|
||||||
|
top: 120px;
|
||||||
|
width: 700px;
|
||||||
|
height: auto;
|
||||||
|
max-height: 80%;
|
||||||
|
left: calc(50% - 350px);
|
||||||
|
overflow-y: auto;
|
||||||
|
position: absolute;
|
||||||
|
padding: 2px 5px 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
box-shadow: 0 0 5px rgba(0, 0, 0, .25);
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
width: 80%;
|
||||||
|
max-height: 60%;
|
||||||
|
left: calc(50% - 40%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
width: 320px;
|
||||||
|
max-height: 60%;
|
||||||
|
left: calc(50% - 160px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.panel-header {
|
.panel-header {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -124,6 +149,13 @@ export const StyledModal = styled(Modal)`
|
|||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.board-settings {
|
||||||
|
width: 83%;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
.save-button {
|
.save-button {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
@@ -132,11 +164,34 @@ export const StyledModal = styled(Modal)`
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#save-board-settings, #reset-board-settings {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-info {
|
||||||
|
overflow-x: auto;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 10px 10px 0 10px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
line-break: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-info strong {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
${({ selectedStyle }) => {
|
${({ selectedStyle }) => {
|
||||||
switch (selectedStyle) {
|
switch (selectedStyle) {
|
||||||
case 'Yotsuba':
|
case 'Yotsuba':
|
||||||
return `
|
return `
|
||||||
.panel {
|
.panel, .panel-board {
|
||||||
background-color: #f0e0d6;
|
background-color: #f0e0d6;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +221,7 @@ export const StyledModal = styled(Modal)`
|
|||||||
|
|
||||||
case 'Yotsuba-B':
|
case 'Yotsuba-B':
|
||||||
return `
|
return `
|
||||||
.panel {
|
.panel, .panel-board {
|
||||||
background-color: #d6daf0;
|
background-color: #d6daf0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +251,7 @@ export const StyledModal = styled(Modal)`
|
|||||||
|
|
||||||
case 'Futaba':
|
case 'Futaba':
|
||||||
return `
|
return `
|
||||||
.panel {
|
.panel, .panel-board {
|
||||||
background-color: #f0e0d6;
|
background-color: #f0e0d6;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +281,7 @@ export const StyledModal = styled(Modal)`
|
|||||||
|
|
||||||
case 'Burichan':
|
case 'Burichan':
|
||||||
return `
|
return `
|
||||||
.panel {
|
.panel, .panel-board {
|
||||||
background-color: #d6daf0;
|
background-color: #d6daf0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +311,7 @@ export const StyledModal = styled(Modal)`
|
|||||||
|
|
||||||
case 'Tomorrow':
|
case 'Tomorrow':
|
||||||
return `
|
return `
|
||||||
.panel {
|
.panel, .panel-board {
|
||||||
background-color: #282a2e;
|
background-color: #282a2e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,7 +351,7 @@ export const StyledModal = styled(Modal)`
|
|||||||
|
|
||||||
case 'Photon':
|
case 'Photon':
|
||||||
return `
|
return `
|
||||||
.panel {
|
.panel, .panel-board {
|
||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export const StyledModal = styled(Modal)`
|
|||||||
}
|
}
|
||||||
|
|
||||||
.settings-cat {
|
.settings-cat {
|
||||||
display: ${({ expanded }) => index => (expanded.includes(index) ? "block" : "none")};
|
display: ${({ expanded }) => index => (expanded?.includes(index) ? "block" : "none")};
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import EditLabel from '../EditLabel';
|
|||||||
import ImageBanner from '../ImageBanner';
|
import ImageBanner from '../ImageBanner';
|
||||||
import AdminListModal from '../modals/AdminListModal';
|
import AdminListModal from '../modals/AdminListModal';
|
||||||
import ModerationModal from '../modals/ModerationModal';
|
import ModerationModal from '../modals/ModerationModal';
|
||||||
|
import BoardSettings from '../BoardSettings';
|
||||||
import OfflineIndicator from '../OfflineIndicator';
|
import OfflineIndicator from '../OfflineIndicator';
|
||||||
import PendingLabel from '../PendingLabel';
|
import PendingLabel from '../PendingLabel';
|
||||||
import Post from '../Post';
|
import Post from '../Post';
|
||||||
@@ -157,8 +158,8 @@ const Board = () => {
|
|||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (subplebbit.roles !== undefined) {
|
if (subplebbit?.roles !== undefined) {
|
||||||
const role = subplebbit.roles[account?.author.address]?.role;
|
const role = subplebbit?.roles[account?.author.address]?.role;
|
||||||
|
|
||||||
if (role === 'moderator' || role === 'admin' || role === 'owner') {
|
if (role === 'moderator' || role === 'admin' || role === 'owner') {
|
||||||
setIsModerator(true);
|
setIsModerator(true);
|
||||||
@@ -166,7 +167,7 @@ const Board = () => {
|
|||||||
setIsModerator(false);
|
setIsModerator(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [account?.author.address, subplebbit.roles]);
|
}, [account?.author.address, subplebbit?.roles]);
|
||||||
|
|
||||||
|
|
||||||
const handleThumbnailClick = (index, type) => {
|
const handleThumbnailClick = (index, type) => {
|
||||||
@@ -717,7 +718,7 @@ const Board = () => {
|
|||||||
selectedStyle={selectedStyle}
|
selectedStyle={selectedStyle}
|
||||||
isOpen={isAdminListOpen}
|
isOpen={isAdminListOpen}
|
||||||
closeModal={() => setIsAdminListOpen(false)}
|
closeModal={() => setIsAdminListOpen(false)}
|
||||||
roles={subplebbit.roles} />
|
roles={subplebbit?.roles} />
|
||||||
<CreateBoardModal
|
<CreateBoardModal
|
||||||
selectedStyle={selectedStyle}
|
selectedStyle={selectedStyle}
|
||||||
isOpen={isCreateBoardOpen}
|
isOpen={isCreateBoardOpen}
|
||||||
@@ -902,6 +903,9 @@ const Board = () => {
|
|||||||
[
|
[
|
||||||
<Link to={`/p/${selectedAddress}/catalog`} onClick={()=> {window.scrollTo(0, 0)}}>Catalog</Link>
|
<Link to={`/p/${selectedAddress}/catalog`} onClick={()=> {window.scrollTo(0, 0)}}>Catalog</Link>
|
||||||
]
|
]
|
||||||
|
{subplebbit.roles && subplebbit?.roles[account?.author?.address]?.role === "admin" ? (
|
||||||
|
<BoardSettings subplebbit={subplebbit} />
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{subplebbit?.state === "succeeded" ? (
|
{subplebbit?.state === "succeeded" ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import ImageBanner from '../ImageBanner';
|
|||||||
import VerifiedAuthor from '../VerifiedAuthor';
|
import VerifiedAuthor from '../VerifiedAuthor';
|
||||||
import CreateBoardModal from '../modals/CreateBoardModal';
|
import CreateBoardModal from '../modals/CreateBoardModal';
|
||||||
import ModerationModal from '../modals/ModerationModal';
|
import ModerationModal from '../modals/ModerationModal';
|
||||||
|
import BoardSettings from '../BoardSettings';
|
||||||
import OfflineIndicator from '../OfflineIndicator';
|
import OfflineIndicator from '../OfflineIndicator';
|
||||||
import SettingsModal from '../modals/SettingsModal';
|
import SettingsModal from '../modals/SettingsModal';
|
||||||
import countLinks from '../../utils/countLinks';
|
import countLinks from '../../utils/countLinks';
|
||||||
@@ -1259,6 +1260,9 @@ const Catalog = () => {
|
|||||||
[
|
[
|
||||||
<Link to={`/p/${selectedAddress}`} onClick={()=> {window.scrollTo(0, 0)}}>Return</Link>
|
<Link to={`/p/${selectedAddress}`} onClick={()=> {window.scrollTo(0, 0)}}>Return</Link>
|
||||||
]
|
]
|
||||||
|
{subplebbit.roles && subplebbit?.roles[account?.author?.address]?.role === "admin" ? (
|
||||||
|
<BoardSettings subplebbit={subplebbit} />
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{subplebbit.state === "succeeded" ? (
|
{subplebbit.state === "succeeded" ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -9670,6 +9670,11 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
||||||
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
||||||
|
|
||||||
|
json-stringify-pretty-compact@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz#cf4844770bddee3cb89a6170fe4b00eee5dbf1d4"
|
||||||
|
integrity sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==
|
||||||
|
|
||||||
json-stringify-safe@^5.0.1:
|
json-stringify-safe@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||||
|
|||||||
Reference in New Issue
Block a user