diff --git a/src/components/_General/TokenMediaDisplay.tsx b/src/components/_General/TokenMediaDisplay.tsx index 252c3925..a7911230 100644 --- a/src/components/_General/TokenMediaDisplay.tsx +++ b/src/components/_General/TokenMediaDisplay.tsx @@ -5,7 +5,15 @@ import { ipcRenderer } from 'electron'; import { Responsive, extractIPFSHash } from 'util/helpers'; import { V } from 'util/theming'; -import { DEFAULT_IPFS_FALLBACK_GATEWAY, IPFS_IPC_ID, IpfsAction } from 'vars/defines'; +import { + DEFAULT_IPFS_FALLBACK_GATEWAY, + IPFS_IPC_ID, + IpfsAction, + TOKEN_WHITE_LIST_LOCATION, +} from 'vars/defines'; + +import { ButtonSmall } from 'components/_General/buttons'; +import FriendlyWarning from 'components/_General/WarningFriendly'; const MediaContent = styled.div` overflow-y: auto; @@ -29,6 +37,19 @@ const TokenMediaIframe = styled.iframe` border: 0; `; +const ButtonWrapper = styled.div` + width: 100%; + display: flex; + justify-content: space-around; + margin-top: 25px; +`; + +const DisclaimerText = styled.div` + text-align: justify; + text-align-last: center; + font-size: 0.9rem; +`; + interface TokenMediaDisplayProps { url?: string; } @@ -38,31 +59,44 @@ const TokenMediaDisplay: React.FC = ({ url }) => { const [iframeLoaded, setIframeLoaded] = useState(false); const [iframeHeight, setIframeHeight] = useState('unset'); const [mediaUrl, setMediaUrl] = useState(null); + const [mediaShouldLoad, setMediaShouldLoad] = useState(false); + const [readMore, setReadMore] = useState(false); const ipfsId = useMemo(() => extractIPFSHash(url), [url]); + const tokenAddress = ipfsId || url; + const tokenInWhiteList = !!localStorage.getItem(`${TOKEN_WHITE_LIST_LOCATION}/${tokenAddress}`); useEffect(() => { setIframeHeight('unset'); setMediaUrl(null); + setIframeLoaded(false); + setMediaShouldLoad(false); + setReadMore(false); iframeRef.current?.contentWindow.postMessage({ mediaUrl: '', width: 0 }); }, [url]); - // Request IPFS file if it's an IPFS link. Set link meanwhile anyway useEffect(() => { - if (ipfsId) { - ipcRenderer.send(IPFS_IPC_ID, { - type: IpfsAction.GET, - payload: { - ipfsId, - }, - }); + if (tokenInWhiteList && !iframeLoaded) setMediaShouldLoad(true); + }, [tokenInWhiteList, iframeLoaded]); - // Set fallback in case IPFS data never streams to our node - setMediaUrl(`${DEFAULT_IPFS_FALLBACK_GATEWAY}/${ipfsId}`); - } else { - setMediaUrl(url); + // Request IPFS file if it's an IPFS link. Set link meanwhile anyway + useEffect(() => { + if (mediaShouldLoad) { + if (ipfsId) { + ipcRenderer.send(IPFS_IPC_ID, { + type: IpfsAction.GET, + payload: { + ipfsId, + }, + }); + + // Set fallback in case IPFS data never streams to our node + setMediaUrl(`${DEFAULT_IPFS_FALLBACK_GATEWAY}/${ipfsId}`); + } else { + setMediaUrl(url); + } } - }, [url, ipfsId]); + }, [url, ipfsId, mediaShouldLoad]); // Listen for IPFS files useEffect(() => { @@ -105,21 +139,99 @@ const TokenMediaDisplay: React.FC = ({ url }) => { return () => { if (observer) observer.disconnect(); }; - }, [iframeRef, iframeLoaded]); + }, [iframeRef]); - if (!mediaUrl) return null; + function addTokenToWhiteList() { + localStorage.setItem(`${TOKEN_WHITE_LIST_LOCATION}/${tokenAddress}`, `${tokenAddress}`); + setMediaShouldLoad(true); + } + + function removeTokenFromWhiteList() { + localStorage.removeItem(`${TOKEN_WHITE_LIST_LOCATION}/${tokenAddress}`); + setMediaShouldLoad(false); + setIframeLoaded(false); + } return ( - - - setIframeLoaded(true)} - /> - - + <> + {mediaShouldLoad && ( + <> + + + { + setIframeLoaded(true); + setMediaShouldLoad(true); + }} + /> + + + + removeTokenFromWhiteList()} + > + Hide Image + + + + )} + {!mediaShouldLoad && ( +
+ + +
+ + {!readMore && ( + <> + + The Tokel team does not own, endorse, host or content moderate anything that is + shown in the dApp. By it's nature, the dApp merely reads... + +
+ setReadMore(true)}> + Read More + + + )} + + {readMore && ( + <> + + The Tokel team does not own, endorse, host or content moderate anything that is + shown in the dApp. By it's nature, the dApp merely reads the media URL's + that are linked within the meta data of tokens that are created on the Tokel public + blockchain. Content moderation issues should be addressed with the token creator, + owner, or through the web host that stores the media itself. + +
+ + By accepting this disclaimer, you are accepting that you have personally verified + the source of the image and are happy for it to be displayed, knowing that there are + no content moderators and you're taking all responsibility for viewing the + media and any risks associated with that. You are accepting that anybody that + participates in creating and/or shipping this open source software holds no + liability for what is shown, and that the decision to proceed is completely + voluntary and at your own risk. + + + + setMediaShouldLoad(true)}> + View once + + addTokenToWhiteList()}> + View and never ask again + + + + )} +
+ )} + ); }; diff --git a/src/vars/defines.ts b/src/vars/defines.ts index f205aa85..6a6868b6 100644 --- a/src/vars/defines.ts +++ b/src/vars/defines.ts @@ -26,6 +26,8 @@ export const IS_DEV = process.env.NODE_ENV === 'development' || process.env.NODE export const IS_PROD = process.env.NODE_ENV === 'production'; export const SATOSHIS = 100000000; +export const TOKEN_WHITE_LIST_LOCATION = 'token_white_list'; + export enum NetworkType { TOKEL = 'TOKEL', TKLTEST = 'TKLTEST2',