diff --git a/src/components/modals/CreateBoardModal.jsx b/src/components/modals/CreateBoardModal.jsx index 3da4044b..1a16b58b 100644 --- a/src/components/modals/CreateBoardModal.jsx +++ b/src/components/modals/CreateBoardModal.jsx @@ -1,22 +1,34 @@ -import React, { useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useAccount, useCreateSubplebbit, + // useSubplebbits, useAccountSubplebbits + } from '@plebbit/plebbit-react-hooks'; import { StyledModal } from '../styled/modals/CreateBoardModal.styled'; import useGeneralStore from '../../hooks/stores/useGeneralStore'; import Modal from 'react-modal'; import useError from '../../hooks/useError'; +import useSuccess from '../../hooks/useSuccess'; const CreateBoardModal = ({ isOpen, closeModal }) => { const { selectedStyle } = useGeneralStore(state => state); - const nodeRef = useRef(null); + const account = useAccount(); + const navigate = useNavigate(); - const [boardTitle, setBoardTitle] = useState(''); + const nodeRef = useRef(null); + const ruleInputRef = useRef(null); + + const [, setNewErrorMessage] = useError(); + const [, setNewSuccessMessage] = useSuccess(); + + const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); + const [avatar, setAvatar] = useState(''); + const [moderators, setModerators] = useState(''); const [rule, setRule] = useState(''); const [rules, setRules] = useState([]); const [editIndex, setEditIndex] = useState(-1); - const [, setNewErrorMessage] = useError(); - const handleAddRule = () => { if (rule.trim() === '') { @@ -39,6 +51,13 @@ const CreateBoardModal = ({ isOpen, closeModal }) => { } + useEffect(() => { + if (ruleInputRef.current) { + ruleInputRef.current.focus(); + } + }, [rules]); + + const handleKeyDown = (e) => { if (e.key === 'Enter') { handleAddRule(); @@ -49,6 +68,7 @@ const CreateBoardModal = ({ isOpen, closeModal }) => { const handleEditRule = (index) => { setRule(rules[index]); setEditIndex(index); + ruleInputRef.current.focus(); } @@ -58,8 +78,88 @@ const CreateBoardModal = ({ isOpen, closeModal }) => { } setRules(rules.filter((_, i) => i !== index)); } + + + const createSubplebbitOptions = { + title: title || undefined, + description: description || undefined, + suggested: { + avatarUrl : avatar || undefined, + }, + roles: moderators || undefined, + rules: rules || undefined, + } + + + const resetFields = () => { + setTitle(''); + setDescription(''); + setAvatar(''); + 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))); + + if (invalidAddresses.length > 0) { + setNewErrorMessage("Invalid moderator addresses: " + invalidAddresses.join(", ")); + return; + } + + const roles = {}; + moderatorAddresses.forEach(addr => { + roles[addr] = { role: 'moderator' }; + }); + roles[account.author.address] = { role: 'admin' }; + + let createSubplebbitOptions = { + roles: roles, + }; + + if (title) { + createSubplebbitOptions.title = title; + } + + if (description) { + createSubplebbitOptions.description = description; + } + + if (avatar) { + createSubplebbitOptions.suggested = { + 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() + // const ownerSubplebbitAddresses = Object.keys(accountSubplebbits).map(subplebbitAddress => accountSubplebbits[subplebbitAddress].role === 'owner') + // const subplebbits = useSubplebbits({subplebbitAddresses: ownerSubplebbitAddresses}) + // console.log(subplebbits); return ( { setBoardTitle(e.target.value)} + value={title} + onChange={(e) => setTitle(e.target.value)} placeholder="Board Title" /> @@ -116,8 +216,8 @@ const CreateBoardModal = ({ isOpen, closeModal }) => { setBoardTitle(e.target.value)} + value={avatar} + onChange={(e) => setAvatar(e.target.value)} placeholder="https://example.com/image.png" /> @@ -128,11 +228,11 @@ const CreateBoardModal = ({ isOpen, closeModal }) => { Optional, let other users help you moderate your board.
- setBoardTitle(e.target.value)} + value={moderators} + onChange={(e) => setModerators(e.target.value)} placeholder='username.eth, 12D3KooW..., username2.eth' />
@@ -144,6 +244,7 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
{ ))} )} - +
@@ -180,4 +281,4 @@ const CreateBoardModal = ({ isOpen, closeModal }) => { Modal.setAppElement('#root'); -export default CreateBoardModal; +export default CreateBoardModal; \ No newline at end of file diff --git a/src/components/modals/SettingsModal.jsx b/src/components/modals/SettingsModal.jsx index dbbe614d..b2b3183b 100755 --- a/src/components/modals/SettingsModal.jsx +++ b/src/components/modals/SettingsModal.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect, useRef } from "react"; import { Link, useLocation, useNavigate } from "react-router-dom"; import Modal from "react-modal"; -import { deleteCaches, exportAccount, importAccount, setAccount, setActiveAccount, useAccount, useAccounts } from "@plebbit/plebbit-react-hooks"; +import { deleteCaches, exportAccount, importAccount, setAccount, setActiveAccount, useAccount, useAccounts, useResolvedAuthorAddress } from "@plebbit/plebbit-react-hooks"; import { StyledModal } from "../styled/modals/SettingsModal.styled"; import useError from "../../hooks/useError"; import useSuccess from "../../hooks/useSuccess"; @@ -22,6 +22,9 @@ const SettingsModal = ({ isOpen, closeModal }) => { const location = useLocation(); const [expanded, setExpanded] = useState([]); const [accountJson, setAccountJson] = useState(null); + const [copyStatus, setCopyStatus] = useState(false); + const [ensName, setEnsName] = useState(''); + const [checkedENS, setCheckedENS] = useState(false); const [, setNewErrorMessage] = useError(); const [, setNewSuccessMessage] = useSuccess(); @@ -37,6 +40,52 @@ const SettingsModal = ({ isOpen, closeModal }) => { const dataPathRef = useRef(); const importRef = useRef(); const nameRef = useRef(); + const ensRef = useRef(); + + const author = {...account?.author, address: ensName}; + const { resolvedAddress, state } = useResolvedAuthorAddress({ author, cache: false }); + + + useEffect(() => { + if (checkedENS && resolvedAddress && state === 'succeeded') { + setCheckedENS(false); + } + }, [checkedENS, state, resolvedAddress]); + + + + const handleENSChange = () => { + const newEnsName = ensRef.current.value; + setEnsName(newEnsName); + }; + + const handleENSCheck = () => { + const newEnsName = ensRef.current.value; + setEnsName(newEnsName); + setCheckedENS(true); + }; + + const handleENSSave = async () => { + if (resolvedAddress === account?.signer?.address) { + try { + await setAccount({ + ...account, + author: { + ...account?.author, + address: ensName, + }, + }); + setNewSuccessMessage("ENS Name Saved"); + + } catch (error) { + setNewErrorMessage(error.message); + console.log(error); + } + } else if (resolvedAddress !== account?.signer?.address) { + setNewErrorMessage(`Failed resolving address, ${ensName}`); + } + }; + const defaultGatewayUrls = [ 'https://ipfs.io', @@ -60,6 +109,7 @@ const SettingsModal = ({ isOpen, closeModal }) => { 'https://polygon-rpc.com', ]; + const isValidURL = (url) => { try { new URL(url); @@ -70,6 +120,32 @@ const SettingsModal = ({ isOpen, closeModal }) => { }; + const handleCopyAddress = async () => { + try { + await navigator.clipboard.writeText(account?.author.address); + setCopyStatus(true); + } catch (err) { + console.error('Failed to copy text: ', err); + } + }; + + + useEffect(() => { + let copyStatusTimeoutId; + let checkedENSTimeoutId; + if (copyStatus) { + copyStatusTimeoutId = setTimeout(() => setCopyStatus(false), 3000); + } + if (checkedENS) { + checkedENSTimeoutId = setTimeout(() => setCheckedENS(false), 5000); + } + return () => { + clearTimeout(copyStatusTimeoutId); + clearTimeout(checkedENSTimeoutId); + }; + }, [copyStatus, checkedENS]); + + const handleSavePlebbitOptions = async () => { let gatewayUrls = gatewayRef.current.value.split('\n').filter(url => url.trim()); let ipfsClientsOptions = ipfsRef.current.value.split('\n').filter(url => url.trim()); @@ -151,7 +227,7 @@ const SettingsModal = ({ isOpen, closeModal }) => { await setAccount({ ...account, plebbitOptions: { - ...account.plebbitOptions, + ...account?.plebbitOptions, chainProviders, }, }); @@ -211,7 +287,7 @@ const SettingsModal = ({ isOpen, closeModal }) => { await setAccount({ ...account, plebbitOptions: { - ...account.plebbitOptions, + ...account?.plebbitOptions, chainProviders, }, }); @@ -297,9 +373,9 @@ const SettingsModal = ({ isOpen, closeModal }) => { try { await setAccount({ ...account, - name: name || account.name, + name: name || account?.name, author: { - ...account.author, + ...account?.author, displayName: name, }}); setNewSuccessMessage("Account Name Saved"); @@ -353,6 +429,11 @@ const SettingsModal = ({ isOpen, closeModal }) => {
+ +