2023-06-27 21:06:30 +02:00
import React , { useCallback , useEffect , useRef , useState } from 'react' ;
import { createPortal } from 'react-dom' ;
import { Helmet } from 'react-helmet-async' ;
import { Link , useNavigate , useParams } from 'react-router-dom' ;
import { Tooltip } from 'react-tooltip' ;
import { useSubplebbit } from '@plebbit/plebbit-react-hooks' ;
import { debounce } from 'lodash' ;
import { Container , NavBar , Header , Break , PostMenu , PostForm } from '../styled/views/Board.styled' ;
import { TopBar , BoardForm , Footer , ReplyFormLink } from '../styled/views/Thread.styled' ;
import { PostMenuCatalog } from '../styled/views/Catalog.styled' ;
import ImageBanner from '../ImageBanner' ;
2023-07-19 18:16:01 +02:00
import CreateBoardModal from '../modals/CreateBoardModal' ;
2023-06-28 15:25:28 +02:00
import AdminListModal from '../modals/AdminListModal' ;
2023-06-27 21:06:30 +02:00
import OfflineIndicator from '../OfflineIndicator' ;
import SettingsModal from '../modals/SettingsModal' ;
import getDate from '../../utils/getDate' ;
import handleStyleChange from '../../utils/handleStyleChange' ;
import useGeneralStore from '../../hooks/stores/useGeneralStore' ;
import packageJson from '../../../package.json'
const { version } = packageJson
const Rules = () => {
const {
defaultSubplebbits ,
isSettingsOpen , setIsSettingsOpen ,
selectedAddress , setSelectedAddress ,
selectedStyle ,
selectedThread , setSelectedThread ,
setSelectedTitle ,
} = useGeneralStore ( state => state );
2023-07-01 10:01:30 +02:00
// temporary hardcode
const plebtokenRules = [
"This community is strictly SFW. Any NSFW language or content will be removed." ,
< span > Only post about the plebbit token ( $PLEB ). For general cryptocurrency discussion , go to < Link className = 'quotelink' to = "/p/business-and-finance.eth" > p / business - and - finance . eth </ Link >.</ span >,
"FUD is allowed unless blatantly meaningless and spammy."
]
2023-06-27 21:06:30 +02:00
const navigate = useNavigate ();
const threadMenuRefs = useRef ({});
const postMenuRef = useRef ( null );
const postMenuCatalogRef = useRef ( null );
2023-06-28 15:25:28 +02:00
const [ isAdminListOpen , setIsAdminListOpen ] = useState ( false );
2023-06-27 21:06:30 +02:00
const [ prevScrollPos , setPrevScrollPos ] = useState ( 0 );
const [ visible , setVisible ] = useState ( true );
const [ menuPosition , setMenuPosition ] = useState ({ top : 0 , left : 0 });
const [ openMenuCid , setOpenMenuCid ] = useState ( null );
2023-07-19 18:16:01 +02:00
const [ isCreateBoardOpen , setIsCreateBoardOpen ] = useState ( false );
2023-06-27 21:06:30 +02:00
const { subplebbitAddress , threadCid } = useParams ();
const subplebbit = useSubplebbit ({ subplebbitAddress : selectedAddress });
const handleOptionClick = () => {
setOpenMenuCid ( null );
};
const handleOutsideClick = useCallback (( e ) => {
if ( openMenuCid !== null && ! postMenuRef . current . contains ( e . target ) && ! postMenuCatalogRef . current . contains ( e . target )) {
setOpenMenuCid ( null );
}
}, [ openMenuCid , postMenuRef , postMenuCatalogRef ]);
useEffect (() => {
if ( openMenuCid !== null ) {
document . addEventListener ( 'click' , handleOutsideClick );
} else {
document . removeEventListener ( 'click' , handleOutsideClick );
}
return () => {
document . removeEventListener ( 'click' , handleOutsideClick );
};
}, [ openMenuCid , handleOutsideClick ]);
useEffect (() => {
window . scrollTo ( 0 , 0 );
}, []);
// temporary title from JSON, gets subplebbitAddress and threadCid from URL
useEffect (() => {
setSelectedAddress ( subplebbitAddress );
setSelectedThread ( threadCid );
const selectedSubplebbit = defaultSubplebbits . find (( subplebbit ) => subplebbit . address === subplebbitAddress );
if ( selectedSubplebbit ) {
setSelectedTitle ( selectedSubplebbit . title );
}
}, [ subplebbitAddress , setSelectedAddress , setSelectedTitle , defaultSubplebbits , setSelectedThread , threadCid ]);
// mobile navbar scroll effect
useEffect (() => {
const debouncedHandleScroll = debounce (() => {
const currentScrollPos = window . scrollY ;
setVisible ( prevScrollPos > currentScrollPos || currentScrollPos < 10 );
setPrevScrollPos ( currentScrollPos );
}, 50 );
window . addEventListener ( 'scroll' , debouncedHandleScroll );
return () => window . removeEventListener ( 'scroll' , debouncedHandleScroll );
}, [ prevScrollPos , visible ]);
// mobile navbar board select functionality
const handleSelectChange = ( event ) => {
const selected = event . target . value ;
if ( selected === 'subscriptions' ) {
navigate ( `/p/subscriptions` );
return ;
} else if ( selected === 'all' ) {
navigate ( `/p/all` );
return ;
}
const selectedTitle = defaultSubplebbits . find (( subplebbit ) => subplebbit . address === selected ). title ;
setSelectedTitle ( selectedTitle );
setSelectedAddress ( selected );
navigate ( `/p/ ${ selected } ` );
};
return (
<>
< Helmet >
< title >
{ "Board Rules - " + ( subplebbit . title || subplebbit . address ) + " - plebchan" }
</ title >
</ Helmet >
< Container >
2023-07-19 18:16:01 +02:00
< CreateBoardModal
selectedStyle = { selectedStyle }
isOpen = { isCreateBoardOpen }
closeModal = {() => setIsCreateBoardOpen ( false )} />
2023-06-28 15:25:28 +02:00
< AdminListModal
selectedStyle = { selectedStyle }
isOpen = { isAdminListOpen }
closeModal = {() => setIsAdminListOpen ( false )}
roles = { subplebbit . roles } />
2023-06-27 21:06:30 +02:00
< SettingsModal
selectedStyle = { selectedStyle }
isOpen = { isSettingsOpen }
closeModal = {() => setIsSettingsOpen ( false )} />
< NavBar selectedStyle = { selectedStyle }>
<>
< span className = "boardList" >
[
2023-07-14 14:32:33 +02:00
< Link to = { `/p/all` } onClick = {() => window . scrollTo ( 0 , 0 )}> All </ Link >
2023-06-27 21:06:30 +02:00
/
2023-07-14 14:32:33 +02:00
< Link to = { `/p/subscriptions` } onClick = {() => window . scrollTo ( 0 , 0 )}> Subscriptions </ Link >
2023-06-27 21:06:30 +02:00
] & nbsp ;[
{ defaultSubplebbits . map (( subplebbit , index ) => (
< span className = "boardList" key = { `span- ${ subplebbit . address } ` }>
{ index === 0 ? null : "\u00a0" }
< Link to = { `/p/ ${ subplebbit . address } ` } key = { `a- ${ subplebbit . address } ` } onClick = {() => {
setSelectedTitle ( subplebbit . title );
setSelectedAddress ( subplebbit . address );
}}
>{ subplebbit . title ? subplebbit . title : subplebbit . address }</ Link >
{ index !== defaultSubplebbits . length - 1 ? " /" : null }
</ span >
))}
]
</ span >
< span className = "nav" >
[
< span id = "button-span" style = {{ cursor : 'pointer' }} onClick = {
2023-07-19 18:16:01 +02:00
() => {
window . electron && window . electron . isElectron ? (
setIsCreateBoardOpen ( true )
) : (
alert (
'You can create a board with the desktop version of plebchan, which you can download from here: https://github.com/plebbit/plebchan/releases/latest\n\nIf you are comfortable with the command line, you can also run a board (called "subplebbit") using plebbit-cli: https://github.com/plebbit/plebbit-cli\n\n'
2023-06-27 21:06:30 +02:00
)
2023-07-19 18:16:01 +02:00
)
}
2023-06-27 21:06:30 +02:00
}> Create Board </ span >
]
[
< Link to = { `/p/ ${ selectedAddress } /c/ ${ selectedThread } /settings` } onClick = {() => setIsSettingsOpen ( true )}> Settings </ Link >
]
[
< Link to = "/" > Home </ Link >
]
</ span >
< div id = "board-nav-mobile" style = {{ top : visible ? 0 : '-23px' }}>
< div className = "nav-container" >
< div className = "board-select" >
< strong > Board </ strong >
& nbsp ;
< select id = "board-select-mobile" value = { selectedAddress } onChange = { handleSelectChange }>
< option value = "all" > All </ option >
< option value = "subscriptions" > Subscriptions </ option >
{ defaultSubplebbits . map ( subplebbit => (
< option key = { `option- ${ subplebbit . address } ` } value = { subplebbit . address }
>{ subplebbit . title ? subplebbit . title : subplebbit . address }</ option >
))}
</ select >
< span id = "button-span" style = {{ cursor : 'pointer' }} onClick = {
() => alert (
2023-07-19 18:16:01 +02:00
'You can create a board with the desktop version of plebchan, which you can download from here: https://github.com/plebbit/plebchan/releases/latest\n\nIf you are comfortable with the command line, you can also run a board (called "subplebbit") using plebbit-cli: https://github.com/plebbit/plebbit-cli\n\n'
2023-06-27 21:06:30 +02:00
)
}> Create Board </ span >
</ div >
< div className = "page-jump" >
< Link to = { `/p/ ${ selectedAddress } /c/ ${ selectedThread } /settings` } onClick = {() => setIsSettingsOpen ( true )}> Settings </ Link >
& nbsp ;
< Link to = "/" onClick = {() => { handleStyleChange ({ target : { value : "Yotsuba" }}); window . scrollTo ( 0 , 0 );}}> Home </ Link >
</ div >
</ div >
</ div >
< div id = "separator-mobile" > & nbsp ;</ div >
< div id = "separator-mobile" > & nbsp ;</ div >
</>
</ NavBar >
< Header selectedStyle = { selectedStyle }>
<>
< div className = "banner" >
< ImageBanner />
</ div >
<>
< div className = "board-title" >{ subplebbit . title ?? null }</ div >
< div className = "board-address" > p / { subplebbit . address }
< OfflineIndicator
address = { subplebbit . address }
className = "offline"
tooltipPlace = "top" />
</ div >
</>
</>
</ Header >
< Break selectedStyle = { selectedStyle } />
< PostForm selectedStyle = { selectedStyle }>
< ReplyFormLink id = "post-form-link" selectedStyle = { selectedStyle } style = {{ marginBottom : '10px' }}>
< div id = "return-button-mobile" >
< span className = "btn-wrap" onClick = {()=> { window . scrollTo ( 0 , 0 )}}>
< Link to = { `/p/ ${ selectedAddress } ` }> Return </ Link >
</ span >
</ div >
< div id = "catalog-button-mobile" >
< span className = "btn-wrap" >
< Link to = { `/p/ ${ selectedAddress } /catalog` } onClick = {()=> { window . scrollTo ( 0 , 0 )}}> Catalog </ Link >
</ span >
</ div >
< div id = "bottom-button-mobile" >
< span className = "btn-wrap" >
< span style = {{ cursor : 'pointer' }} onClick = {() => window . scrollTo ( 0 , document . body . scrollHeight )} onMouseOver = {( event ) => event . target . style . cursor = 'pointer' }> Bottom </ span >
</ span >
</ div >
</ ReplyFormLink >
</ PostForm >
< TopBar selectedStyle = { selectedStyle }>
< span className = "style-changer" >
Style :
< select id = "style-selector" onChange = { handleStyleChange } value = { selectedStyle }>
< option value = "Yotsuba" > Yotsuba </ option >
< option value = "Yotsuba-B" > Yotsuba B </ option >
< option value = "Futaba" > Futaba </ option >
< option value = "Burichan" > Burichan </ option >
< option value = "Tomorrow" > Tomorrow </ option >
< option value = "Photon" > Photon </ option >
</ select >
</ span >
< span className = "return-button" id = "return-button-desktop" >
[
< Link to = { `/p/ ${ selectedAddress } ` }> Return </ Link >
]
</ span >
< span className = "return-button catalog-button" id = "catalog-button-desktop" >
[
< Link to = { `/p/ ${ selectedAddress } /catalog` }> Catalog </ Link >
]
</ span >
< span className = "return-button catalog-button" id = "bottom-button-desktop" >
[
< span id = "button" style = {{ cursor : 'pointer' }} onClick = {() => window . scrollTo ( 0 , document . body . scrollHeight )}
onMouseOver = {( event ) => event . target . style . cursor = 'pointer' }
onTouchStart = {() => window . scrollTo ( 0 , document . body . scrollHeight )}> Bottom </ span >
]
</ span >
</ TopBar >
< Tooltip id = "tooltip" className = "tooltip" />
< BoardForm selectedStyle = { selectedStyle }>
<>
< div className = "thread" >
< div className = "op-container" >
< div className = "post op op-desktop" >
< hr />
< div className = "post-info" >
< span className = "name-block" >
< span className = "title" > Rules </ span >
& nbsp ;
2023-06-28 15:25:28 +02:00
< span className = "name capcode"
style = {{ cursor : 'pointer' }}
onClick = {() => { setIsAdminListOpen ( ! isAdminListOpen )}}
> ## Board Admins </ span >
2023-06-27 21:06:30 +02:00
& nbsp ;
< span className = "date-time" >{ getDate ( subplebbit . createdAt )}</ span >
& nbsp ;
2023-06-29 09:00:21 +02:00
< img src = "assets/sticky.gif" alt = "Sticky" title = "Sticky" style = {{ marginBottom : "-1px" , imageRendering : 'pixelated' }} />
2023-06-27 21:06:30 +02:00
& nbsp ;
2023-06-29 09:00:21 +02:00
< img src = "assets/closed.gif" alt = "Closed" title = "Closed" style = {{ marginBottom : "-1px" , imageRendering : 'pixelated' }} />
2023-06-27 21:06:30 +02:00
< span > & nbsp ;
[
< Link to = {() => {}} style = {{ textDecoration : "none" }} className = "reply-link" > Reply </ Link >
]
</ span >
< PostMenu
title = "Post menu"
ref = { el => {
threadMenuRefs . current [ "rules" ] = el ;
}}
className = 'post-menu-button'
rotated = { openMenuCid === "rules" }
onClick = {( event ) => {
event . stopPropagation ();
const rect = threadMenuRefs . current [ "rules" ]. getBoundingClientRect ();
setMenuPosition ({ top : rect . top + window . scrollY , left : rect . left });
setOpenMenuCid ( prevCid => ( prevCid === "rules" ? null : "rules" ));
}}
>
▶
</ PostMenu >
{ createPortal (
< PostMenuCatalog selectedStyle = { selectedStyle }
ref = { el => { postMenuCatalogRef . current = el }}
onClick = {( event ) => event . stopPropagation ()}
style = {{ position : "absolute" ,
top : menuPosition . top + 7 ,
left : menuPosition . left }}>
< div className = { `post-menu-thread post-menu-thread- ${ "rules" } ` }
style = {{ display : openMenuCid === "rules" ? 'block' : 'none' }}
>
< ul className = "post-menu-catalog" >
< li onClick = {() => handleOptionClick ( "rules" )}> Hide thread </ li >
{ /* {isModerator ? (
<>
change rules
</>
) : null} */ }
</ ul >
</ div >
</ PostMenuCatalog >, document . body
)}
</ span >
< blockquote >
< div className = "custom-paragraph" style = {{ paddingBottom : '17px' }}>
2023-07-01 10:01:30 +02:00
{ subplebbit . address === "plebtoken.eth" ? (
plebtokenRules . map (( rule , index ) => (
< React.Fragment key = { index }>
{ index + 1 }. { rule }
< br />
</ React.Fragment >
))
) : (
subplebbit . rules ? . map (( rule , index ) => (
< React.Fragment key = { index }>
{ index + 1 }. { rule }
< br />
</ React.Fragment >
))
)}
2023-06-27 21:06:30 +02:00
</ div >
</ blockquote >
</ div >
</ div >
</ div >
</ div >
< div className = "thread-mobile" >
< hr />
< div className = "op-container" >
< div className = "post op op-mobile" >
< div className = "post-info-mobile" >
< button className = "post-menu-button-mobile"
style = {{ all : 'unset' , cursor : 'pointer' }}>...</ button >
< span className = "name-block-mobile" >
2023-06-28 15:25:28 +02:00
< span className = "name-mobile capcode"
style = {{ cursor : 'pointer' }}
onClick = {() => { setIsAdminListOpen ( ! isAdminListOpen )}}
> ## Board Admins </ span >
2023-06-27 21:06:30 +02:00
& nbsp ;
< span className = "thread-icons-mobile"
style = {{ float : "right" , marginRight : "18px" }}>
2023-06-29 09:00:21 +02:00
< img src = "assets/sticky.gif" alt = "Sticky" title = "Sticky" style = {{ marginTop : "-1px" , marginRight : "2px" , imageRendering : 'pixelated' }} />
2023-06-27 21:06:30 +02:00
& nbsp ;
2023-06-29 09:00:21 +02:00
< img src = "assets/closed.gif" alt = "Closed" title = "Closed" style = {{ marginTop : "-1px" , marginRight : "2px" , imageRendering : 'pixelated' }} />
2023-06-27 21:06:30 +02:00
</ span >
< br />
< span className = "subject-mobile"
style = {{ marginBottom : "-15px" }}> Rules </ span >
& nbsp ;
</ span >
< span className = "date-time-mobile post-number-mobile" >{ getDate ( subplebbit . createdAt )}</ span >
& nbsp ;
</ div >
< blockquote className = "post-message-mobile" >
< div className = "custom-paragraph" >
2023-07-01 10:01:30 +02:00
{ subplebbit . address === "plebtoken.eth" ? (
plebtokenRules . map (( rule , index ) => (
< React.Fragment key = { index }>
{ index + 1 }. { rule }
< br />
</ React.Fragment >
))
) : (
subplebbit . rules ? . map (( rule , index ) => (
< React.Fragment key = { index }>
{ index + 1 }. { rule }
< br />
</ React.Fragment >
))
)}
2023-06-27 21:06:30 +02:00
</ div >
</ blockquote >
</ div >
< div className = "post-link-mobile" >
< Link to = {() => {}} className = "button-mobile" > View Thread </ Link >
</ div >
</ div >
</ div >
</>
</ BoardForm >
< Footer selectedStyle = { selectedStyle }>
< Break id = "break" selectedStyle = { selectedStyle } style = {{
marginTop : "-36px" ,
width : "100%" ,
}} />
< Break selectedStyle = { selectedStyle } style = {{
width : "100%" ,
}} />
< span className = "style-changer" style = {{
float : "right" ,
marginTop : "2px" ,
}}>
Style :
< select id = "style-selector" onChange = { handleStyleChange } value = { selectedStyle }>
< option value = "Yotsuba" > Yotsuba </ option >
< option value = "Yotsuba-B" > Yotsuba B </ option >
< option value = "Futaba" > Futaba </ option >
< option value = "Burichan" > Burichan </ option >
< option value = "Tomorrow" > Tomorrow </ option >
< option value = "Photon" > Photon </ option >
</ select >
</ span >
< NavBar selectedStyle = { selectedStyle } style = {{
marginTop : "42px" ,
}}>
<>
< span className = "boardList" >
[
2023-07-14 14:32:33 +02:00
< Link to = { `/p/all` } onClick = {() => window . scrollTo ( 0 , 0 )}> All </ Link >
2023-06-27 21:06:30 +02:00
/
2023-07-14 14:32:33 +02:00
< Link to = { `/p/subscriptions` } onClick = {() => window . scrollTo ( 0 , 0 )}> Subscriptions </ Link >
2023-06-27 21:06:30 +02:00
] & nbsp ;
</ span >
{ defaultSubplebbits . map (( subplebbit , index ) => (
< span className = "boardList" key = { `span- ${ subplebbit . address } ` }>
{ index === 0 ? null : "\u00a0" }
< Link to = { `/p/ ${ subplebbit . address } ` } key = { `a- ${ subplebbit . address } ` } onClick = {() => {
setSelectedTitle ( subplebbit . title );
setSelectedAddress ( subplebbit . address );
}}
>{ subplebbit . title ? subplebbit . title : subplebbit . address }</ Link >
{ index !== defaultSubplebbits . length - 1 ? " /" : null }
</ span >
))}
< span className = "nav" >
[
2023-07-19 18:16:01 +02:00
< span id = "button-span" style = {{ cursor : 'pointer' }} onClick = {
() => {
window . electron && window . electron . isElectron ? (
setIsCreateBoardOpen ( true )
) : (
alert (
'You can create a board with the desktop version of plebchan, which you can download from here: https://github.com/plebbit/plebchan/releases/latest\n\nIf you are comfortable with the command line, you can also run a board (called "subplebbit") using plebbit-cli: https://github.com/plebbit/plebbit-cli\n\n'
)
)
}
}> Create Board </ span >
2023-06-27 21:06:30 +02:00
]
[
< Link to = { `/p/ ${ selectedAddress } /c/ ${ selectedThread } /settings` } onClick = {() => setIsSettingsOpen ( true )}> Settings </ Link >
]
[
< Link to = "/" onClick = {() => handleStyleChange ({ target : { value : "Yotsuba" }}
)}> Home </ Link >
]
</ span >
</>
</ NavBar >
< div id = "version" >
plebchan v { version }. GPL - 2.0
</ div >
< div className = "footer-links"
style = {{
textAlign : "center" ,
fontSize : "x-small" ,
fontFamily : "arial" ,
marginTop : "5px" ,
marginBottom : "15px" ,
}}>
< a style = {{ textDecoration : 'underline' }} href = "https://plebbit.com" target = "_blank" rel = "noopener noreferrer" > About </ a >
& nbsp ; • & nbsp ;
< a style = {{ textDecoration : 'underline' }} href = "https://github.com/plebbit/plebchan/releases/latest" target = "_blank" rel = "noopener noreferrer" > App </ a >
& nbsp ; • & nbsp ;
< a style = {{ textDecoration : 'underline' }} href = "https://twitter.com/plebchan_eth" target = "_blank" rel = "noopener noreferrer" > Twitter </ a >
& nbsp ; • & nbsp ;
< a style = {{ textDecoration : 'underline' }} href = "https://t.me/plebbit" target = "_blank" rel = "noopener noreferrer" > Telegram </ a >
</ div >
</ Footer >
</ Container >
</>
);
}
export default Rules ;