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 ( <>
Board Settings
Allowed settings: {`{ ${possibleSettingsList.join(', ')} }`}
API docs: https://github.com/plebbit/plebbit-js#readme