diff --git a/src/App.js b/src/App.js
index fdeb09c9..0606f34b 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,7 +1,7 @@
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 useBoardStore from './useBoardStore';
+import useAppStore from './useAppStore';
import Home from './components/views/Home';
import Board from './components/views/Board';
import Thread from './components/views/Thread';
@@ -48,8 +48,19 @@ const StyledContainer = styled(ToastContainer)`
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`);
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 (
diff --git a/src/components/CaptchaModal.jsx b/src/components/CaptchaModal.jsx
index a8f1b46b..99de849e 100644
--- a/src/components/CaptchaModal.jsx
+++ b/src/components/CaptchaModal.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import useBoardStore from '../useBoardStore';
+import useAppStore from '../useAppStore';
import Modal from 'react-modal';
import styled from 'styled-components';
import Draggable from 'react-draggable';
@@ -246,8 +246,8 @@ const StyledModal = styled(Modal)`
const CaptchaModal = ({ isOpen, closeModal, captchaImage }) => {
- const { captchaResponse, setCaptchaResponse } = useBoardStore(state => state);
- const selectedStyle = useBoardStore(state => state.selectedStyle);
+ const { captchaResponse, setCaptchaResponse } = useAppStore(state => state);
+ const selectedStyle = useAppStore(state => state.selectedStyle);
const handleCloseModal = () => {
closeModal();
diff --git a/src/components/CatalogLoader.jsx b/src/components/CatalogLoader.jsx
index f302866a..6a66d35c 100644
--- a/src/components/CatalogLoader.jsx
+++ b/src/components/CatalogLoader.jsx
@@ -1,10 +1,10 @@
import React from 'react';
import ContentLoader from 'react-content-loader';
-import useBoardStore from '../useBoardStore';
+import useAppStore from '../useAppStore';
const SingleRectLoader = () => {
- const selectedStyle = useBoardStore((state) => state.selectedStyle);
+ const selectedStyle = useAppStore((state) => state.selectedStyle);
const backgroundColor = selectedStyle === 'Tomorrow' ? '#333' : '#f3f3f3';
const foregroundColor = selectedStyle === 'Tomorrow' ? '#555' : '#ecebeb';
diff --git a/src/components/PostLoader.jsx b/src/components/PostLoader.jsx
index 1980b19c..fcb70d9b 100644
--- a/src/components/PostLoader.jsx
+++ b/src/components/PostLoader.jsx
@@ -1,9 +1,9 @@
import React from 'react';
import ContentLoader from 'react-content-loader';
-import useBoardStore from '../useBoardStore';
+import useAppStore from '../useAppStore';
const SinglePostLoader = () => {
- const selectedStyle = useBoardStore((state) => state.selectedStyle);
+ const selectedStyle = useAppStore((state) => state.selectedStyle);
const backgroundColor = selectedStyle === 'Tomorrow' ? '#333' : '#f3f3f3';
const foregroundColor = selectedStyle === 'Tomorrow' ? '#555' : '#ecebeb';
diff --git a/src/components/ReplyModal.jsx b/src/components/ReplyModal.jsx
index 43f6dc15..cea0bea3 100644
--- a/src/components/ReplyModal.jsx
+++ b/src/components/ReplyModal.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import useBoardStore from '../useBoardStore';
+import useAppStore from '../useAppStore';
import Modal from 'react-modal';
import styled from 'styled-components';
import Draggable from 'react-draggable';
@@ -248,7 +248,7 @@ const StyledModal = styled(Modal)`
const ReplyModal = ({ isOpen, closeModal }) => {
- const selectedStyle = useBoardStore(state => state.selectedStyle);
+ const selectedStyle = useAppStore(state => state.selectedStyle);
const handleCloseModal = () => {
closeModal();
diff --git a/src/components/SettingsModal.jsx b/src/components/SettingsModal.jsx
index 7f5f0107..4aefe5da 100644
--- a/src/components/SettingsModal.jsx
+++ b/src/components/SettingsModal.jsx
@@ -2,7 +2,7 @@ import React from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import Modal from "react-modal";
import styled from "styled-components";
-import useBoardStore from "../useBoardStore";
+import useAppStore from "../useAppStore";
const StyledModal = styled(Modal)`
.panel {
@@ -140,7 +140,7 @@ const customOverlayStyles = {
};
const SettingsModal = ({ isOpen, closeModal }) => {
- const selectedStyle = useBoardStore(state => state.selectedStyle);
+ const selectedStyle = useAppStore(state => state.selectedStyle);
const navigate = useNavigate();
const location = useLocation();
diff --git a/src/components/views/Board.jsx b/src/components/views/Board.jsx
index 1373042b..9ec60fac 100644
--- a/src/components/views/Board.jsx
+++ b/src/components/views/Board.jsx
@@ -1,6 +1,6 @@
import React, { useState, useEffect, Fragment } from 'react';
-import { Link, useNavigate, useParams, useLocation } from 'react-router-dom';
-import useBoardStore from '../../useBoardStore';
+import { Link, useNavigate, useParams } from 'react-router-dom';
+import useAppStore from '../../useAppStore';
import { Container, NavBar, Header, Break, PostFormLink, PostFormTable, PostForm, TopBar, BoardForm } from './styles/Board.styled';
import CaptchaModal from '../CaptchaModal';
import ImageBanner from '../ImageBanner';
@@ -10,39 +10,35 @@ import SettingsModal from '../SettingsModal';
import { useFeed, useAccountsActions } from '@plebbit/plebbit-react-hooks';
import InfiniteScroll from 'react-infinite-scroller';
import { Tooltip } from 'react-tooltip';
+import handleStyleChange from '../../utils/handleStyleChange';
import onError from '../../utils/onError';
import onSuccess from '../../utils/onSuccess';
import getDate from '../../utils/getDate';
import renderComments from '../../utils/renderComments';
+import useClickForm from '../../hooks/useClickForm';
-const Board = ({ setBodyStyle }) => {
-
+const Board = () => {
const {
- selectedTitle,
- setSelectedTitle,
- selectedAddress,
- setSelectedAddress,
- setSelectedThread,
+ captchaResponse, setCaptchaResponse,
+ defaultSubplebbits,
+ isSettingsOpen, setIsSettingsOpen,
+ selectedAddress, setSelectedAddress,
selectedStyle,
- setSelectedStyle,
- captchaResponse,
- setCaptchaResponse
- } = useBoardStore(state => state);
+ setSelectedThread,
+ selectedTitle, setSelectedTitle,
+ showPostForm,
+ showPostFormLink
+ } = useAppStore(state => state);
- const [defaultSubplebbits, setDefaultSubplebbits] = useState([]);
- const [showPostFormLink, setShowPostFormLink] = useState(true);
- const [showPostForm, setShowPostForm] = useState(false);
const [name, setName] = useState('');
const [subject, setSubject] = useState('');
const [comment, setComment] = useState('');
const { publishComment } = useAccountsActions();
const [isCaptchaOpen, setIsCaptchaOpen] = useState(false);
const [isReplyOpen, setIsReplyOpen] = useState(false);
- const [isSettingsOpen, setIsSettingsOpen] = useState(false);
const [captchaImage, setCaptchaImage] = useState('');
const navigate = useNavigate();
- const location = useLocation();
const [prevScrollPos, setPrevScrollPos] = useState(0);
const [visible, setVisible] = useState(true);
const [endIndex, setEndIndex] = useState(2);
@@ -50,6 +46,7 @@ const Board = ({ setBodyStyle }) => {
const [selectedFeed, setSelectedFeed] = useState(feed);
const renderedFeed = selectedFeed.slice(0, endIndex);
const { subplebbitAddress } = useParams();
+ const handleClickForm = useClickForm();
// temporary title from JSON, gets subplebbitAddress from URL
@@ -66,24 +63,6 @@ const Board = ({ setBodyStyle }) => {
setSelectedFeed(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
useEffect(() => {
const handleScroll = () => {
@@ -103,60 +82,6 @@ const Board = ({ setBodyStyle }) => {
setEndIndex(2);
}, [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 () => {
@@ -260,13 +185,6 @@ const Board = ({ setBodyStyle }) => {
};
- const handleClickForm = () => {
- setShowPostFormLink(false);
- setShowPostForm(true);
- navigate(`/${selectedAddress}/post`);
- };
-
-
const handleClickThread = (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 (
diff --git a/src/components/views/Catalog.jsx b/src/components/views/Catalog.jsx
index 8c602ab6..d8fbe4f5 100644
--- a/src/components/views/Catalog.jsx
+++ b/src/components/views/Catalog.jsx
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
-import { Link, useNavigate, useParams, useLocation } from 'react-router-dom';
-import useBoardStore from '../../useBoardStore';
+import { Link, useNavigate, useParams } from 'react-router-dom';
+import useAppStore from '../../useAppStore';
import { Container, NavBar, Header, Break, PostForm, PostFormLink, PostFormTable } from './styles/Board.styled';
import { TopBar } from './styles/Thread.styled';
import { Threads } from './styles/Catalog.styled';
@@ -9,41 +9,38 @@ import ImageBanner from '../ImageBanner';
import CaptchaModal from '../CaptchaModal';
import CatalogLoader from '../CatalogLoader';
import SettingsModal from '../SettingsModal';
+import handleStyleChange from '../../utils/handleStyleChange';
import onError from '../../utils/onError';
import onSuccess from '../../utils/onSuccess';
import InfiniteScroll from 'react-infinite-scroller';
+import useClickForm from '../../hooks/useClickForm';
-const Catalog = ({ setBodyStyle }) => {
-
+const Catalog = () => {
const {
- selectedTitle,
- setSelectedTitle,
- selectedAddress,
- setSelectedAddress,
- setSelectedThread,
+ captchaResponse, setCaptchaResponse,
+ defaultSubplebbits,
+ isSettingsOpen, setIsSettingsOpen,
+ selectedAddress, setSelectedAddress,
selectedStyle,
- setSelectedStyle,
- captchaResponse,
- setCaptchaResponse
- } = useBoardStore(state => state);
+ setSelectedThread,
+ selectedTitle, setSelectedTitle,
+ showPostForm,
+ showPostFormLink
+ } = useAppStore(state => state);
- const [defaultSubplebbits, setDefaultSubplebbits] = useState([]);
- const [showPostFormLink, setShowPostFormLink] = useState(true);
- const [showPostForm, setShowPostForm] = useState(false);
const [name, setName] = useState('');
const [subject, setSubject] = useState('');
const [comment, setComment] = useState('');
const { publishComment } = useAccountsActions();
const [isCaptchaOpen, setIsCaptchaOpen] = useState(false);
- const [isSettingsOpen, setIsSettingsOpen] = useState(false);
const [captchaImage, setCaptchaImage] = useState('');
const navigate = useNavigate();
- const location = useLocation();
const [prevScrollPos, setPrevScrollPos] = useState(0);
const [visible, setVisible] = useState(true);
const { feed, hasMore, loadMore } = useFeed([`${selectedAddress}`], 'new');
const { subplebbitAddress } = useParams();
+ const handleClickForm = useClickForm();
@@ -56,24 +53,6 @@ const Catalog = ({ setBodyStyle }) => {
}, [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(() => {
const handleScroll = () => {
const currentScrollPos = window.pageYOffset;
@@ -87,59 +66,6 @@ const Catalog = ({ setBodyStyle }) => {
return () => window.removeEventListener('scroll', handleScroll);
}, [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 () => {
@@ -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.");
};
-
- const handleClickForm = () => {
- setShowPostFormLink(false);
- setShowPostForm(true);
- navigate(`/${selectedAddress}/catalog/post`);
- };
-
-
const handleClickThread = (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 (
diff --git a/src/components/views/Home.jsx b/src/components/views/Home.jsx
index 91dbbd80..5b77e538 100644
--- a/src/components/views/Home.jsx
+++ b/src/components/views/Home.jsx
@@ -1,12 +1,12 @@
import React, { useState, useEffect } from 'react';
-import useBoardStore from '../../useBoardStore';
+import useAppStore from '../../useAppStore';
import { Link } from 'react-router-dom';
import { Container, Header, Logo, Page, Search, About, AboutTitle, AboutContent, Boards, BoardsTitle, BoardsContent, Footer } from './styles/Home.styled';
const Home = ({ setBodyStyle }) => {
const [defaultSubplebbits, setDefaultSubplebbits] = useState([]);
- const { setSelectedTitle, setSelectedAddress, setSelectedStyle } = useBoardStore(state => state);
+ const { setSelectedTitle, setSelectedAddress, setSelectedStyle } = useAppStore(state => state);
useEffect(() => {
setBodyStyle({
diff --git a/src/components/views/NotFound.jsx b/src/components/views/NotFound.jsx
index 36641cb3..67024bb8 100644
--- a/src/components/views/NotFound.jsx
+++ b/src/components/views/NotFound.jsx
@@ -1,12 +1,12 @@
import React, { useEffect } from 'react';
-import useBoardStore from '../../useBoardStore';
+import useAppStore from '../../useAppStore';
import { Link } from "react-router-dom";
import { Container, Header, Logo, Page, Boards, BoardsTitle } from './styles/Home.styled';
const NotFound = ({ setBodyStyle }) => {
- const { setSelectedStyle } = useBoardStore(state => state);
+ const { setSelectedStyle } = useAppStore(state => state);
useEffect(() => {
setBodyStyle({
diff --git a/src/components/views/Thread.jsx b/src/components/views/Thread.jsx
index 9304096f..9aea0347 100644
--- a/src/components/views/Thread.jsx
+++ b/src/components/views/Thread.jsx
@@ -1,53 +1,50 @@
import React, { useState, useEffect } from 'react';
-import { Link, useNavigate, useParams, useLocation } from 'react-router-dom';
-import useBoardStore from '../../useBoardStore';
+import { Link, useNavigate, useParams } from 'react-router-dom';
+import useAppStore from '../../useAppStore';
import { Container, NavBar, Header, Break, PostForm, PostFormTable, BoardForm } from './styles/Board.styled';
import { ReplyFormLink, TopBar, BottomBar } from './styles/Thread.styled';
import { useComment, useAccountsActions } from '@plebbit/plebbit-react-hooks';
+import { Tooltip } from 'react-tooltip';
import CaptchaModal from '../CaptchaModal';
import ImageBanner from '../ImageBanner';
import PostLoader from '../PostLoader';
import ReplyModal from '../ReplyModal';
import SettingsModal from '../SettingsModal';
-import { Tooltip } from 'react-tooltip';
+import handleStyleChange from '../../utils/handleStyleChange';
import onError from '../../utils/onError';
import onSuccess from '../../utils/onSuccess';
import getDate from '../../utils/getDate';
import renderThreadComments from '../../utils/renderThreadComments';
+import useClickForm from '../../hooks/useClickForm';
-const Thread = ({ setBodyStyle }) => {
-
+const Thread = () => {
const {
- selectedTitle,
- setSelectedTitle,
- selectedAddress,
- setSelectedAddress,
- selectedThread,
- setSelectedThread,
+ captchaResponse, setCaptchaResponse,
+ defaultSubplebbits,
+ isSettingsOpen, setIsSettingsOpen,
+ selectedAddress, setSelectedAddress,
selectedStyle,
- setSelectedStyle,
- captchaResponse,
- setCaptchaResponse
- } = useBoardStore(state => state);
+ selectedThread, setSelectedThread,
+ selectedTitle, setSelectedTitle,
+ showPostForm,
+ showPostFormLink
+ } = useAppStore(state => state);
- const [defaultSubplebbits, setDefaultSubplebbits] = useState([]);
- const [showPostFormLink, setShowPostFormLink] = useState(true);
- const [showPostForm, setShowPostForm] = useState(false);
+
const [name, setName] = useState('');
const [subject, setSubject] = useState('');
const [commentContent, setCommentContent] = useState('');
const { publishComment } = useAccountsActions();
const [isCaptchaOpen, setIsCaptchaOpen] = useState(false);
const [isReplyOpen, setIsReplyOpen] = useState(false);
- const [isSettingsOpen, setIsSettingsOpen] = useState(false);
const [captchaImage, setCaptchaImage] = useState('');
const navigate = useNavigate();
- const location = useLocation();
const [prevScrollPos, setPrevScrollPos] = useState(0);
const [visible, setVisible] = useState(true);
const comment = useComment(`${selectedThread}`);
const { subplebbitAddress, threadCid } = useParams();
+ const handleClickForm = useClickForm();
// temporary title from JSON, gets subplebbitAddress and threadCid from URL
@@ -59,24 +56,6 @@ const Thread = ({ setBodyStyle }) => {
setSelectedTitle(selectedSubplebbit.title);
}
}, [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
useEffect(() => {
@@ -91,62 +70,9 @@ const Thread = ({ setBodyStyle }) => {
return () => window.removeEventListener('scroll', handleScroll);
}, [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) => {
if (challengeVerification.challengeSuccess === true) {
onSuccess('challenge success', {publishedCid: challengeVerification.publication.cid})
@@ -227,13 +153,6 @@ const Thread = ({ setBodyStyle }) => {
};
- const handleClickForm = () => {
- setShowPostFormLink(false);
- setShowPostForm(true);
- navigate(`/${selectedAddress}/thread/${selectedThread}/post`);
- };
-
-
const handleClickTop = () => {
window.scrollTo(0, 0);
}
@@ -293,94 +212,6 @@ const Thread = ({ setBodyStyle }) => {
const handleSettingsOpen = () => {
setIsSettingsOpen(true);
}
-
-
- 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));
- }
- }
diff --git a/src/hooks/useClickForm.js b/src/hooks/useClickForm.js
new file mode 100644
index 00000000..a69857ca
--- /dev/null
+++ b/src/hooks/useClickForm.js
@@ -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;
diff --git a/src/useBoardStore.js b/src/useAppStore.js
similarity index 67%
rename from src/useBoardStore.js
rename to src/useAppStore.js
index bd76d9df..7aea5d5a 100644
--- a/src/useBoardStore.js
+++ b/src/useAppStore.js
@@ -1,6 +1,9 @@
import { create } from 'zustand';
-const useBoardStore = create((set) => ({
+const useAppStore = create((set) => ({
+ captchaResponse: '',
+ setCaptchaResponse: (response) => set({ captchaResponse: response }),
+
bodyStyle: JSON.parse(localStorage.getItem('bodyStyle')) || {
background: '#ffe url(/assets/fade.png) top repeat-x',
color: 'maroon',
@@ -10,24 +13,35 @@ const useBoardStore = create((set) => ({
localStorage.setItem('bodyStyle', JSON.stringify(bodyStyle));
set(() => ({ bodyStyle }));
},
-
- selectedTitle: '',
- setSelectedTitle: (title) => set({ selectedTitle: title }),
+ defaultSubplebbits: [],
+ setDefaultSubplebbits: (subplebbits) => set({ defaultSubplebbits: subplebbits }),
+
+ isSettingsOpen: false,
+ setIsSettingsOpen: (isOpen) => set({ isSettingsOpen: isOpen }),
+
selectedAddress: '',
setSelectedAddress: (address) => set({ selectedAddress: address }),
-
- selectedThread: '',
- setSelectedThread: (thread) => set({ selectedThread: thread }),
-
+
+
selectedStyle: localStorage.getItem('selectedStyle') || 'Yotsuba',
setSelectedStyle: (style) => {
localStorage.setItem('selectedStyle', style);
set({ selectedStyle: style });
},
+
+ selectedThread: '',
+ 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 }),
- captchaResponse: '',
- setCaptchaResponse: (response) => set({ captchaResponse: response }),
}));
-export default useBoardStore;
\ No newline at end of file
+export default useAppStore;
\ No newline at end of file
diff --git a/src/utils/handleStyleChange.js b/src/utils/handleStyleChange.js
new file mode 100644
index 00000000..9bd1053f
--- /dev/null
+++ b/src/utils/handleStyleChange.js
@@ -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;
\ No newline at end of file