mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
Merge branch 'master' of github.com:plebbit/plebchan
This commit is contained in:
@@ -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 { StyledModal } from '../styled/modals/CreateBoardModal.styled';
|
||||||
import useGeneralStore from '../../hooks/stores/useGeneralStore';
|
import useGeneralStore from '../../hooks/stores/useGeneralStore';
|
||||||
import Modal from 'react-modal';
|
import Modal from 'react-modal';
|
||||||
import useError from '../../hooks/useError';
|
import useError from '../../hooks/useError';
|
||||||
|
import useSuccess from '../../hooks/useSuccess';
|
||||||
|
|
||||||
const CreateBoardModal = ({ isOpen, closeModal }) => {
|
const CreateBoardModal = ({ isOpen, closeModal }) => {
|
||||||
const { selectedStyle } = useGeneralStore(state => state);
|
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 [description, setDescription] = useState('');
|
||||||
|
const [avatar, setAvatar] = useState('');
|
||||||
|
const [moderators, setModerators] = useState('');
|
||||||
const [rule, setRule] = useState('');
|
const [rule, setRule] = useState('');
|
||||||
const [rules, setRules] = useState([]);
|
const [rules, setRules] = useState([]);
|
||||||
const [editIndex, setEditIndex] = useState(-1);
|
const [editIndex, setEditIndex] = useState(-1);
|
||||||
|
|
||||||
const [, setNewErrorMessage] = useError();
|
|
||||||
|
|
||||||
|
|
||||||
const handleAddRule = () => {
|
const handleAddRule = () => {
|
||||||
if (rule.trim() === '') {
|
if (rule.trim() === '') {
|
||||||
@@ -39,6 +51,13 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (ruleInputRef.current) {
|
||||||
|
ruleInputRef.current.focus();
|
||||||
|
}
|
||||||
|
}, [rules]);
|
||||||
|
|
||||||
|
|
||||||
const handleKeyDown = (e) => {
|
const handleKeyDown = (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
handleAddRule();
|
handleAddRule();
|
||||||
@@ -49,6 +68,7 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
|||||||
const handleEditRule = (index) => {
|
const handleEditRule = (index) => {
|
||||||
setRule(rules[index]);
|
setRule(rules[index]);
|
||||||
setEditIndex(index);
|
setEditIndex(index);
|
||||||
|
ruleInputRef.current.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -58,8 +78,88 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
|||||||
}
|
}
|
||||||
setRules(rules.filter((_, i) => i !== index));
|
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 (
|
return (
|
||||||
<StyledModal
|
<StyledModal
|
||||||
@@ -86,8 +186,8 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
|||||||
<input
|
<input
|
||||||
id="name"
|
id="name"
|
||||||
type="text"
|
type="text"
|
||||||
value={boardTitle}
|
value={title}
|
||||||
onChange={(e) => setBoardTitle(e.target.value)}
|
onChange={(e) => setTitle(e.target.value)}
|
||||||
placeholder="Board Title"
|
placeholder="Board Title"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -116,8 +216,8 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
|||||||
<input
|
<input
|
||||||
id="name"
|
id="name"
|
||||||
type="text"
|
type="text"
|
||||||
value={boardTitle}
|
value={avatar}
|
||||||
onChange={(e) => setBoardTitle(e.target.value)}
|
onChange={(e) => setAvatar(e.target.value)}
|
||||||
placeholder="https://example.com/image.png"
|
placeholder="https://example.com/image.png"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -128,11 +228,11 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
|||||||
Optional, let other users help you moderate your board.
|
Optional, let other users help you moderate your board.
|
||||||
</div>
|
</div>
|
||||||
<div className='settings-input'>
|
<div className='settings-input'>
|
||||||
<input
|
<textarea
|
||||||
id="name"
|
id="name"
|
||||||
type="text"
|
type="text"
|
||||||
value={boardTitle}
|
value={moderators}
|
||||||
onChange={(e) => setBoardTitle(e.target.value)}
|
onChange={(e) => setModerators(e.target.value)}
|
||||||
placeholder='username.eth, 12D3KooW..., username2.eth'
|
placeholder='username.eth, 12D3KooW..., username2.eth'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -144,6 +244,7 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className='settings-input'>
|
<div className='settings-input'>
|
||||||
<input
|
<input
|
||||||
|
ref={ruleInputRef}
|
||||||
id="rule"
|
id="rule"
|
||||||
type="text"
|
type="text"
|
||||||
value={rule}
|
value={rule}
|
||||||
@@ -171,7 +272,7 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
|||||||
))}
|
))}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
)}
|
)}
|
||||||
<button id="create-board-btn">Create Board</button>
|
<button onClick={handleCreateBoard} id="create-board-btn">Create Board</button>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</StyledModal>
|
</StyledModal>
|
||||||
@@ -180,4 +281,4 @@ const CreateBoardModal = ({ isOpen, closeModal }) => {
|
|||||||
|
|
||||||
Modal.setAppElement('#root');
|
Modal.setAppElement('#root');
|
||||||
|
|
||||||
export default CreateBoardModal;
|
export default CreateBoardModal;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect, useRef } from "react";
|
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 { 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 { 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";
|
||||||
@@ -22,6 +22,9 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [expanded, setExpanded] = useState([]);
|
const [expanded, setExpanded] = useState([]);
|
||||||
const [accountJson, setAccountJson] = useState(null);
|
const [accountJson, setAccountJson] = useState(null);
|
||||||
|
const [copyStatus, setCopyStatus] = useState(false);
|
||||||
|
const [ensName, setEnsName] = useState('');
|
||||||
|
const [checkedENS, setCheckedENS] = useState(false);
|
||||||
|
|
||||||
const [, setNewErrorMessage] = useError();
|
const [, setNewErrorMessage] = useError();
|
||||||
const [, setNewSuccessMessage] = useSuccess();
|
const [, setNewSuccessMessage] = useSuccess();
|
||||||
@@ -37,6 +40,52 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
const dataPathRef = useRef();
|
const dataPathRef = useRef();
|
||||||
const importRef = useRef();
|
const importRef = useRef();
|
||||||
const nameRef = 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 = [
|
const defaultGatewayUrls = [
|
||||||
'https://ipfs.io',
|
'https://ipfs.io',
|
||||||
@@ -60,6 +109,7 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
'https://polygon-rpc.com',
|
'https://polygon-rpc.com',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
const isValidURL = (url) => {
|
const isValidURL = (url) => {
|
||||||
try {
|
try {
|
||||||
new URL(url);
|
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 () => {
|
const handleSavePlebbitOptions = async () => {
|
||||||
let gatewayUrls = gatewayRef.current.value.split('\n').filter(url => url.trim());
|
let gatewayUrls = gatewayRef.current.value.split('\n').filter(url => url.trim());
|
||||||
let ipfsClientsOptions = ipfsRef.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({
|
await setAccount({
|
||||||
...account,
|
...account,
|
||||||
plebbitOptions: {
|
plebbitOptions: {
|
||||||
...account.plebbitOptions,
|
...account?.plebbitOptions,
|
||||||
chainProviders,
|
chainProviders,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -211,7 +287,7 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
await setAccount({
|
await setAccount({
|
||||||
...account,
|
...account,
|
||||||
plebbitOptions: {
|
plebbitOptions: {
|
||||||
...account.plebbitOptions,
|
...account?.plebbitOptions,
|
||||||
chainProviders,
|
chainProviders,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -297,9 +373,9 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
try {
|
try {
|
||||||
await setAccount({
|
await setAccount({
|
||||||
...account,
|
...account,
|
||||||
name: name || account.name,
|
name: name || account?.name,
|
||||||
author: {
|
author: {
|
||||||
...account.author,
|
...account?.author,
|
||||||
displayName: name,
|
displayName: name,
|
||||||
}});
|
}});
|
||||||
setNewSuccessMessage("Account Name Saved");
|
setNewSuccessMessage("Account Name Saved");
|
||||||
@@ -353,6 +429,11 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
<div className="plebbit-options-buttons"
|
<div className="plebbit-options-buttons"
|
||||||
style={{ display: expanded.includes(1) ? 'block' : 'none' }}
|
style={{ display: expanded.includes(1) ? 'block' : 'none' }}
|
||||||
>
|
>
|
||||||
|
<button className="save-button"
|
||||||
|
onClick={handleExport}>Export</button>
|
||||||
|
<button className="reset-button"
|
||||||
|
onClick={handleImport}
|
||||||
|
>Import</button>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<ul className="settings-cat" style={{ display: expanded.includes(1) ? 'block' : 'none' }}>
|
<ul className="settings-cat" style={{ display: expanded.includes(1) ? 'block' : 'none' }}>
|
||||||
@@ -366,7 +447,7 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
checked={anonymousMode}
|
checked={anonymousMode}
|
||||||
onChange={() => {setAnonymousMode(!anonymousMode)}}
|
onChange={() => {setAnonymousMode(!anonymousMode)}}
|
||||||
/>
|
/>
|
||||||
Anon mode
|
Anon Mode
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
<li className="settings-tip anon-tip">
|
<li className="settings-tip anon-tip">
|
||||||
@@ -375,13 +456,6 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
<li className="settings-option disc">
|
<li className="settings-option disc">
|
||||||
Account Data
|
Account Data
|
||||||
</li>
|
</li>
|
||||||
<div className="plebbit-options-buttons">
|
|
||||||
<button className="save-button"
|
|
||||||
onClick={handleExport}>Export</button>
|
|
||||||
<button className="reset-button"
|
|
||||||
onClick={handleImport}
|
|
||||||
>Import</button>
|
|
||||||
</div>
|
|
||||||
<li className="settings-tip">
|
<li className="settings-tip">
|
||||||
To export, click "Export", then save your account data displayed below in a safe place. To import, paste your account data into the box below, then click "Import".
|
To export, click "Export", then save your account data displayed below in a safe place. To import, paste your account data into the box below, then click "Import".
|
||||||
</li>
|
</li>
|
||||||
@@ -392,7 +466,7 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
<textarea ref={importRef} />
|
<textarea ref={importRef} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<li className="settings-option disc" style={{marginTop: '15px'}}>
|
<li className="settings-option disc">
|
||||||
Account Address: u/{account?.author.shortAddress}
|
Account Address: u/{account?.author.shortAddress}
|
||||||
</li>
|
</li>
|
||||||
<li className="settings-tip">
|
<li className="settings-tip">
|
||||||
@@ -410,9 +484,49 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
{account?.author.address.endsWith(".eth") ? null : (
|
||||||
|
<button style={{marginLeft: '35px'}} className="save-button" id="save-name" onClick={handleCopyAddress}>
|
||||||
|
{copyStatus ? "Copied!" : "Copy Full Address"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li className="settings-option disc" style={{marginTop: '15px'}}>
|
<li className="settings-option disc">
|
||||||
|
Crypto Address
|
||||||
|
</li>
|
||||||
|
<li className="settings-tip">
|
||||||
|
{account?.author.address.endsWith(".eth") ? "Your account address is already an ENS name." : (
|
||||||
|
'Change your account address to an ENS name you own: in your ENS name page on ens.domains, click on "Records", "Edit Records", "Add record", add "plebbit-author-address" as record name, add your full address as value (copy it with the button above) and save.'
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
<div className="settings-input">
|
||||||
|
<input
|
||||||
|
className="settings-input"
|
||||||
|
style={{marginLeft: '20px'}}
|
||||||
|
placeholder="address.eth"
|
||||||
|
ref={ensRef}
|
||||||
|
value={ensName}
|
||||||
|
onChange={handleENSChange}
|
||||||
|
disabled={checkedENS}
|
||||||
|
/>
|
||||||
|
<button className="save-button" id="save-name" onClick={handleENSSave}>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
<button className="save-button check-button" id="save-name" onClick={handleENSCheck}>
|
||||||
|
Check
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{checkedENS && ensName === account?.signer?.address && (
|
||||||
|
<li className="settings-tip" style={{marginTop: '10px', color: 'green'}}>
|
||||||
|
{ensName} has been acquired by you correctly.
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
{checkedENS && ensName !== account?.signer?.address && (
|
||||||
|
<li className="settings-tip" style={{marginTop: '10px', color: 'red'}}>
|
||||||
|
{ensName} has not been acquired by you yet.
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
<li className="settings-option disc">
|
||||||
Account Name
|
Account Name
|
||||||
</li>
|
</li>
|
||||||
<li className="settings-tip">
|
<li className="settings-tip">
|
||||||
@@ -424,8 +538,9 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
type="text" ref={nameRef} defaultValue={account?.author.displayName}
|
type="text" ref={nameRef} defaultValue={account?.author.displayName}
|
||||||
placeholder="Anonymous"
|
placeholder="Anonymous"
|
||||||
/>
|
/>
|
||||||
<button className="save-button" id="save-name"
|
<button className="save-button" id="save-name" onClick={handleDisplayName}>
|
||||||
onClick={handleDisplayName}>Save</button>
|
Save
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -447,7 +562,7 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
onClick={handleResetPlebbitOptions}>Reset</button>
|
onClick={handleResetPlebbitOptions}>Reset</button>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<ul className="settings-cat" style={{ display: expanded.includes(2) ? 'block' : 'none' }}>
|
<ul className="settings-cat" style={{ display: expanded.includes(2) ? 'block' : 'none', marginTop: '-10px' }}>
|
||||||
<li className="settings-option disc">
|
<li className="settings-option disc">
|
||||||
IPFS Gateway URLs
|
IPFS Gateway URLs
|
||||||
</li>
|
</li>
|
||||||
@@ -509,23 +624,23 @@ const SettingsModal = ({ isOpen, closeModal }) => {
|
|||||||
<button className="save-button" onClick={handleResetChainProviders}>Reset</button>
|
<button className="save-button" onClick={handleResetChainProviders}>Reset</button>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<ul className="settings-cat" style={{ display: expanded.includes(3) ? 'block' : 'none' }}>
|
<ul className="settings-cat" style={{ display: expanded.includes(3) ? 'block' : 'none', marginTop: '-10px'}}>
|
||||||
<li className="settings-option disc">Ethereum RPC</li>
|
<li className="settings-option disc">Ethereum RPC</li>
|
||||||
<li className="settings-tip">Needed for .eth addresses</li>
|
<li className="settings-tip">Needed for .eth addresses.</li>
|
||||||
<div className="settings-input">
|
<div className="settings-input">
|
||||||
<textarea placeholder="Ethereum RPC URLs"
|
<textarea placeholder="Ethereum RPC URLs"
|
||||||
defaultValue={account?.plebbitOptions?.chainProviders?.['eth']?.urls.join("\n")}
|
defaultValue={account?.plebbitOptions?.chainProviders?.['eth']?.urls.join("\n")}
|
||||||
ref={ethereumRpcRef}
|
ref={ethereumRpcRef}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<li className="settings-option disc">Polygon RPC</li>
|
{/* <li className="settings-option disc">Polygon RPC</li>
|
||||||
<li className="settings-tip">Needed for XPLEB NFTs</li>
|
<li className="settings-tip">Needed for XPLEB NFTs.</li>
|
||||||
<div className="settings-input">
|
<div className="settings-input">
|
||||||
<textarea placeholder="Polygon RPC URLs"
|
<textarea placeholder="Polygon RPC URLs"
|
||||||
defaultValue={account?.plebbitOptions?.chainProviders?.['matic']?.urls.join("\n")}
|
defaultValue={account?.plebbitOptions?.chainProviders?.['matic']?.urls.join("\n")}
|
||||||
ref={polygonRpcRef}
|
ref={polygonRpcRef}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div> */}
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -3,14 +3,15 @@ import Modal from "react-modal";
|
|||||||
|
|
||||||
export const StyledModal = styled(Modal)`
|
export const StyledModal = styled(Modal)`
|
||||||
.modal-content {
|
.modal-content {
|
||||||
top: 25%;
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
width: 450px;
|
width: 450px;
|
||||||
max-height: 650px;
|
height: auto;
|
||||||
left: calc(50% - 50px);
|
max-height: calc(100vh - 2*50px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
margin-left: -178px;
|
padding: 5px;
|
||||||
position: absolute;
|
|
||||||
padding: 5px 5px 5px;
|
|
||||||
box-shadow: 0 0 5px rgba(0, 0, 0, .25);
|
box-shadow: 0 0 5px rgba(0, 0, 0, .25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,10 @@ export const StyledModal = styled(Modal)`
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.check-button {
|
||||||
|
margin-left: 58px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.cache-button {
|
.cache-button {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
@@ -122,9 +126,10 @@ export const StyledModal = styled(Modal)`
|
|||||||
}
|
}
|
||||||
|
|
||||||
.settings-option {
|
.settings-option {
|
||||||
margin: 5px 0 5px 0;
|
margin: 15px 0 5px 0 !important;
|
||||||
padding-left: 23px;
|
padding-left: 23px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-tip {
|
.settings-tip {
|
||||||
@@ -145,13 +150,6 @@ export const StyledModal = styled(Modal)`
|
|||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-option {
|
|
||||||
margin: 5px 0 5px 0;
|
|
||||||
padding-left: 23px;
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-input {
|
.settings-input {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-left: 23px;
|
padding-left: 23px;
|
||||||
@@ -195,7 +193,7 @@ export const StyledModal = styled(Modal)`
|
|||||||
}
|
}
|
||||||
|
|
||||||
.anon-tip {
|
.anon-tip {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.anon-tip::before {
|
.anon-tip::before {
|
||||||
|
|||||||
@@ -1905,7 +1905,7 @@ export const BoardForm = styled.div`
|
|||||||
.file-thumb {
|
.file-thumb {
|
||||||
float: left;
|
float: left;
|
||||||
margin: 3px 20px 5px 20px;
|
margin: 3px 20px 5px 20px;
|
||||||
background-color: rgba(0, 0, 0, 0.1);
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.reply-file-text {
|
.reply-file-text {
|
||||||
@@ -2070,6 +2070,19 @@ export const BoardForm = styled.div`
|
|||||||
margin: 7px 2px 0;
|
margin: 7px 2px 0;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
|
||||||
|
.button-mobile {
|
||||||
|
float: right;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 6px 10px 5px;
|
||||||
|
user-select: none;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
video {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.post-reply {
|
.post-reply {
|
||||||
margin-top: 0 !important;
|
margin-top: 0 !important;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -2849,6 +2862,10 @@ export const BoardForm = styled.div`
|
|||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-thumb {
|
||||||
|
background-color: rgba(255, 255, 255, 0.01) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.op-container {
|
.op-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2017,6 +2017,16 @@ const Board = () => {
|
|||||||
replyMediaInfo?.url.slice(0, 30) + "(...)" :
|
replyMediaInfo?.url.slice(0, 30) + "(...)" :
|
||||||
replyMediaInfo?.url
|
replyMediaInfo?.url
|
||||||
}</a> ({replyMediaInfo?.type})
|
}</a> ({replyMediaInfo?.type})
|
||||||
|
{replyMediaInfo?.type === "video" || "iframe" ? (
|
||||||
|
isReplyThumbnailClicked[index] ? (
|
||||||
|
<span>
|
||||||
|
-[
|
||||||
|
<span className='reply-link'
|
||||||
|
style={{textDecoration: 'underline', cursor: 'pointer'}}
|
||||||
|
onClick={() => {handleThumbnailClick(index, 'reply')}}>Close</span>
|
||||||
|
]
|
||||||
|
</span>) : null)
|
||||||
|
: null}
|
||||||
</div>
|
</div>
|
||||||
{replyMediaInfo?.type === "webpage" ? (
|
{replyMediaInfo?.type === "webpage" ? (
|
||||||
<div key={`enlarge-reply-${index}`} className="img-container">
|
<div key={`enlarge-reply-${index}`} className="img-container">
|
||||||
@@ -2043,12 +2053,29 @@ const Board = () => {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{replyMediaInfo?.type === "video" ? (
|
{replyMediaInfo?.type === "video" ? (
|
||||||
|
<div key={`enlarge-reply-${index}`} className={`img-container ${isReplyThumbnailClicked[index] ? 'expanded-container' : ''}`}>
|
||||||
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
|
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
|
||||||
<video controls
|
{isReplyThumbnailClicked[index] ? (
|
||||||
key={`fti-${index}`}
|
<video controls
|
||||||
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
className='enlarged'
|
||||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
key={`fti-${index}`}
|
||||||
|
src={replyMediaInfo.url}
|
||||||
|
alt={replyMediaInfo.type}
|
||||||
|
style={{cursor: "pointer"}}
|
||||||
|
onError={(e) => e.target.src = fallbackImgUrl}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<video
|
||||||
|
key={`fti-${index}`}
|
||||||
|
src={replyMediaInfo.url}
|
||||||
|
alt="thumbnail"
|
||||||
|
onClick={() => {handleThumbnailClick(index, 'reply')}}
|
||||||
|
style={{cursor: "pointer"}}
|
||||||
|
onError={(e) => e.target.src = fallbackImgUrl}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{replyMediaInfo?.type === "audio" ? (
|
{replyMediaInfo?.type === "audio" ? (
|
||||||
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
|
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
|
||||||
@@ -2625,13 +2652,35 @@ const Board = () => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : replyMediaInfo.type === "video" ? (
|
) : replyMediaInfo.type === "video" ? (
|
||||||
<span key={`mob-ft${reply.cid}`} className="file-thumb-mobile" style={isMobileReplyThumbnailClicked[index] ? {} : {width: displayWidthMobile, height: displayHeightMobile}}>
|
<span key={`mob-ft${thread.cid}`} className="file-thumb-mobile" style={isMobileReplyThumbnailClicked[index] ? {} : {marginBottom: '25px'}}>
|
||||||
<video key={`fti-${index}`}
|
{isMobileReplyThumbnailClicked[index] ? (
|
||||||
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
<video key={`fti-${index}`}
|
||||||
style={{ pointerEvents: "none" }}
|
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
||||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
controls
|
||||||
<div key={`mob-fi-${index}`} className="file-info-mobile">{replyMediaInfo.type}</div>
|
style={{ all: 'unset', width: '100%', height: '100%', cursor: 'pointer'}}
|
||||||
</span>
|
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||||
|
) : (
|
||||||
|
<video
|
||||||
|
key={`fti-${index}`}
|
||||||
|
src={replyMediaInfo.url}
|
||||||
|
alt="thumbnail"
|
||||||
|
onClick={() => {handleThumbnailClick(index, 'mobileReply')}}
|
||||||
|
style={{cursor: "pointer"}}
|
||||||
|
id="video-thumbnail-mobile"
|
||||||
|
onError={(e) => e.target.src = fallbackImgUrl}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{replyMediaInfo?.type === "video" || "iframe" ? (
|
||||||
|
isMobileReplyThumbnailClicked[index] ? (
|
||||||
|
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
|
||||||
|
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
|
||||||
|
onClick={() => {handleThumbnailClick(index, 'mobileReply')}}
|
||||||
|
>Close</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div key={`mob-fi-${index}`} className="file-info-mobile">video</div>
|
||||||
|
)) : <div key={`mob-fi-${index}`} className="file-info-mobile">video</div>}
|
||||||
|
</span>
|
||||||
) : replyMediaInfo.type === "audio" ? (
|
) : replyMediaInfo.type === "audio" ? (
|
||||||
<span key={`mob-ft${reply.cid}`} className="file-thumb-mobile" style={isMobileReplyThumbnailClicked[index] ? {} : {width: displayWidthMobile, height: displayHeightMobile}}>
|
<span key={`mob-ft${reply.cid}`} className="file-thumb-mobile" style={isMobileReplyThumbnailClicked[index] ? {} : {width: displayWidthMobile, height: displayHeightMobile}}>
|
||||||
<audio key={`mob-img-${index}`}
|
<audio key={`mob-img-${index}`}
|
||||||
|
|||||||
+126
-76
@@ -113,8 +113,9 @@ const Thread = () => {
|
|||||||
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
|
const [postOnHoverHeight, setPostOnHoverHeight] = useState(0);
|
||||||
const [executeAnonMode, setExecuteAnonMode] = useState(false);
|
const [executeAnonMode, setExecuteAnonMode] = useState(false);
|
||||||
const [cidTracker, setCidTracker] = useState({});
|
const [cidTracker, setCidTracker] = useState({});
|
||||||
const [isThumbnailClicked, setIsThumbnailClicked] = useState({});
|
const [isThreadThumbnailClicked, setIsThreadThumbnailClicked] = useState({});
|
||||||
const [isMobileThumbnailClicked, setIsMobileThumbnailClicked] = useState({});
|
const [isReplyThumbnailClicked, setIsReplyThumbnailClicked] = useState({});
|
||||||
|
const [isMobileThreadThumbnailClicked, setIsMobileThreadThumbnailClicked] = useState({});
|
||||||
const [isCreateBoardOpen, setIsCreateBoardOpen] = useState(false);
|
const [isCreateBoardOpen, setIsCreateBoardOpen] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
@@ -131,17 +132,28 @@ const Thread = () => {
|
|||||||
const fallbackImgUrl = "assets/filedeleted-res.gif";
|
const fallbackImgUrl = "assets/filedeleted-res.gif";
|
||||||
|
|
||||||
|
|
||||||
const handleThumbnailClick = (index, isMobile=false) => {
|
const handleThumbnailClick = (index, type) => {
|
||||||
if (isMobile) {
|
switch(type) {
|
||||||
setIsMobileThumbnailClicked(prevState => ({
|
case 'thread':
|
||||||
...prevState,
|
setIsThreadThumbnailClicked(prevState => ({
|
||||||
[index]: !prevState[index],
|
...prevState,
|
||||||
}));
|
[index]: !prevState[index],
|
||||||
} else {
|
}));
|
||||||
setIsThumbnailClicked(prevState => ({
|
break;
|
||||||
...prevState,
|
case 'reply':
|
||||||
[index]: !prevState[index],
|
setIsReplyThumbnailClicked(prevState => ({
|
||||||
}));
|
...prevState,
|
||||||
|
[index]: !prevState[index],
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
case 'mobileThread':
|
||||||
|
setIsMobileThreadThumbnailClicked(prevState => ({
|
||||||
|
...prevState,
|
||||||
|
[index]: !prevState[index],
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -844,21 +856,21 @@ const Thread = () => {
|
|||||||
commentMediaInfo?.url.slice(0, 30) + "(...)" :
|
commentMediaInfo?.url.slice(0, 30) + "(...)" :
|
||||||
commentMediaInfo?.url
|
commentMediaInfo?.url
|
||||||
}</a> ({commentMediaInfo?.type === "iframe" ? "video" : commentMediaInfo?.type})
|
}</a> ({commentMediaInfo?.type === "iframe" ? "video" : commentMediaInfo?.type})
|
||||||
{isThumbnailClicked[index] ? (
|
{isThreadThumbnailClicked[index] ? (
|
||||||
<span>
|
<span>
|
||||||
-[
|
-[
|
||||||
<span className='reply-link'
|
<span className='reply-link'
|
||||||
style={{textDecoration: 'underline', cursor: 'pointer'}}
|
style={{textDecoration: 'underline', cursor: 'pointer'}}
|
||||||
onClick={() => {handleThumbnailClick(index)}}>Close</span>
|
onClick={() => {handleThumbnailClick(index, 'thread')}}>Close</span>
|
||||||
]
|
]
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{commentMediaInfo?.type === 'iframe' && (
|
{commentMediaInfo?.type === 'iframe' && (
|
||||||
<div key={`enlarge-${index}`}
|
<div key={`enlarge-${index}`}
|
||||||
className={`img-container ${isThumbnailClicked[index] ? 'expanded-container' : ''}`}>
|
className={`img-container ${isThreadThumbnailClicked[index] ? 'expanded-container' : ''}`}>
|
||||||
<span key={`fta-${index}`} className="file-thumb">
|
<span key={`fta-${index}`} className="file-thumb">
|
||||||
{(isThumbnailClicked[index] || !commentMediaInfo.thumbnail) && commentMediaInfo.embedUrl ? (
|
{(isThreadThumbnailClicked[index] || !commentMediaInfo.thumbnail) && commentMediaInfo.embedUrl ? (
|
||||||
<iframe
|
<iframe
|
||||||
className='enlarged'
|
className='enlarged'
|
||||||
key={`fti-${index}`}
|
key={`fti-${index}`}
|
||||||
@@ -874,7 +886,7 @@ const Thread = () => {
|
|||||||
key={`fti-${index}`}
|
key={`fti-${index}`}
|
||||||
src={commentMediaInfo.thumbnail}
|
src={commentMediaInfo.thumbnail}
|
||||||
alt="thumbnail"
|
alt="thumbnail"
|
||||||
onClick={() => {handleThumbnailClick(index)}}
|
onClick={() => {handleThumbnailClick(index, 'thread')}}
|
||||||
style={{cursor: "pointer"}}
|
style={{cursor: "pointer"}}
|
||||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||||
)}
|
)}
|
||||||
@@ -887,7 +899,7 @@ const Thread = () => {
|
|||||||
{comment.thumbnailUrl ? (
|
{comment.thumbnailUrl ? (
|
||||||
<img key={`fti-${index}`}
|
<img key={`fti-${index}`}
|
||||||
src={commentMediaInfo.thumbnail} alt={commentMediaInfo.type}
|
src={commentMediaInfo.thumbnail} alt={commentMediaInfo.type}
|
||||||
onClick={handleImageClick}
|
onClick={(e) => {handleImageClick(e); handleThumbnailClick(index, 'thread')}}
|
||||||
style={{cursor: "pointer"}}
|
style={{cursor: "pointer"}}
|
||||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||||
) : null}
|
) : null}
|
||||||
@@ -899,16 +911,16 @@ const Thread = () => {
|
|||||||
<span key={`fta-${index}`} className="file-thumb">
|
<span key={`fta-${index}`} className="file-thumb">
|
||||||
<img key={`fti-${index}`}
|
<img key={`fti-${index}`}
|
||||||
src={commentMediaInfo.url} alt={commentMediaInfo.type}
|
src={commentMediaInfo.url} alt={commentMediaInfo.type}
|
||||||
onClick={handleImageClick}
|
onClick={(e) => {handleImageClick(e); handleThumbnailClick(index, 'thread')}}
|
||||||
style={{cursor: "pointer"}}
|
style={{cursor: "pointer"}}
|
||||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{commentMediaInfo?.type === "video" ? (
|
{commentMediaInfo?.type === "video" ? (
|
||||||
<div key={`enlarge-${index}`} className={`img-container ${isThumbnailClicked[index] ? 'expanded-container' : ''}`}>
|
<div key={`enlarge-${index}`} className={`img-container ${isThreadThumbnailClicked[index] ? 'expanded-container' : ''}`}>
|
||||||
<span key={`fta-${index}`} className="file-thumb">
|
<span key={`fta-${index}`} className="file-thumb">
|
||||||
{isThumbnailClicked[index] ? (
|
{isThreadThumbnailClicked[index] ? (
|
||||||
<video
|
<video
|
||||||
className='enlarged'
|
className='enlarged'
|
||||||
key={`fti-${index}`}
|
key={`fti-${index}`}
|
||||||
@@ -922,7 +934,7 @@ const Thread = () => {
|
|||||||
key={`fti-${index}`}
|
key={`fti-${index}`}
|
||||||
src={commentMediaInfo.url}
|
src={commentMediaInfo.url}
|
||||||
alt="thumbnail"
|
alt="thumbnail"
|
||||||
onClick={() => {handleThumbnailClick(index)}}
|
onClick={() => {handleThumbnailClick(index, 'thread')}}
|
||||||
style={{cursor: "pointer"}}
|
style={{cursor: "pointer"}}
|
||||||
onError={(e) => e.target.src = fallbackImgUrl}
|
onError={(e) => e.target.src = fallbackImgUrl}
|
||||||
/>
|
/>
|
||||||
@@ -1193,6 +1205,15 @@ const Thread = () => {
|
|||||||
if (storedSigners[selectedThread]) {
|
if (storedSigners[selectedThread]) {
|
||||||
signerAddress = storedSigners[selectedThread].address;
|
signerAddress = storedSigners[selectedThread].address;
|
||||||
}
|
}
|
||||||
|
let replyDisplayWidth, replyDisplayHeight;
|
||||||
|
if (reply.linkWidth && reply.linkHeight) {
|
||||||
|
const scale = Math.min(1, 125 / Math.max(reply.linkWidth, reply.linkHeight));
|
||||||
|
replyDisplayWidth = `${reply.linkWidth * scale}px`;
|
||||||
|
replyDisplayHeight = `${reply.linkHeight * scale}px`;
|
||||||
|
} else {
|
||||||
|
replyDisplayWidth = '125px';
|
||||||
|
replyDisplayHeight = '125px';
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div key={`pc-${index}`} className="reply-container">
|
<div key={`pc-${index}`} className="reply-container">
|
||||||
<div key={`sa-${index}`} className="side-arrows">{'>>'}</div>
|
<div key={`sa-${index}`} className="side-arrows">{'>>'}</div>
|
||||||
@@ -1424,57 +1445,86 @@ const Thread = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{replyMediaInfo?.url ? (
|
{replyMediaInfo?.url ? (
|
||||||
<div key={`f-${index}`} className="file"
|
<div key={`f-${index}`} className="file"
|
||||||
style={{marginBottom: "5px"}}>
|
style={{marginBottom: "5px"}}>
|
||||||
<div key={`ft-${index}`} className="reply-file-text">
|
<div key={`ft-${index}`} className="reply-file-text">
|
||||||
Link:
|
Link:
|
||||||
<a key={`fa-${index}`} href={replyMediaInfo.url}
|
<a key={`fa-${index}`} href={replyMediaInfo.url} target="_blank"
|
||||||
target="_blank" rel="noreferrer">{
|
rel="noopener noreferrer">{
|
||||||
replyMediaInfo?.url.length > 30 ?
|
replyMediaInfo?.url.length > 30 ?
|
||||||
replyMediaInfo?.url.slice(0, 30) + "(...)" :
|
replyMediaInfo?.url.slice(0, 30) + "(...)" :
|
||||||
replyMediaInfo?.url
|
replyMediaInfo?.url
|
||||||
}</a> ({replyMediaInfo?.type})
|
}</a> ({replyMediaInfo?.type})
|
||||||
</div>
|
{replyMediaInfo?.type === "video" || "iframe" ? (
|
||||||
{replyMediaInfo?.type === "webpage" ? (
|
isReplyThumbnailClicked[index] ? (
|
||||||
<div key="enlarge-reply" className="img-container">
|
<span>
|
||||||
<span key={`fta-${index}`} className="file-thumb-reply">
|
-[
|
||||||
{reply.thumbnailUrl ? (
|
<span className='reply-link'
|
||||||
<img key={`fti-${index}`}
|
style={{textDecoration: 'underline', cursor: 'pointer'}}
|
||||||
src={replyMediaInfo.thumbnail} alt="thumbnail"
|
onClick={() => {handleThumbnailClick(index, 'reply')}}>Close</span>
|
||||||
onClick={handleImageClick}
|
]
|
||||||
style={{cursor: "pointer"}}
|
</span>) : null)
|
||||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
: null}
|
||||||
) : null}
|
</div>
|
||||||
</span>
|
{replyMediaInfo?.type === "webpage" ? (
|
||||||
</div>
|
<div key={`enlarge-reply-${index}`} className="img-container">
|
||||||
) : null}
|
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
|
||||||
{replyMediaInfo?.type === "image" ? (
|
{reply.thumbnailUrl ? (
|
||||||
<div key="enlarge-reply" className="img-container">
|
|
||||||
<span key={`fta-${index}`} className="file-thumb-reply">
|
|
||||||
<img key={`fti-${index}`}
|
<img key={`fti-${index}`}
|
||||||
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
src={replyMediaInfo.thumbnail} alt={replyMediaInfo.type}
|
||||||
onClick={handleImageClick}
|
onClick={(e) => {handleImageClick(e); handleThumbnailClick(index, 'reply')}}
|
||||||
style={{cursor: "pointer"}}
|
style={{cursor: "pointer"}}
|
||||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||||
</span>
|
) : null}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{replyMediaInfo?.type === "video" ? (
|
{replyMediaInfo?.type === "image" ? (
|
||||||
<span key={`fta-${index}`} className="file-thumb-reply">
|
<div key={`enlarge-reply-${index}`} className="img-container">
|
||||||
<video controls key={`fti-${index}`}
|
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
|
||||||
|
<img key={`fti-${index}`}
|
||||||
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
||||||
|
onClick={(e) => {handleImageClick(e); handleThumbnailClick(index, 'reply')}}
|
||||||
|
style={{cursor: "pointer"}}
|
||||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
</div>
|
||||||
{replyMediaInfo?.type === "audio" ? (
|
) : null}
|
||||||
<span key={`fta-${index}`} className="file-thumb-reply">
|
{replyMediaInfo?.type === "video" ? (
|
||||||
<audio controls key={`fti-${index}`}
|
<div key={`enlarge-reply-${index}`} className={`img-container ${isReplyThumbnailClicked[index] ? 'expanded-container' : ''}`}>
|
||||||
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
|
||||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
{isReplyThumbnailClicked[index] ? (
|
||||||
</span>
|
<video controls
|
||||||
) : null}
|
className='enlarged'
|
||||||
|
key={`fti-${index}`}
|
||||||
|
src={replyMediaInfo.url}
|
||||||
|
alt={replyMediaInfo.type}
|
||||||
|
style={{cursor: "pointer"}}
|
||||||
|
onError={(e) => e.target.src = fallbackImgUrl}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<video
|
||||||
|
key={`fti-${index}`}
|
||||||
|
src={replyMediaInfo.url}
|
||||||
|
alt="thumbnail"
|
||||||
|
onClick={() => {handleThumbnailClick(index, 'reply')}}
|
||||||
|
style={{cursor: "pointer"}}
|
||||||
|
onError={(e) => e.target.src = fallbackImgUrl}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
{replyMediaInfo?.type === "audio" ? (
|
||||||
|
<span key={`fta-${index}`} className="file-thumb-reply" style={isReplyThumbnailClicked[index] ? {} : {width: replyDisplayWidth, height: replyDisplayHeight}}>
|
||||||
|
<audio controls
|
||||||
|
key={`fti-${index}`}
|
||||||
|
src={replyMediaInfo.url} alt={replyMediaInfo.type}
|
||||||
|
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<blockquote key={`pm-${index}`} className="post-message">
|
<blockquote key={`pm-${index}`} className="post-message">
|
||||||
<Link to={() => {}} key={`r-pm-${index}`} className="quotelink"
|
<Link to={() => {}} key={`r-pm-${index}`} className="quotelink"
|
||||||
ref={el => {
|
ref={el => {
|
||||||
@@ -1666,7 +1716,7 @@ const Thread = () => {
|
|||||||
{commentMediaInfo?.type === 'iframe' && (
|
{commentMediaInfo?.type === 'iframe' && (
|
||||||
<div key={`enlarge-mob-${index}`} className="img-container">
|
<div key={`enlarge-mob-${index}`} className="img-container">
|
||||||
<span key={`mob-fta-${index}`} className="file-thumb-mobile">
|
<span key={`mob-fta-${index}`} className="file-thumb-mobile">
|
||||||
{(isMobileThumbnailClicked[index] || !commentMediaInfo.thumbnail) && commentMediaInfo.embedUrl ? (
|
{(isMobileThreadThumbnailClicked[index] || !commentMediaInfo.thumbnail) && commentMediaInfo.embedUrl ? (
|
||||||
<div style={{width: "92vw"}}>
|
<div style={{width: "92vw"}}>
|
||||||
<iframe
|
<iframe
|
||||||
key={`mob-fti-${index}`}
|
key={`mob-fti-${index}`}
|
||||||
@@ -1681,15 +1731,15 @@ const Thread = () => {
|
|||||||
key={`mob-fti-${index}`}
|
key={`mob-fti-${index}`}
|
||||||
src={commentMediaInfo.thumbnail}
|
src={commentMediaInfo.thumbnail}
|
||||||
alt="thumbnail"
|
alt="thumbnail"
|
||||||
onClick={() => {handleThumbnailClick(index, true)}}
|
onClick={() => {handleThumbnailClick(index, 'mobileThread')}}
|
||||||
style={{cursor: "pointer"}}
|
style={{cursor: "pointer"}}
|
||||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||||
)}
|
)}
|
||||||
{commentMediaInfo?.type === "video" || "iframe" ? (
|
{commentMediaInfo?.type === "video" || "iframe" ? (
|
||||||
isMobileThumbnailClicked[index] ? (
|
isMobileThreadThumbnailClicked[index] ? (
|
||||||
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
|
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
|
||||||
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
|
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
|
||||||
onClick={() => {handleThumbnailClick(index, true)}}
|
onClick={() => {handleThumbnailClick(index, 'mobileThread')}}
|
||||||
>Close</span>
|
>Close</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -1705,7 +1755,7 @@ const Thread = () => {
|
|||||||
{comment.thumbnailUrl ? (
|
{comment.thumbnailUrl ? (
|
||||||
<img key={`mob-img-${index}`}
|
<img key={`mob-img-${index}`}
|
||||||
src={commentMediaInfo.thumbnail} alt="thumbnail"
|
src={commentMediaInfo.thumbnail} alt="thumbnail"
|
||||||
onClick={handleImageClick}
|
onClick={(e) => {handleImageClick(e); handleThumbnailClick(index, 'mobileThread')}}
|
||||||
style={{cursor: "pointer"}}
|
style={{cursor: "pointer"}}
|
||||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||||
) : null}
|
) : null}
|
||||||
@@ -1717,7 +1767,7 @@ const Thread = () => {
|
|||||||
<span key={`mob-ft${comment.cid}`} className="file-thumb-mobile">
|
<span key={`mob-ft${comment.cid}`} className="file-thumb-mobile">
|
||||||
<img key={`mob-img-${index}`}
|
<img key={`mob-img-${index}`}
|
||||||
src={commentMediaInfo.url} alt={commentMediaInfo.type}
|
src={commentMediaInfo.url} alt={commentMediaInfo.type}
|
||||||
onClick={handleImageClick}
|
onClick={(e) => {handleImageClick(e); handleThumbnailClick(index, 'mobileThread')}}
|
||||||
style={{cursor: "pointer"}}
|
style={{cursor: "pointer"}}
|
||||||
onError={(e) => e.target.src = fallbackImgUrl} />
|
onError={(e) => e.target.src = fallbackImgUrl} />
|
||||||
<div key={`mob-fi-${index}`} className="file-info-mobile">{commentMediaInfo?.type}</div>
|
<div key={`mob-fi-${index}`} className="file-info-mobile">{commentMediaInfo?.type}</div>
|
||||||
@@ -1725,7 +1775,7 @@ const Thread = () => {
|
|||||||
</div>
|
</div>
|
||||||
) : commentMediaInfo.type === "video" ? (
|
) : commentMediaInfo.type === "video" ? (
|
||||||
<span key={`mob-ft${comment.cid}`} className="file-thumb-mobile">
|
<span key={`mob-ft${comment.cid}`} className="file-thumb-mobile">
|
||||||
{isMobileThumbnailClicked[index] ? (
|
{isMobileThreadThumbnailClicked[index] ? (
|
||||||
<video key={`fti-${index}`}
|
<video key={`fti-${index}`}
|
||||||
src={commentMediaInfo.url} alt={commentMediaInfo.type}
|
src={commentMediaInfo.url} alt={commentMediaInfo.type}
|
||||||
controls
|
controls
|
||||||
@@ -1736,17 +1786,17 @@ const Thread = () => {
|
|||||||
key={`fti-${index}`}
|
key={`fti-${index}`}
|
||||||
src={commentMediaInfo.url}
|
src={commentMediaInfo.url}
|
||||||
alt="thumbnail"
|
alt="thumbnail"
|
||||||
onClick={() => {handleThumbnailClick(index, true)}}
|
onClick={() => {handleThumbnailClick(index, 'mobileThread')}}
|
||||||
style={{cursor: "pointer"}}
|
style={{cursor: "pointer"}}
|
||||||
id="video-thumbnail-mobile"
|
id="video-thumbnail-mobile"
|
||||||
onError={(e) => e.target.src = fallbackImgUrl}
|
onError={(e) => e.target.src = fallbackImgUrl}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{commentMediaInfo?.type === "video" || "iframe" ? (
|
{commentMediaInfo?.type === "video" || "iframe" ? (
|
||||||
isMobileThumbnailClicked[index] ? (
|
isMobileThreadThumbnailClicked[index] ? (
|
||||||
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
|
<div style={{textAlign: "center", marginTop: "15px", marginBottom: "15px"}}>
|
||||||
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
|
<span className='button-mobile' style={{float: "none", cursor: "pointer"}}
|
||||||
onClick={() => {handleThumbnailClick(index, true)}}
|
onClick={() => {handleThumbnailClick(index, 'mobileThread')}}
|
||||||
>Close</span>
|
>Close</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ const getCommentMediaInfo = (comment) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const mime = extName(url.pathname.replace('/', ''))[0]?.mime;
|
const mime = extName(url.pathname.toLowerCase().replace('/', ''))[0]?.mime;
|
||||||
|
|
||||||
if (mime?.startsWith('image')) {
|
if (mime?.startsWith('image')) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user