mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
moved some repeated code
This commit is contained in:
+85
-3
@@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Route, Routes } from 'react-router-dom';
|
import { Route, Routes, useLocation } from 'react-router-dom';
|
||||||
import { Helmet } from 'react-helmet-async';
|
import { Helmet } from 'react-helmet-async';
|
||||||
import useBoardStore from './useBoardStore';
|
import useAppStore from './useAppStore';
|
||||||
import Home from './components/views/Home';
|
import Home from './components/views/Home';
|
||||||
import Board from './components/views/Board';
|
import Board from './components/views/Board';
|
||||||
import Thread from './components/views/Thread';
|
import Thread from './components/views/Thread';
|
||||||
@@ -48,8 +48,19 @@ const StyledContainer = styled(ToastContainer)`
|
|||||||
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const { bodyStyle, setBodyStyle } = useBoardStore(state => state);
|
const {
|
||||||
|
bodyStyle, setBodyStyle,
|
||||||
|
setDefaultSubplebbits,
|
||||||
|
setIsSettingsOpen,
|
||||||
|
setSelectedStyle,
|
||||||
|
setShowPostForm,
|
||||||
|
setShowPostFormLink,
|
||||||
|
} = useAppStore(state => state);
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
|
||||||
|
// preload images
|
||||||
const imageUrls = Array.from({ length: 14 }, (_, index) => `/assets/banners/banner-${index + 1}.jpg`);
|
const imageUrls = Array.from({ length: 14 }, (_, index) => `/assets/banners/banner-${index + 1}.jpg`);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -57,6 +68,77 @@ export default function App() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
// automatic dark mode without interefering with user's selected style
|
||||||
|
useEffect(() => {
|
||||||
|
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
const isDarkMode = darkModeMediaQuery.matches;
|
||||||
|
|
||||||
|
if (isDarkMode) {
|
||||||
|
setSelectedStyle('Tomorrow');
|
||||||
|
setBodyStyle({
|
||||||
|
background: '#1d1f21 none',
|
||||||
|
color: '#c5c8c6',
|
||||||
|
fontFamily: 'Arial, Helvetica, sans-serif'
|
||||||
|
});
|
||||||
|
localStorage.setItem('selectedStyle', 'Tomorrow');
|
||||||
|
}
|
||||||
|
|
||||||
|
const darkModeListener = (e) => {
|
||||||
|
if (e.matches) {
|
||||||
|
setSelectedStyle('Tomorrow');
|
||||||
|
setBodyStyle({
|
||||||
|
background: '#1d1f21 none',
|
||||||
|
color: '#c5c8c6',
|
||||||
|
fontFamily: 'Arial, Helvetica, sans-serif'
|
||||||
|
});
|
||||||
|
localStorage.setItem('selectedStyle', 'Tomorrow');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
darkModeMediaQuery.addEventListener('change', darkModeListener);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
darkModeMediaQuery.removeEventListener('change', darkModeListener);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// fetch default subplebbits
|
||||||
|
useEffect(() => {
|
||||||
|
let didCancel = false;
|
||||||
|
fetch(
|
||||||
|
"https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/subplebbits.json",
|
||||||
|
{ cache: "no-cache" }
|
||||||
|
)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then(res => {
|
||||||
|
if (!didCancel) {
|
||||||
|
setDefaultSubplebbits(res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
didCancel = true;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// handle nested routes
|
||||||
|
useEffect(() => {
|
||||||
|
const path = location.pathname;
|
||||||
|
if (path.endsWith('/post')) {
|
||||||
|
setShowPostFormLink(false);
|
||||||
|
setShowPostForm(true);
|
||||||
|
} else {
|
||||||
|
setShowPostFormLink(true);
|
||||||
|
setShowPostForm(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith('/settings')) {
|
||||||
|
setIsSettingsOpen(true);
|
||||||
|
} else {
|
||||||
|
setIsSettingsOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [location.pathname]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import useBoardStore from '../useBoardStore';
|
import useAppStore from '../useAppStore';
|
||||||
import Modal from 'react-modal';
|
import Modal from 'react-modal';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Draggable from 'react-draggable';
|
import Draggable from 'react-draggable';
|
||||||
@@ -246,8 +246,8 @@ const StyledModal = styled(Modal)`
|
|||||||
|
|
||||||
|
|
||||||
const CaptchaModal = ({ isOpen, closeModal, captchaImage }) => {
|
const CaptchaModal = ({ isOpen, closeModal, captchaImage }) => {
|
||||||
const { captchaResponse, setCaptchaResponse } = useBoardStore(state => state);
|
const { captchaResponse, setCaptchaResponse } = useAppStore(state => state);
|
||||||
const selectedStyle = useBoardStore(state => state.selectedStyle);
|
const selectedStyle = useAppStore(state => state.selectedStyle);
|
||||||
|
|
||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
closeModal();
|
closeModal();
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ContentLoader from 'react-content-loader';
|
import ContentLoader from 'react-content-loader';
|
||||||
import useBoardStore from '../useBoardStore';
|
import useAppStore from '../useAppStore';
|
||||||
|
|
||||||
|
|
||||||
const SingleRectLoader = () => {
|
const SingleRectLoader = () => {
|
||||||
const selectedStyle = useBoardStore((state) => state.selectedStyle);
|
const selectedStyle = useAppStore((state) => state.selectedStyle);
|
||||||
const backgroundColor = selectedStyle === 'Tomorrow' ? '#333' : '#f3f3f3';
|
const backgroundColor = selectedStyle === 'Tomorrow' ? '#333' : '#f3f3f3';
|
||||||
const foregroundColor = selectedStyle === 'Tomorrow' ? '#555' : '#ecebeb';
|
const foregroundColor = selectedStyle === 'Tomorrow' ? '#555' : '#ecebeb';
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ContentLoader from 'react-content-loader';
|
import ContentLoader from 'react-content-loader';
|
||||||
import useBoardStore from '../useBoardStore';
|
import useAppStore from '../useAppStore';
|
||||||
|
|
||||||
const SinglePostLoader = () => {
|
const SinglePostLoader = () => {
|
||||||
const selectedStyle = useBoardStore((state) => state.selectedStyle);
|
const selectedStyle = useAppStore((state) => state.selectedStyle);
|
||||||
const backgroundColor = selectedStyle === 'Tomorrow' ? '#333' : '#f3f3f3';
|
const backgroundColor = selectedStyle === 'Tomorrow' ? '#333' : '#f3f3f3';
|
||||||
const foregroundColor = selectedStyle === 'Tomorrow' ? '#555' : '#ecebeb';
|
const foregroundColor = selectedStyle === 'Tomorrow' ? '#555' : '#ecebeb';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import useBoardStore from '../useBoardStore';
|
import useAppStore from '../useAppStore';
|
||||||
import Modal from 'react-modal';
|
import Modal from 'react-modal';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Draggable from 'react-draggable';
|
import Draggable from 'react-draggable';
|
||||||
@@ -248,7 +248,7 @@ const StyledModal = styled(Modal)`
|
|||||||
|
|
||||||
|
|
||||||
const ReplyModal = ({ isOpen, closeModal }) => {
|
const ReplyModal = ({ isOpen, closeModal }) => {
|
||||||
const selectedStyle = useBoardStore(state => state.selectedStyle);
|
const selectedStyle = useAppStore(state => state.selectedStyle);
|
||||||
|
|
||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
closeModal();
|
closeModal();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React 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 styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import useBoardStore from "../useBoardStore";
|
import useAppStore from "../useAppStore";
|
||||||
|
|
||||||
const StyledModal = styled(Modal)`
|
const StyledModal = styled(Modal)`
|
||||||
.panel {
|
.panel {
|
||||||
@@ -140,7 +140,7 @@ const customOverlayStyles = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SettingsModal = ({ isOpen, closeModal }) => {
|
const SettingsModal = ({ isOpen, closeModal }) => {
|
||||||
const selectedStyle = useBoardStore(state => state.selectedStyle);
|
const selectedStyle = useAppStore(state => state.selectedStyle);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
|
|||||||
+15
-185
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect, Fragment } from 'react';
|
import React, { useState, useEffect, Fragment } from 'react';
|
||||||
import { Link, useNavigate, useParams, useLocation } from 'react-router-dom';
|
import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||||
import useBoardStore from '../../useBoardStore';
|
import useAppStore from '../../useAppStore';
|
||||||
import { Container, NavBar, Header, Break, PostFormLink, PostFormTable, PostForm, TopBar, BoardForm } from './styles/Board.styled';
|
import { Container, NavBar, Header, Break, PostFormLink, PostFormTable, PostForm, TopBar, BoardForm } from './styles/Board.styled';
|
||||||
import CaptchaModal from '../CaptchaModal';
|
import CaptchaModal from '../CaptchaModal';
|
||||||
import ImageBanner from '../ImageBanner';
|
import ImageBanner from '../ImageBanner';
|
||||||
@@ -10,39 +10,35 @@ import SettingsModal from '../SettingsModal';
|
|||||||
import { useFeed, useAccountsActions } from '@plebbit/plebbit-react-hooks';
|
import { useFeed, useAccountsActions } from '@plebbit/plebbit-react-hooks';
|
||||||
import InfiniteScroll from 'react-infinite-scroller';
|
import InfiniteScroll from 'react-infinite-scroller';
|
||||||
import { Tooltip } from 'react-tooltip';
|
import { Tooltip } from 'react-tooltip';
|
||||||
|
import handleStyleChange from '../../utils/handleStyleChange';
|
||||||
import onError from '../../utils/onError';
|
import onError from '../../utils/onError';
|
||||||
import onSuccess from '../../utils/onSuccess';
|
import onSuccess from '../../utils/onSuccess';
|
||||||
import getDate from '../../utils/getDate';
|
import getDate from '../../utils/getDate';
|
||||||
import renderComments from '../../utils/renderComments';
|
import renderComments from '../../utils/renderComments';
|
||||||
|
import useClickForm from '../../hooks/useClickForm';
|
||||||
|
|
||||||
|
|
||||||
const Board = ({ setBodyStyle }) => {
|
const Board = () => {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
selectedTitle,
|
captchaResponse, setCaptchaResponse,
|
||||||
setSelectedTitle,
|
defaultSubplebbits,
|
||||||
selectedAddress,
|
isSettingsOpen, setIsSettingsOpen,
|
||||||
setSelectedAddress,
|
selectedAddress, setSelectedAddress,
|
||||||
setSelectedThread,
|
|
||||||
selectedStyle,
|
selectedStyle,
|
||||||
setSelectedStyle,
|
setSelectedThread,
|
||||||
captchaResponse,
|
selectedTitle, setSelectedTitle,
|
||||||
setCaptchaResponse
|
showPostForm,
|
||||||
} = useBoardStore(state => state);
|
showPostFormLink
|
||||||
|
} = useAppStore(state => state);
|
||||||
|
|
||||||
const [defaultSubplebbits, setDefaultSubplebbits] = useState([]);
|
|
||||||
const [showPostFormLink, setShowPostFormLink] = useState(true);
|
|
||||||
const [showPostForm, setShowPostForm] = useState(false);
|
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [subject, setSubject] = useState('');
|
const [subject, setSubject] = useState('');
|
||||||
const [comment, setComment] = useState('');
|
const [comment, setComment] = useState('');
|
||||||
const { publishComment } = useAccountsActions();
|
const { publishComment } = useAccountsActions();
|
||||||
const [isCaptchaOpen, setIsCaptchaOpen] = useState(false);
|
const [isCaptchaOpen, setIsCaptchaOpen] = useState(false);
|
||||||
const [isReplyOpen, setIsReplyOpen] = useState(false);
|
const [isReplyOpen, setIsReplyOpen] = useState(false);
|
||||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
|
||||||
const [captchaImage, setCaptchaImage] = useState('');
|
const [captchaImage, setCaptchaImage] = useState('');
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
|
||||||
const [prevScrollPos, setPrevScrollPos] = useState(0);
|
const [prevScrollPos, setPrevScrollPos] = useState(0);
|
||||||
const [visible, setVisible] = useState(true);
|
const [visible, setVisible] = useState(true);
|
||||||
const [endIndex, setEndIndex] = useState(2);
|
const [endIndex, setEndIndex] = useState(2);
|
||||||
@@ -50,6 +46,7 @@ const Board = ({ setBodyStyle }) => {
|
|||||||
const [selectedFeed, setSelectedFeed] = useState(feed);
|
const [selectedFeed, setSelectedFeed] = useState(feed);
|
||||||
const renderedFeed = selectedFeed.slice(0, endIndex);
|
const renderedFeed = selectedFeed.slice(0, endIndex);
|
||||||
const { subplebbitAddress } = useParams();
|
const { subplebbitAddress } = useParams();
|
||||||
|
const handleClickForm = useClickForm();
|
||||||
|
|
||||||
|
|
||||||
// temporary title from JSON, gets subplebbitAddress from URL
|
// temporary title from JSON, gets subplebbitAddress from URL
|
||||||
@@ -66,24 +63,6 @@ const Board = ({ setBodyStyle }) => {
|
|||||||
setSelectedFeed(feed);
|
setSelectedFeed(feed);
|
||||||
}, [feed]);
|
}, [feed]);
|
||||||
|
|
||||||
// fetches default subplebbits from JSON
|
|
||||||
useEffect(() => {
|
|
||||||
let didCancel = false;
|
|
||||||
fetch(
|
|
||||||
"https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/subplebbits.json",
|
|
||||||
{ cache: "no-cache" }
|
|
||||||
)
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then(res => {
|
|
||||||
if (!didCancel) {
|
|
||||||
setDefaultSubplebbits(res);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return () => {
|
|
||||||
didCancel = true;
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// mobile navbar scroll effect
|
// mobile navbar scroll effect
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
@@ -103,60 +82,6 @@ const Board = ({ setBodyStyle }) => {
|
|||||||
setEndIndex(2);
|
setEndIndex(2);
|
||||||
}, [selectedAddress]);
|
}, [selectedAddress]);
|
||||||
|
|
||||||
// nested routes handling
|
|
||||||
useEffect(() => {
|
|
||||||
const path = location.pathname;
|
|
||||||
|
|
||||||
if (path.endsWith('/post')) {
|
|
||||||
setShowPostFormLink(false);
|
|
||||||
setShowPostForm(true);
|
|
||||||
} else {
|
|
||||||
setShowPostFormLink(true);
|
|
||||||
setShowPostForm(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.endsWith('/settings')) {
|
|
||||||
setIsSettingsOpen(true);
|
|
||||||
} else {
|
|
||||||
setIsSettingsOpen(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [location.pathname]);
|
|
||||||
|
|
||||||
// automatic dark mode without interefering with user's selected style
|
|
||||||
useEffect(() => {
|
|
||||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
||||||
const isDarkMode = darkModeMediaQuery.matches;
|
|
||||||
|
|
||||||
if (isDarkMode) {
|
|
||||||
setSelectedStyle('Tomorrow');
|
|
||||||
setBodyStyle({
|
|
||||||
background: '#1d1f21 none',
|
|
||||||
color: '#c5c8c6',
|
|
||||||
fontFamily: 'Arial, Helvetica, sans-serif'
|
|
||||||
});
|
|
||||||
localStorage.setItem('selectedStyle', 'Tomorrow');
|
|
||||||
}
|
|
||||||
|
|
||||||
const darkModeListener = (e) => {
|
|
||||||
if (e.matches) {
|
|
||||||
setSelectedStyle('Tomorrow');
|
|
||||||
setBodyStyle({
|
|
||||||
background: '#1d1f21 none',
|
|
||||||
color: '#c5c8c6',
|
|
||||||
fontFamily: 'Arial, Helvetica, sans-serif'
|
|
||||||
});
|
|
||||||
localStorage.setItem('selectedStyle', 'Tomorrow');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
darkModeMediaQuery.addEventListener('change', darkModeListener);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
darkModeMediaQuery.removeEventListener('change', darkModeListener);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const tryLoadMore = async () => {
|
const tryLoadMore = async () => {
|
||||||
@@ -260,13 +185,6 @@ const Board = ({ setBodyStyle }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleClickForm = () => {
|
|
||||||
setShowPostFormLink(false);
|
|
||||||
setShowPostForm(true);
|
|
||||||
navigate(`/${selectedAddress}/post`);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const handleClickThread = (thread) => {
|
const handleClickThread = (thread) => {
|
||||||
setSelectedThread(thread);
|
setSelectedThread(thread);
|
||||||
};
|
};
|
||||||
@@ -323,94 +241,6 @@ const Board = ({ setBodyStyle }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const handleStyleChange = (event) => {
|
|
||||||
switch (event.target.value) {
|
|
||||||
case "Yotsuba":
|
|
||||||
const yotsubaBodyStyle = {
|
|
||||||
background: "#ffe url(/assets/fade.png) top repeat-x",
|
|
||||||
color: "maroon",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(yotsubaBodyStyle);
|
|
||||||
setSelectedStyle("Yotsuba");
|
|
||||||
localStorage.setItem("selectedStyle", "Yotsuba");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(yotsubaBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Yotsuba-B":
|
|
||||||
const yotsubaBBodyStyle = {
|
|
||||||
background: "#eef2ff url(/assets/fade-blue.png) top center repeat-x",
|
|
||||||
color: "#000",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(yotsubaBBodyStyle);
|
|
||||||
setSelectedStyle("Yotsuba-B");
|
|
||||||
localStorage.setItem("selectedStyle", "Yotsuba-B");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(yotsubaBBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Futaba":
|
|
||||||
const futabaBodyStyle = {
|
|
||||||
background: "#ffe",
|
|
||||||
color: "maroon",
|
|
||||||
fontFamily: "times new roman, serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(futabaBodyStyle);
|
|
||||||
setSelectedStyle("Futaba");
|
|
||||||
localStorage.setItem("selectedStyle", "Futaba");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(futabaBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Burichan":
|
|
||||||
const burichanBodyStyle = {
|
|
||||||
background: "#eef2ff",
|
|
||||||
color: "#000",
|
|
||||||
fontFamily: "times new roman, serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(burichanBodyStyle);
|
|
||||||
setSelectedStyle("Burichan");
|
|
||||||
localStorage.setItem("selectedStyle", "Burichan");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(burichanBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Tomorrow":
|
|
||||||
const tomorrowBodyStyle = {
|
|
||||||
background: "#1d1f21 none",
|
|
||||||
color: "#c5c8c6",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(tomorrowBodyStyle);
|
|
||||||
setSelectedStyle("Tomorrow");
|
|
||||||
localStorage.setItem("selectedStyle", "Tomorrow");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(tomorrowBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Photon":
|
|
||||||
const photonBodyStyle = {
|
|
||||||
background: "#eee none",
|
|
||||||
color: "#333",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(photonBodyStyle);
|
|
||||||
setSelectedStyle("Photon");
|
|
||||||
localStorage.setItem("selectedStyle", "Photon");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(photonBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
const defaultBodyStyle = {
|
|
||||||
background: "#ffe url(/assets/fade.png) top repeat-x",
|
|
||||||
color: "maroon",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(defaultBodyStyle);
|
|
||||||
setSelectedStyle("Yotsuba");
|
|
||||||
localStorage.setItem("selectedStyle", "Yotsuba");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(defaultBodyStyle));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Link, useNavigate, useParams, useLocation } from 'react-router-dom';
|
import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||||
import useBoardStore from '../../useBoardStore';
|
import useAppStore from '../../useAppStore';
|
||||||
import { Container, NavBar, Header, Break, PostForm, PostFormLink, PostFormTable } from './styles/Board.styled';
|
import { Container, NavBar, Header, Break, PostForm, PostFormLink, PostFormTable } from './styles/Board.styled';
|
||||||
import { TopBar } from './styles/Thread.styled';
|
import { TopBar } from './styles/Thread.styled';
|
||||||
import { Threads } from './styles/Catalog.styled';
|
import { Threads } from './styles/Catalog.styled';
|
||||||
@@ -9,41 +9,38 @@ import ImageBanner from '../ImageBanner';
|
|||||||
import CaptchaModal from '../CaptchaModal';
|
import CaptchaModal from '../CaptchaModal';
|
||||||
import CatalogLoader from '../CatalogLoader';
|
import CatalogLoader from '../CatalogLoader';
|
||||||
import SettingsModal from '../SettingsModal';
|
import SettingsModal from '../SettingsModal';
|
||||||
|
import handleStyleChange from '../../utils/handleStyleChange';
|
||||||
import onError from '../../utils/onError';
|
import onError from '../../utils/onError';
|
||||||
import onSuccess from '../../utils/onSuccess';
|
import onSuccess from '../../utils/onSuccess';
|
||||||
import InfiniteScroll from 'react-infinite-scroller';
|
import InfiniteScroll from 'react-infinite-scroller';
|
||||||
|
import useClickForm from '../../hooks/useClickForm';
|
||||||
|
|
||||||
|
|
||||||
const Catalog = ({ setBodyStyle }) => {
|
const Catalog = () => {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
selectedTitle,
|
captchaResponse, setCaptchaResponse,
|
||||||
setSelectedTitle,
|
defaultSubplebbits,
|
||||||
selectedAddress,
|
isSettingsOpen, setIsSettingsOpen,
|
||||||
setSelectedAddress,
|
selectedAddress, setSelectedAddress,
|
||||||
setSelectedThread,
|
|
||||||
selectedStyle,
|
selectedStyle,
|
||||||
setSelectedStyle,
|
setSelectedThread,
|
||||||
captchaResponse,
|
selectedTitle, setSelectedTitle,
|
||||||
setCaptchaResponse
|
showPostForm,
|
||||||
} = useBoardStore(state => state);
|
showPostFormLink
|
||||||
|
} = useAppStore(state => state);
|
||||||
|
|
||||||
const [defaultSubplebbits, setDefaultSubplebbits] = useState([]);
|
|
||||||
const [showPostFormLink, setShowPostFormLink] = useState(true);
|
|
||||||
const [showPostForm, setShowPostForm] = useState(false);
|
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [subject, setSubject] = useState('');
|
const [subject, setSubject] = useState('');
|
||||||
const [comment, setComment] = useState('');
|
const [comment, setComment] = useState('');
|
||||||
const { publishComment } = useAccountsActions();
|
const { publishComment } = useAccountsActions();
|
||||||
const [isCaptchaOpen, setIsCaptchaOpen] = useState(false);
|
const [isCaptchaOpen, setIsCaptchaOpen] = useState(false);
|
||||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
|
||||||
const [captchaImage, setCaptchaImage] = useState('');
|
const [captchaImage, setCaptchaImage] = useState('');
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
|
||||||
const [prevScrollPos, setPrevScrollPos] = useState(0);
|
const [prevScrollPos, setPrevScrollPos] = useState(0);
|
||||||
const [visible, setVisible] = useState(true);
|
const [visible, setVisible] = useState(true);
|
||||||
const { feed, hasMore, loadMore } = useFeed([`${selectedAddress}`], 'new');
|
const { feed, hasMore, loadMore } = useFeed([`${selectedAddress}`], 'new');
|
||||||
const { subplebbitAddress } = useParams();
|
const { subplebbitAddress } = useParams();
|
||||||
|
const handleClickForm = useClickForm();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -56,24 +53,6 @@ const Catalog = ({ setBodyStyle }) => {
|
|||||||
}, [subplebbitAddress, setSelectedAddress, setSelectedTitle, defaultSubplebbits]);
|
}, [subplebbitAddress, setSelectedAddress, setSelectedTitle, defaultSubplebbits]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let didCancel = false;
|
|
||||||
fetch(
|
|
||||||
"https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/subplebbits.json",
|
|
||||||
{ cache: "no-cache" }
|
|
||||||
)
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then(res => {
|
|
||||||
if (!didCancel) {
|
|
||||||
setDefaultSubplebbits(res);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return () => {
|
|
||||||
didCancel = true;
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
const currentScrollPos = window.pageYOffset;
|
const currentScrollPos = window.pageYOffset;
|
||||||
@@ -87,59 +66,6 @@ const Catalog = ({ setBodyStyle }) => {
|
|||||||
return () => window.removeEventListener('scroll', handleScroll);
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
}, [prevScrollPos, visible]);
|
}, [prevScrollPos, visible]);
|
||||||
|
|
||||||
// nested routes handling
|
|
||||||
useEffect(() => {
|
|
||||||
const path = location.pathname;
|
|
||||||
if (path.endsWith('/post')) {
|
|
||||||
setShowPostFormLink(false);
|
|
||||||
setShowPostForm(true);
|
|
||||||
} else {
|
|
||||||
setShowPostFormLink(true);
|
|
||||||
setShowPostForm(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.endsWith('/settings')) {
|
|
||||||
setIsSettingsOpen(true);
|
|
||||||
} else {
|
|
||||||
setIsSettingsOpen(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [location.pathname]);
|
|
||||||
|
|
||||||
// automatic dark mode without interefering with user's selected style
|
|
||||||
useEffect(() => {
|
|
||||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
||||||
const isDarkMode = darkModeMediaQuery.matches;
|
|
||||||
|
|
||||||
if (isDarkMode) {
|
|
||||||
setSelectedStyle('Tomorrow');
|
|
||||||
setBodyStyle({
|
|
||||||
background: '#1d1f21 none',
|
|
||||||
color: '#c5c8c6',
|
|
||||||
fontFamily: 'Arial, Helvetica, sans-serif'
|
|
||||||
});
|
|
||||||
localStorage.setItem('selectedStyle', 'Tomorrow');
|
|
||||||
}
|
|
||||||
|
|
||||||
const darkModeListener = (e) => {
|
|
||||||
if (e.matches) {
|
|
||||||
setSelectedStyle('Tomorrow');
|
|
||||||
setBodyStyle({
|
|
||||||
background: '#1d1f21 none',
|
|
||||||
color: '#c5c8c6',
|
|
||||||
fontFamily: 'Arial, Helvetica, sans-serif'
|
|
||||||
});
|
|
||||||
localStorage.setItem('selectedStyle', 'Tomorrow');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
darkModeMediaQuery.addEventListener('change', darkModeListener);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
darkModeMediaQuery.removeEventListener('change', darkModeListener);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const tryLoadMore = async () => {
|
const tryLoadMore = async () => {
|
||||||
@@ -225,14 +151,6 @@ const Catalog = ({ setBodyStyle }) => {
|
|||||||
alert("- Embedding media is optional, posts can be text-only. \n- A CAPTCHA challenge will appear after posting. \n- The CAPTCHA is case-sensitive.");
|
alert("- Embedding media is optional, posts can be text-only. \n- A CAPTCHA challenge will appear after posting. \n- The CAPTCHA is case-sensitive.");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleClickForm = () => {
|
|
||||||
setShowPostFormLink(false);
|
|
||||||
setShowPostForm(true);
|
|
||||||
navigate(`/${selectedAddress}/catalog/post`);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const handleClickThread = (thread) => {
|
const handleClickThread = (thread) => {
|
||||||
setSelectedThread(thread);
|
setSelectedThread(thread);
|
||||||
}
|
}
|
||||||
@@ -271,94 +189,6 @@ const Catalog = ({ setBodyStyle }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const handleStyleChange = (event) => {
|
|
||||||
switch (event.target.value) {
|
|
||||||
case "Yotsuba":
|
|
||||||
const yotsubaBodyStyle = {
|
|
||||||
background: "#ffe url(/assets/fade.png) top repeat-x",
|
|
||||||
color: "maroon",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(yotsubaBodyStyle);
|
|
||||||
setSelectedStyle("Yotsuba");
|
|
||||||
localStorage.setItem("selectedStyle", "Yotsuba");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(yotsubaBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Yotsuba-B":
|
|
||||||
const yotsubaBBodyStyle = {
|
|
||||||
background: "#eef2ff url(/assets/fade-blue.png) top center repeat-x",
|
|
||||||
color: "#000",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(yotsubaBBodyStyle);
|
|
||||||
setSelectedStyle("Yotsuba-B");
|
|
||||||
localStorage.setItem("selectedStyle", "Yotsuba-B");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(yotsubaBBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Futaba":
|
|
||||||
const futabaBodyStyle = {
|
|
||||||
background: "#ffe",
|
|
||||||
color: "maroon",
|
|
||||||
fontFamily: "times new roman, serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(futabaBodyStyle);
|
|
||||||
setSelectedStyle("Futaba");
|
|
||||||
localStorage.setItem("selectedStyle", "Futaba");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(futabaBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Burichan":
|
|
||||||
const burichanBodyStyle = {
|
|
||||||
background: "#eef2ff",
|
|
||||||
color: "#000",
|
|
||||||
fontFamily: "times new roman, serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(burichanBodyStyle);
|
|
||||||
setSelectedStyle("Burichan");
|
|
||||||
localStorage.setItem("selectedStyle", "Burichan");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(burichanBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Tomorrow":
|
|
||||||
const tomorrowBodyStyle = {
|
|
||||||
background: "#1d1f21 none",
|
|
||||||
color: "#c5c8c6",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(tomorrowBodyStyle);
|
|
||||||
setSelectedStyle("Tomorrow");
|
|
||||||
localStorage.setItem("selectedStyle", "Tomorrow");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(tomorrowBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Photon":
|
|
||||||
const photonBodyStyle = {
|
|
||||||
background: "#eee none",
|
|
||||||
color: "#333",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(photonBodyStyle);
|
|
||||||
setSelectedStyle("Photon");
|
|
||||||
localStorage.setItem("selectedStyle", "Photon");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(photonBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
const defaultBodyStyle = {
|
|
||||||
background: "#ffe url(/assets/fade.png) top repeat-x",
|
|
||||||
color: "maroon",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(defaultBodyStyle);
|
|
||||||
setSelectedStyle("Yotsuba");
|
|
||||||
localStorage.setItem("selectedStyle", "Yotsuba");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(defaultBodyStyle));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import useBoardStore from '../../useBoardStore';
|
import useAppStore from '../../useAppStore';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Container, Header, Logo, Page, Search, About, AboutTitle, AboutContent, Boards, BoardsTitle, BoardsContent, Footer } from './styles/Home.styled';
|
import { Container, Header, Logo, Page, Search, About, AboutTitle, AboutContent, Boards, BoardsTitle, BoardsContent, Footer } from './styles/Home.styled';
|
||||||
|
|
||||||
|
|
||||||
const Home = ({ setBodyStyle }) => {
|
const Home = ({ setBodyStyle }) => {
|
||||||
const [defaultSubplebbits, setDefaultSubplebbits] = useState([]);
|
const [defaultSubplebbits, setDefaultSubplebbits] = useState([]);
|
||||||
const { setSelectedTitle, setSelectedAddress, setSelectedStyle } = useBoardStore(state => state);
|
const { setSelectedTitle, setSelectedAddress, setSelectedStyle } = useAppStore(state => state);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setBodyStyle({
|
setBodyStyle({
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import useBoardStore from '../../useBoardStore';
|
import useAppStore from '../../useAppStore';
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { Container, Header, Logo, Page, Boards, BoardsTitle } from './styles/Home.styled';
|
import { Container, Header, Logo, Page, Boards, BoardsTitle } from './styles/Home.styled';
|
||||||
|
|
||||||
|
|
||||||
const NotFound = ({ setBodyStyle }) => {
|
const NotFound = ({ setBodyStyle }) => {
|
||||||
|
|
||||||
const { setSelectedStyle } = useBoardStore(state => state);
|
const { setSelectedStyle } = useAppStore(state => state);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setBodyStyle({
|
setBodyStyle({
|
||||||
|
|||||||
+17
-186
@@ -1,53 +1,50 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Link, useNavigate, useParams, useLocation } from 'react-router-dom';
|
import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||||
import useBoardStore from '../../useBoardStore';
|
import useAppStore from '../../useAppStore';
|
||||||
import { Container, NavBar, Header, Break, PostForm, PostFormTable, BoardForm } from './styles/Board.styled';
|
import { Container, NavBar, Header, Break, PostForm, PostFormTable, BoardForm } from './styles/Board.styled';
|
||||||
import { ReplyFormLink, TopBar, BottomBar } from './styles/Thread.styled';
|
import { ReplyFormLink, TopBar, BottomBar } from './styles/Thread.styled';
|
||||||
import { useComment, useAccountsActions } from '@plebbit/plebbit-react-hooks';
|
import { useComment, useAccountsActions } from '@plebbit/plebbit-react-hooks';
|
||||||
|
import { Tooltip } from 'react-tooltip';
|
||||||
import CaptchaModal from '../CaptchaModal';
|
import CaptchaModal from '../CaptchaModal';
|
||||||
import ImageBanner from '../ImageBanner';
|
import ImageBanner from '../ImageBanner';
|
||||||
import PostLoader from '../PostLoader';
|
import PostLoader from '../PostLoader';
|
||||||
import ReplyModal from '../ReplyModal';
|
import ReplyModal from '../ReplyModal';
|
||||||
import SettingsModal from '../SettingsModal';
|
import SettingsModal from '../SettingsModal';
|
||||||
import { Tooltip } from 'react-tooltip';
|
import handleStyleChange from '../../utils/handleStyleChange';
|
||||||
import onError from '../../utils/onError';
|
import onError from '../../utils/onError';
|
||||||
import onSuccess from '../../utils/onSuccess';
|
import onSuccess from '../../utils/onSuccess';
|
||||||
import getDate from '../../utils/getDate';
|
import getDate from '../../utils/getDate';
|
||||||
import renderThreadComments from '../../utils/renderThreadComments';
|
import renderThreadComments from '../../utils/renderThreadComments';
|
||||||
|
import useClickForm from '../../hooks/useClickForm';
|
||||||
|
|
||||||
|
|
||||||
const Thread = ({ setBodyStyle }) => {
|
const Thread = () => {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
selectedTitle,
|
captchaResponse, setCaptchaResponse,
|
||||||
setSelectedTitle,
|
defaultSubplebbits,
|
||||||
selectedAddress,
|
isSettingsOpen, setIsSettingsOpen,
|
||||||
setSelectedAddress,
|
selectedAddress, setSelectedAddress,
|
||||||
selectedThread,
|
|
||||||
setSelectedThread,
|
|
||||||
selectedStyle,
|
selectedStyle,
|
||||||
setSelectedStyle,
|
selectedThread, setSelectedThread,
|
||||||
captchaResponse,
|
selectedTitle, setSelectedTitle,
|
||||||
setCaptchaResponse
|
showPostForm,
|
||||||
} = useBoardStore(state => state);
|
showPostFormLink
|
||||||
|
} = useAppStore(state => state);
|
||||||
|
|
||||||
|
|
||||||
const [defaultSubplebbits, setDefaultSubplebbits] = useState([]);
|
|
||||||
const [showPostFormLink, setShowPostFormLink] = useState(true);
|
|
||||||
const [showPostForm, setShowPostForm] = useState(false);
|
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [subject, setSubject] = useState('');
|
const [subject, setSubject] = useState('');
|
||||||
const [commentContent, setCommentContent] = useState('');
|
const [commentContent, setCommentContent] = useState('');
|
||||||
const { publishComment } = useAccountsActions();
|
const { publishComment } = useAccountsActions();
|
||||||
const [isCaptchaOpen, setIsCaptchaOpen] = useState(false);
|
const [isCaptchaOpen, setIsCaptchaOpen] = useState(false);
|
||||||
const [isReplyOpen, setIsReplyOpen] = useState(false);
|
const [isReplyOpen, setIsReplyOpen] = useState(false);
|
||||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
|
||||||
const [captchaImage, setCaptchaImage] = useState('');
|
const [captchaImage, setCaptchaImage] = useState('');
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
|
||||||
const [prevScrollPos, setPrevScrollPos] = useState(0);
|
const [prevScrollPos, setPrevScrollPos] = useState(0);
|
||||||
const [visible, setVisible] = useState(true);
|
const [visible, setVisible] = useState(true);
|
||||||
const comment = useComment(`${selectedThread}`);
|
const comment = useComment(`${selectedThread}`);
|
||||||
const { subplebbitAddress, threadCid } = useParams();
|
const { subplebbitAddress, threadCid } = useParams();
|
||||||
|
const handleClickForm = useClickForm();
|
||||||
|
|
||||||
|
|
||||||
// temporary title from JSON, gets subplebbitAddress and threadCid from URL
|
// temporary title from JSON, gets subplebbitAddress and threadCid from URL
|
||||||
@@ -60,24 +57,6 @@ const Thread = ({ setBodyStyle }) => {
|
|||||||
}
|
}
|
||||||
}, [subplebbitAddress, setSelectedAddress, setSelectedTitle, defaultSubplebbits]);
|
}, [subplebbitAddress, setSelectedAddress, setSelectedTitle, defaultSubplebbits]);
|
||||||
|
|
||||||
// fetches default subplebbits from JSON
|
|
||||||
useEffect(() => {
|
|
||||||
let didCancel = false;
|
|
||||||
fetch(
|
|
||||||
"https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/subplebbits.json",
|
|
||||||
{ cache: "no-cache" }
|
|
||||||
)
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then(res => {
|
|
||||||
if (!didCancel) {
|
|
||||||
setDefaultSubplebbits(res);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return () => {
|
|
||||||
didCancel = true;
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// mobile navbar scroll effect
|
// mobile navbar scroll effect
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
@@ -92,59 +71,6 @@ const Thread = ({ setBodyStyle }) => {
|
|||||||
return () => window.removeEventListener('scroll', handleScroll);
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
}, [prevScrollPos, visible]);
|
}, [prevScrollPos, visible]);
|
||||||
|
|
||||||
// nested routes handling
|
|
||||||
useEffect(() => {
|
|
||||||
const path = location.pathname;
|
|
||||||
if (path.endsWith('/post')) {
|
|
||||||
setShowPostFormLink(false);
|
|
||||||
setShowPostForm(true);
|
|
||||||
} else {
|
|
||||||
setShowPostFormLink(true);
|
|
||||||
setShowPostForm(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.endsWith('/settings')) {
|
|
||||||
setIsSettingsOpen(true);
|
|
||||||
} else {
|
|
||||||
setIsSettingsOpen(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [location.pathname]);
|
|
||||||
|
|
||||||
// automatic dark mode without interefering with user's selected style
|
|
||||||
useEffect(() => {
|
|
||||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
||||||
const isDarkMode = darkModeMediaQuery.matches;
|
|
||||||
|
|
||||||
if (isDarkMode) {
|
|
||||||
setSelectedStyle('Tomorrow');
|
|
||||||
setBodyStyle({
|
|
||||||
background: '#1d1f21 none',
|
|
||||||
color: '#c5c8c6',
|
|
||||||
fontFamily: 'Arial, Helvetica, sans-serif'
|
|
||||||
});
|
|
||||||
localStorage.setItem('selectedStyle', 'Tomorrow');
|
|
||||||
}
|
|
||||||
|
|
||||||
const darkModeListener = (e) => {
|
|
||||||
if (e.matches) {
|
|
||||||
setSelectedStyle('Tomorrow');
|
|
||||||
setBodyStyle({
|
|
||||||
background: '#1d1f21 none',
|
|
||||||
color: '#c5c8c6',
|
|
||||||
fontFamily: 'Arial, Helvetica, sans-serif'
|
|
||||||
});
|
|
||||||
localStorage.setItem('selectedStyle', 'Tomorrow');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
darkModeMediaQuery.addEventListener('change', darkModeListener);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
darkModeMediaQuery.removeEventListener('change', darkModeListener);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const onChallengeVerification = (challengeVerification) => {
|
const onChallengeVerification = (challengeVerification) => {
|
||||||
@@ -227,13 +153,6 @@ const Thread = ({ setBodyStyle }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleClickForm = () => {
|
|
||||||
setShowPostFormLink(false);
|
|
||||||
setShowPostForm(true);
|
|
||||||
navigate(`/${selectedAddress}/thread/${selectedThread}/post`);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const handleClickTop = () => {
|
const handleClickTop = () => {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
@@ -295,94 +214,6 @@ const Thread = ({ setBodyStyle }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const handleStyleChange = (event) => {
|
|
||||||
switch (event.target.value) {
|
|
||||||
case "Yotsuba":
|
|
||||||
const yotsubaBodyStyle = {
|
|
||||||
background: "#ffe url(/assets/fade.png) top repeat-x",
|
|
||||||
color: "maroon",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(yotsubaBodyStyle);
|
|
||||||
setSelectedStyle("Yotsuba");
|
|
||||||
localStorage.setItem("selectedStyle", "Yotsuba");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(yotsubaBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Yotsuba-B":
|
|
||||||
const yotsubaBBodyStyle = {
|
|
||||||
background: "#eef2ff url(/assets/fade-blue.png) top center repeat-x",
|
|
||||||
color: "#000",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(yotsubaBBodyStyle);
|
|
||||||
setSelectedStyle("Yotsuba-B");
|
|
||||||
localStorage.setItem("selectedStyle", "Yotsuba-B");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(yotsubaBBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Futaba":
|
|
||||||
const futabaBodyStyle = {
|
|
||||||
background: "#ffe",
|
|
||||||
color: "maroon",
|
|
||||||
fontFamily: "times new roman, serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(futabaBodyStyle);
|
|
||||||
setSelectedStyle("Futaba");
|
|
||||||
localStorage.setItem("selectedStyle", "Futaba");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(futabaBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Burichan":
|
|
||||||
const burichanBodyStyle = {
|
|
||||||
background: "#eef2ff",
|
|
||||||
color: "#000",
|
|
||||||
fontFamily: "times new roman, serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(burichanBodyStyle);
|
|
||||||
setSelectedStyle("Burichan");
|
|
||||||
localStorage.setItem("selectedStyle", "Burichan");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(burichanBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Tomorrow":
|
|
||||||
const tomorrowBodyStyle = {
|
|
||||||
background: "#1d1f21 none",
|
|
||||||
color: "#c5c8c6",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(tomorrowBodyStyle);
|
|
||||||
setSelectedStyle("Tomorrow");
|
|
||||||
localStorage.setItem("selectedStyle", "Tomorrow");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(tomorrowBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Photon":
|
|
||||||
const photonBodyStyle = {
|
|
||||||
background: "#eee none",
|
|
||||||
color: "#333",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(photonBodyStyle);
|
|
||||||
setSelectedStyle("Photon");
|
|
||||||
localStorage.setItem("selectedStyle", "Photon");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(photonBodyStyle));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
const defaultBodyStyle = {
|
|
||||||
background: "#ffe url(/assets/fade.png) top repeat-x",
|
|
||||||
color: "maroon",
|
|
||||||
fontFamily: "Arial, Helvetica, sans-serif"
|
|
||||||
};
|
|
||||||
setBodyStyle(defaultBodyStyle);
|
|
||||||
setSelectedStyle("Yotsuba");
|
|
||||||
localStorage.setItem("selectedStyle", "Yotsuba");
|
|
||||||
localStorage.setItem("bodyStyle", JSON.stringify(defaultBodyStyle));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { useNavigate, useLocation } from "react-router-dom";
|
||||||
|
import useAppStore from "../useAppStore";
|
||||||
|
|
||||||
|
const useClickForm = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
const { setShowPostForm, setShowPostFormLink } = useAppStore.getState();
|
||||||
|
|
||||||
|
const handleClickForm = () => {
|
||||||
|
setShowPostForm(true);
|
||||||
|
setShowPostFormLink(false);
|
||||||
|
|
||||||
|
const subplebbitAddress = location.pathname.split("/")[1];
|
||||||
|
const threadCid = location.pathname.split("/")[3];
|
||||||
|
|
||||||
|
if (location.pathname === "/") {
|
||||||
|
navigate("/post");
|
||||||
|
} else if (location.pathname.endsWith("/catalog")) {
|
||||||
|
navigate(`/${subplebbitAddress}/catalog/post`);
|
||||||
|
} else if (location.pathname.endsWith("/thread")) {
|
||||||
|
navigate(`/${subplebbitAddress}/thread/post`);
|
||||||
|
} else if (threadCid) {
|
||||||
|
navigate(`/${subplebbitAddress}/thread/${threadCid}/post`);
|
||||||
|
} else {
|
||||||
|
navigate(`/${subplebbitAddress}/post`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return handleClickForm;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useClickForm;
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
|
|
||||||
const useBoardStore = create((set) => ({
|
const useAppStore = create((set) => ({
|
||||||
|
captchaResponse: '',
|
||||||
|
setCaptchaResponse: (response) => set({ captchaResponse: response }),
|
||||||
|
|
||||||
bodyStyle: JSON.parse(localStorage.getItem('bodyStyle')) || {
|
bodyStyle: JSON.parse(localStorage.getItem('bodyStyle')) || {
|
||||||
background: '#ffe url(/assets/fade.png) top repeat-x',
|
background: '#ffe url(/assets/fade.png) top repeat-x',
|
||||||
color: 'maroon',
|
color: 'maroon',
|
||||||
@@ -11,14 +14,15 @@ const useBoardStore = create((set) => ({
|
|||||||
set(() => ({ bodyStyle }));
|
set(() => ({ bodyStyle }));
|
||||||
},
|
},
|
||||||
|
|
||||||
selectedTitle: '',
|
defaultSubplebbits: [],
|
||||||
setSelectedTitle: (title) => set({ selectedTitle: title }),
|
setDefaultSubplebbits: (subplebbits) => set({ defaultSubplebbits: subplebbits }),
|
||||||
|
|
||||||
|
isSettingsOpen: false,
|
||||||
|
setIsSettingsOpen: (isOpen) => set({ isSettingsOpen: isOpen }),
|
||||||
|
|
||||||
selectedAddress: '',
|
selectedAddress: '',
|
||||||
setSelectedAddress: (address) => set({ selectedAddress: address }),
|
setSelectedAddress: (address) => set({ selectedAddress: address }),
|
||||||
|
|
||||||
selectedThread: '',
|
|
||||||
setSelectedThread: (thread) => set({ selectedThread: thread }),
|
|
||||||
|
|
||||||
selectedStyle: localStorage.getItem('selectedStyle') || 'Yotsuba',
|
selectedStyle: localStorage.getItem('selectedStyle') || 'Yotsuba',
|
||||||
setSelectedStyle: (style) => {
|
setSelectedStyle: (style) => {
|
||||||
@@ -26,8 +30,18 @@ const useBoardStore = create((set) => ({
|
|||||||
set({ selectedStyle: style });
|
set({ selectedStyle: style });
|
||||||
},
|
},
|
||||||
|
|
||||||
captchaResponse: '',
|
selectedThread: '',
|
||||||
setCaptchaResponse: (response) => set({ captchaResponse: response }),
|
setSelectedThread: (thread) => set({ selectedThread: thread }),
|
||||||
|
|
||||||
|
selectedTitle: '',
|
||||||
|
setSelectedTitle: (title) => set({ selectedTitle: title }),
|
||||||
|
|
||||||
|
showPostForm: false,
|
||||||
|
setShowPostForm: (show) => set({ showPostForm: show }),
|
||||||
|
|
||||||
|
showPostFormLink: true,
|
||||||
|
setShowPostFormLink: (show) => set({ showPostFormLink: show }),
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default useBoardStore;
|
export default useAppStore;
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import useAppStore from "../useAppStore";
|
||||||
|
|
||||||
|
const handleStyleChange = (event) => {
|
||||||
|
const {
|
||||||
|
setBodyStyle, setSelectedStyle
|
||||||
|
} = useAppStore.getState();
|
||||||
|
|
||||||
|
switch (event.target.value) {
|
||||||
|
case "Yotsuba":
|
||||||
|
const yotsubaBodyStyle = {
|
||||||
|
background: "#ffe url(/assets/fade.png) top repeat-x",
|
||||||
|
color: "maroon",
|
||||||
|
fontFamily: "Arial, Helvetica, sans-serif"
|
||||||
|
};
|
||||||
|
setBodyStyle(yotsubaBodyStyle);
|
||||||
|
setSelectedStyle("Yotsuba");
|
||||||
|
localStorage.setItem("selectedStyle", "Yotsuba");
|
||||||
|
localStorage.setItem("bodyStyle", JSON.stringify(yotsubaBodyStyle));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Yotsuba-B":
|
||||||
|
const yotsubaBBodyStyle = {
|
||||||
|
background: "#eef2ff url(/assets/fade-blue.png) top center repeat-x",
|
||||||
|
color: "#000",
|
||||||
|
fontFamily: "Arial, Helvetica, sans-serif"
|
||||||
|
};
|
||||||
|
setBodyStyle(yotsubaBBodyStyle);
|
||||||
|
setSelectedStyle("Yotsuba-B");
|
||||||
|
localStorage.setItem("selectedStyle", "Yotsuba-B");
|
||||||
|
localStorage.setItem("bodyStyle", JSON.stringify(yotsubaBBodyStyle));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Futaba":
|
||||||
|
const futabaBodyStyle = {
|
||||||
|
background: "#ffe",
|
||||||
|
color: "maroon",
|
||||||
|
fontFamily: "times new roman, serif"
|
||||||
|
};
|
||||||
|
setBodyStyle(futabaBodyStyle);
|
||||||
|
setSelectedStyle("Futaba");
|
||||||
|
localStorage.setItem("selectedStyle", "Futaba");
|
||||||
|
localStorage.setItem("bodyStyle", JSON.stringify(futabaBodyStyle));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Burichan":
|
||||||
|
const burichanBodyStyle = {
|
||||||
|
background: "#eef2ff",
|
||||||
|
color: "#000",
|
||||||
|
fontFamily: "times new roman, serif"
|
||||||
|
};
|
||||||
|
setBodyStyle(burichanBodyStyle);
|
||||||
|
setSelectedStyle("Burichan");
|
||||||
|
localStorage.setItem("selectedStyle", "Burichan");
|
||||||
|
localStorage.setItem("bodyStyle", JSON.stringify(burichanBodyStyle));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Tomorrow":
|
||||||
|
const tomorrowBodyStyle = {
|
||||||
|
background: "#1d1f21 none",
|
||||||
|
color: "#c5c8c6",
|
||||||
|
fontFamily: "Arial, Helvetica, sans-serif"
|
||||||
|
};
|
||||||
|
setBodyStyle(tomorrowBodyStyle);
|
||||||
|
setSelectedStyle("Tomorrow");
|
||||||
|
localStorage.setItem("selectedStyle", "Tomorrow");
|
||||||
|
localStorage.setItem("bodyStyle", JSON.stringify(tomorrowBodyStyle));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Photon":
|
||||||
|
const photonBodyStyle = {
|
||||||
|
background: "#eee none",
|
||||||
|
color: "#333",
|
||||||
|
fontFamily: "Arial, Helvetica, sans-serif"
|
||||||
|
};
|
||||||
|
setBodyStyle(photonBodyStyle);
|
||||||
|
setSelectedStyle("Photon");
|
||||||
|
localStorage.setItem("selectedStyle", "Photon");
|
||||||
|
localStorage.setItem("bodyStyle", JSON.stringify(photonBodyStyle));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
const defaultBodyStyle = {
|
||||||
|
background: "#ffe url(/assets/fade.png) top repeat-x",
|
||||||
|
color: "maroon",
|
||||||
|
fontFamily: "Arial, Helvetica, sans-serif"
|
||||||
|
};
|
||||||
|
setBodyStyle(defaultBodyStyle);
|
||||||
|
setSelectedStyle("Yotsuba");
|
||||||
|
localStorage.setItem("selectedStyle", "Yotsuba");
|
||||||
|
localStorage.setItem("bodyStyle", JSON.stringify(defaultBodyStyle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default handleStyleChange;
|
||||||
Reference in New Issue
Block a user