diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..1a6287a4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,127 @@ +## [0.1.13](https://github.com/plebbit/plebchan/compare/v0.1.12...v0.1.13) (2023-10-05) + + +### Bug Fixes + +* **App.js:** remove automatic dark mode, because it's not part of 4chan UX and it's not old school, and the selected style is saved anyway ([e3b577b](https://github.com/plebbit/plebchan/commit/e3b577b6900ba87be0aab85745038dfe40fb6c6a)) +* **app:** new version info toast should only appear once ([bdb661b](https://github.com/plebbit/plebchan/commit/bdb661b0dc7317a37232c5a1869610b3efc1cbc0)) +* **CaptchaModal:** improve captcha visibility by fixing margin ([a312c64](https://github.com/plebbit/plebchan/commit/a312c64bb047f155bdcfe77f5c1832fb75512479)) +* **Catalog:** added missing post menu button to rules and description ([a7a845e](https://github.com/plebbit/plebchan/commit/a7a845eada43174bdf6df8846d7eb96011415474)) +* **EditLabel:** don't show the edit label if comment.original.content is identical to comment.content ([bcbf311](https://github.com/plebbit/plebchan/commit/bcbf311b952cc5b153b75a92f9840595ddd52c3e)) +* **home:** ensure removed threads don't appear in popular threads box ([fee9e6f](https://github.com/plebbit/plebchan/commit/fee9e6fa08ccc944f386a31a403a95637e8f119c)) +* **home:** fix displacement of threads while rendered in popular threads box ([06decd9](https://github.com/plebbit/plebchan/commit/06decd99e2f96c2c6e07e8fb8d5fafe5a8862707)) +* **home:** fix rerender with useEffect dep ([c63ade8](https://github.com/plebbit/plebchan/commit/c63ade854b3eec55b4e267a9da8af8f19d15d314)) +* **home:** remove fallback image warning ([eec7e5c](https://github.com/plebbit/plebchan/commit/eec7e5cd640ba554f8672ab3718f54b0444abba2)) +* **Home:** remove preload of boards because it's resource-intensive and doesn't have concurrency maximum ([8758996](https://github.com/plebbit/plebchan/commit/8758996c970244783f02b9342564209bec146150)) +* **hooks:** more accurate state strings ([2230049](https://github.com/plebbit/plebchan/commit/22300490ffb602201f5cccfccee0b0ea3aae9ba1)) +* **mobile reply:** remove unnecessary width calculation for reply images ([8e4ca88](https://github.com/plebbit/plebchan/commit/8e4ca881ad1ed90b8d9cf3084c01df7a570c6986)) +* **offline indicator:** check for online status every 30 minutes instead of 20 ([4eba8e7](https://github.com/plebbit/plebchan/commit/4eba8e7530516a798396696418e013251740481d)) +* **post form:** make subject field optional, not mandatory ([548d491](https://github.com/plebbit/plebchan/commit/548d4914bf1924e4312f5baa2773c98d027d011a)) +* **Post Form:** use defaultValue for Name when displayName is defined ([c779c34](https://github.com/plebbit/plebchan/commit/c779c34243dea4c08c527929f4796b7bfe5f199c)) +* **post:** fix misplaced pin and lock icons ([3a02990](https://github.com/plebbit/plebchan/commit/3a029906744c6f42d501567389ffa34ccf650b12)) +* **post:** fix misplaced user address ([1a4f7ac](https://github.com/plebbit/plebchan/commit/1a4f7ac281d39dc99f0b4759e76611460d7442d8)) +* **PostOnHover:** add embed thumbnail ([946606a](https://github.com/plebbit/plebchan/commit/946606ac1f4dbd73aad40a1af19a4b226545dd0b)) +* **Post:** remove markdown links showing them as text ([a5a01a8](https://github.com/plebbit/plebchan/commit/a5a01a8732f9ddcfc46932db23c812c039e9b3fb)) +* **Post:** remove unnecessary key property causing warning ([2d8ecaa](https://github.com/plebbit/plebchan/commit/2d8ecaa9a07a0cb0dda7cc30639228d2ee1d570c)) +* **scroll:** resolve race condition in onClick scroll-to-top behavior ([d5715e2](https://github.com/plebbit/plebchan/commit/d5715e29eb796b11d5fb98827cc8f44097d63afe)) +* **SettingsModal:** add page reload for automatic anon mode change for ENS name ([7f7c9ff](https://github.com/plebbit/plebchan/commit/7f7c9ffaf913d21ef78b8de44e6b1b662d2fc919)) +* **Thread:** fix undefined ([fdebe14](https://github.com/plebbit/plebchan/commit/fdebe14cdb8134ece9d5161ce5c572862c4aec89)) +* **Thread:** remove useless wrapper for webpage comment.link with no thumbnail ([e4b1fc6](https://github.com/plebbit/plebchan/commit/e4b1fc6d35c34c5c4108f20a0bd211ddb4031497)) +* **usestatestring:** don't show updating state if comment/subplebbit is succeeded ([7169366](https://github.com/plebbit/plebchan/commit/7169366d11d433f6e30c4c213a8f9bcf0a02685c)) +* **views:** add CSS effect for useAuthorAddress jank ([40a2ff9](https://github.com/plebbit/plebchan/commit/40a2ff94ea1fa80deb9f2384fc05f5e90d14590c)) +* **views:** add missing parser for quote links in thread op content ([be8bed7](https://github.com/plebbit/plebchan/commit/be8bed74ef04162fe5824a1c6e9be2e39f356098)) +* **views:** fix scrolling jank removing margin between desktop reply cards ([288ae4d](https://github.com/plebbit/plebchan/commit/288ae4d066d310f1e5df8d433124015b1c63c63e)) + + +### Features + +* **AdminListModal:** inform the user when a board doesn't have moderators yet ([8a81177](https://github.com/plebbit/plebchan/commit/8a81177dd23a750e70669b8176f00d2969e47069)) +* **home:** boards box shows list of boards being moderated by the user ([fabec54](https://github.com/plebbit/plebchan/commit/fabec543bf0be636a63cdcbe693583cd5da215a2)) +* **home:** improve popular threads box with much more accurate conditions ([053ec57](https://github.com/plebbit/plebchan/commit/053ec57bc45413d00b196f8b42d59d90178d21bb)) +* **home:** recent threads box only shows posts with media ([de2ac49](https://github.com/plebbit/plebchan/commit/de2ac49c1f7ee4d7d2115a8bd438f0f04a9b5ff3)) +* **home:** redesigned home to be more similar to 4chan, with boards box listing all boards and thread box showing recent threads ([685cbb3](https://github.com/plebbit/plebchan/commit/685cbb3c170dc343794c316e42693e937b1bbdfd)) +* **SettingsModal:** add button to create an account and automatically switching to it, update setting description and modal width ([57c3f71](https://github.com/plebbit/plebchan/commit/57c3f71a22b151ea2aa04d4427249d2695d487be)) +* **SettingsModal:** automatically disable anon mode and tell the user, detecting ENS name when importing account, saving account or saving ENS ([c59bbb6](https://github.com/plebbit/plebchan/commit/c59bbb6f50e8b89e516255b947e23b8bfa6022f9)) +* **SettingsModal:** force keep the same account id when saving to allow faster account import ([eab5469](https://github.com/plebbit/plebchan/commit/eab546939d8be5abe599a25e5cad6538bf4d1b0f)) +* **Share button:** add success toast for copying share link to clipboard ([f0c0e64](https://github.com/plebbit/plebchan/commit/f0c0e64d750c98e6d2cad80bc721ddff228aa523)) +* **views:** show board admin role next to usernames, if any, with capcode colors and admin modal function ([e9cfdac](https://github.com/plebbit/plebchan/commit/e9cfdac18f7111a0fe42900c6f56133ce1819d14)) + + + +## [0.1.12](https://github.com/plebbit/plebchan/compare/v0.1.11...v0.1.12) (2023-09-18) + + +### Bug Fixes + +* show new version info toast only once ([3f33dcb](https://github.com/plebbit/plebchan/commit/3f33dcb37a900ec5d0710842d7f0c9b0e4f0cf2e)) + + +### Reverts + +* Revert "add support for non-direct imgur links" ([be89323](https://github.com/plebbit/plebchan/commit/be8932343184a96d719cf5fd4a5032ce5162d12a)) +* Revert "Update SettingsModal.jsx" ([5c72bc6](https://github.com/plebbit/plebchan/commit/5c72bc69e1c5e125c8c9348b3e2c5a0c877f5bdb)) +* Revert "fix conditional useMemo" ([572ce08](https://github.com/plebbit/plebchan/commit/572ce0869adbc51fa5d5855152350fe041bad96f)) + + + +## [0.1.10](https://github.com/plebbit/plebchan/compare/v0.1.9...v0.1.10) (2023-08-10) + + +### Reverts + +* Revert "add refresh button" ([f7ae7e7](https://github.com/plebbit/plebchan/commit/f7ae7e78dfb36391f39a28f444b86f90ddc1996e)) + + + +## [0.1.8](https://github.com/plebbit/plebchan/compare/v0.1.7...v0.1.8) (2023-06-09) + + + +## [0.1.7](https://github.com/plebbit/plebchan/compare/v0.1.6...v0.1.7) (2023-06-08) + + +### Reverts + +* Revert "refactor toasts" ([37d4966](https://github.com/plebbit/plebchan/commit/37d49668e4f4ae80bc9129a1061a00932bb63abd)) + + + +## [0.1.5](https://github.com/plebbit/plebchan/compare/v0.1.4...v0.1.5) (2023-05-12) + + + +## [0.1.4](https://github.com/plebbit/plebchan/compare/v0.1.3...v0.1.4) (2023-05-11) + + + +## [0.1.3](https://github.com/plebbit/plebchan/compare/v0.1.2...v0.1.3) (2023-05-06) + + + +## [0.1.2](https://github.com/plebbit/plebchan/compare/v0.1.1...v0.1.2) (2023-04-29) + + + +## [0.1.1](https://github.com/plebbit/plebchan/compare/v0.1.0...v0.1.1) (2023-04-27) + + + +# [0.1.0](https://github.com/plebbit/plebchan/compare/0acd472a9fce50a66964d11750ee9e1275a49617...v0.1.0) (2023-04-24) + + +### Bug Fixes + +* added png lowercase ([908dcd7](https://github.com/plebbit/plebchan/commit/908dcd77faa313e05d74c1e6865c29ae53ebceac)) +* delete uppercase png ([0acd472](https://github.com/plebbit/plebchan/commit/0acd472a9fce50a66964d11750ee9e1275a49617)) + + +### Reverts + +* Revert "fix subplebbitAddress in ReplyModal" ([24b2177](https://github.com/plebbit/plebchan/commit/24b2177e8743785591a0947cbdb85bd87a9cdc56)) +* Revert "removed markdown" ([33eec76](https://github.com/plebbit/plebchan/commit/33eec7644a564bc8825f48927b3a054858991744)) +* Revert "fix touch bug" ([777935e](https://github.com/plebbit/plebchan/commit/777935ecbe1e3d781bc4bbadf8f824323acc8c8d)) +* Revert "test InfiniteScroll" ([212a0c1](https://github.com/plebbit/plebchan/commit/212a0c10e8a563b75eb35f8d20ba8b93d0adec45)) +* Revert "better lint, added InfiniteScroll, debugUtils" ([af0a4c6](https://github.com/plebbit/plebchan/commit/af0a4c67de33c544f997ec7bd64d94e5a5a41df3)) + + + diff --git a/package.json b/package.json index 6867b918..31b653d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "plebchan", - "version": "0.1.12", + "version": "0.1.13", "private": true, "dependencies": { "@babel/core": "7.21.3", diff --git a/src/App.js b/src/App.js index c9b59773..eb2c5ca2 100755 --- a/src/App.js +++ b/src/App.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef } from 'react'; import { Route, Routes, useLocation } from 'react-router-dom'; import { Helmet } from 'react-helmet-async'; import 'react-tooltip/dist/react-tooltip.css'; @@ -21,6 +21,7 @@ import Thread from './components/views/Thread'; import CaptchaModal from './components/modals/CaptchaModal'; import { importAll } from './components/ImageBanner'; import preloadImages from './utils/preloadImages'; +import showNewVersionToast from './utils/showNewVersionToast'; import useError from './hooks/useError'; import useInfo from './hooks/useInfo'; import useSuccess from './hooks/useSuccess'; @@ -44,12 +45,16 @@ export default function App() { const location = useLocation(); const isElectron = window.electron && window.electron.isElectron; + useEffect(() => { + if (location.state?.scrollToTop) { + window.scrollTo(0, 0); + } + }, [location]); + const [, setNewErrorMessage] = useError(); const [, setNewSuccessMessage] = useSuccess(); const [, setNewInfoMessage] = useInfo(); - const [infoMessageShown, setInfoMessageShown] = useState(false); - useEffect(() => { const successToast = localStorage.getItem('successToast'); const errorToast = localStorage.getItem('errorToast'); @@ -64,41 +69,15 @@ export default function App() { } }, [setNewErrorMessage, setNewSuccessMessage]); - // check for new version - // TODO: delete this code and toast when v1.0.0 is released + // TODO: delete this code and toast when v1.0.0 is released (minor updates expected) + let hasRun = useRef(false); + useEffect(() => { - const fetchVersionInfo = async () => { - try { - const packageRes = await fetch('https://raw.githubusercontent.com/plebbit/plebchan/master/package.json', { cache: 'no-cache' }); - const packageData = await packageRes.json(); - - if (packageJson.version !== packageData.version && !infoMessageShown) { - const newVersionInfo = isElectron - ? `New version available, plebchan v${packageData.version}. You are using v${packageJson.version}. Download the latest version here: https://github.com/plebbit/plebchan/releases/latest` - : `New version available, plebchan v${packageData.version}. You are using v${packageJson.version}. Refresh to update.`; - setNewInfoMessage(newVersionInfo); - setInfoMessageShown(true); - } - - if (commitRef.length > 0) { - const commitRes = await fetch('https://api.github.com/repos/plebbit/plebchan/commits?per_page=1&sha=development', { cache: 'no-cache' }); - const commitData = await commitRes.json(); - - const latestCommitHash = commitData[0].sha; - - if (latestCommitHash.trim() !== commitRef.trim() && !infoMessageShown) { - const newVersionInfo = `New dev version available, commit ${latestCommitHash.slice(0, 7)}. You are using commit ${commitRef.slice(0, 7)}. Refresh to update.`; - setNewInfoMessage(newVersionInfo); - setInfoMessageShown(true); - } - } - } catch (error) { - console.error('Failed to fetch latest version info:', error); - } - }; - - fetchVersionInfo(); - }, [setNewInfoMessage, isElectron, infoMessageShown]); + if (!hasRun.current) { + showNewVersionToast(setNewInfoMessage, isElectron, packageJson, commitRef); + hasRun.current = true; + } + }, [setNewInfoMessage, isElectron]); // preload banners useEffect(() => { diff --git a/src/components/Embed.jsx b/src/components/Embed.jsx index 80283dae..f8d61729 100644 --- a/src/components/Embed.jsx +++ b/src/components/Embed.jsx @@ -72,7 +72,7 @@ const TwitterEmbed = ({ parsedUrl }) => { allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share' title={parsedUrl.href} srcdoc={` -
+
@@ -101,7 +101,7 @@ const RedditEmbed = ({ parsedUrl }) => { margin: 0!important; } -
+
@@ -152,7 +152,7 @@ const TiktokEmbed = ({ parsedUrl }) => { allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share' title={parsedUrl.href} srcdoc={` -
+
@@ -177,7 +177,7 @@ const InstagramEmbed = ({ parsedUrl }) => { allow='accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share' title={parsedUrl.href} srcdoc={` -
+
diff --git a/src/components/OfflineIndicator.jsx b/src/components/OfflineIndicator.jsx index e33f8c2b..46c737b4 100644 --- a/src/components/OfflineIndicator.jsx +++ b/src/components/OfflineIndicator.jsx @@ -3,7 +3,7 @@ import { useSubplebbit } from '@plebbit/plebbit-react-hooks'; const OfflineIndicator = ({ address, className, tooltipPlace }) => { const subplebbit = useSubplebbit({ subplebbitAddress: address }); - const isOnline = subplebbit.updatedAt > Date.now() / 1000 - 60 * 20; + const isOnline = subplebbit.updatedAt > Date.now() / 1000 - 60 * 30; return ( <> diff --git a/src/components/styled/views/Board.styled.jsx b/src/components/styled/views/Board.styled.jsx index 52a7ef36..b90370e2 100644 --- a/src/components/styled/views/Board.styled.jsx +++ b/src/components/styled/views/Board.styled.jsx @@ -2147,6 +2147,12 @@ export const BoardForm = styled.div` margin: 0; } + .thread-icons-mobile { + margin-top: -1px; + margin-right: 2px; + image-rendering: pixelated; + } + blockquote { padding-top: 5px; display: block; diff --git a/src/components/styled/views/Thread.styled.jsx b/src/components/styled/views/Thread.styled.jsx index 5803acad..01ef11ff 100644 --- a/src/components/styled/views/Thread.styled.jsx +++ b/src/components/styled/views/Thread.styled.jsx @@ -1336,6 +1336,12 @@ export const BoardForm = styled.div` object-fit: scale-down; } + .thread-icons-mobile { + margin-top: -1px; + margin-right: 2px; + image-rendering: pixelated; + } + blockquote { padding-top: 5px; display: block; diff --git a/src/components/views/All.jsx b/src/components/views/All.jsx index 78ba0ff9..b89362f5 100755 --- a/src/components/views/All.jsx +++ b/src/components/views/All.jsx @@ -2,7 +2,7 @@ import React, { Fragment, useCallback, useEffect, useMemo, useState, useRef } fr import { confirmAlert } from 'react-confirm-alert'; import { createPortal } from 'react-dom'; import { Helmet } from 'react-helmet-async'; -import { Link, useNavigate } from 'react-router-dom'; +import { Link, useLocation, useNavigate } from 'react-router-dom'; import { Tooltip } from 'react-tooltip'; import { Virtuoso } from 'react-virtuoso'; import { useAccount, useAccountComments, useFeed, usePublishCommentEdit, useSubplebbits } from '@plebbit/plebbit-react-hooks'; @@ -541,7 +541,12 @@ const All = () => { navigate(`/p/${selected}`); }; + const location = useLocation(); + useEffect(() => { + if (location.state?.scrollToTop) { + lastVirtuosoStates[`all`] = null; + } const setLastVirtuosoState = () => { virtuosoRef.current?.getState((snapshot) => { if (snapshot?.scrollTop === 0 || snapshot?.ranges?.length) { @@ -552,7 +557,7 @@ const All = () => { window.addEventListener('scroll', setLastVirtuosoState); return () => window.removeEventListener('scroll', setLastVirtuosoState); - }, []); + }, [location.state?.scrollToTop]); const lastVirtuosoState = lastVirtuosoStates['all']; @@ -578,14 +583,9 @@ const All = () => { <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ] [ {defaultSubplebbits.map((subplebbit, index) => ( @@ -652,10 +652,9 @@ const All = () => {   { handleStyleChange({ target: { value: 'Yotsuba' } }); - window.scrollTo(0, 0); }} > Home @@ -3486,14 +3485,9 @@ const All = () => { > <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ] [ {defaultSubplebbits.map((subplebbit, index) => ( diff --git a/src/components/views/AllCatalog.jsx b/src/components/views/AllCatalog.jsx index f5fa5d1b..dd9735d2 100755 --- a/src/components/views/AllCatalog.jsx +++ b/src/components/views/AllCatalog.jsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { Helmet } from 'react-helmet-async'; -import { Link, useNavigate } from 'react-router-dom'; +import { Link, useLocation, useNavigate } from 'react-router-dom'; import { confirmAlert } from 'react-confirm-alert'; import { Tooltip } from 'react-tooltip'; import { Virtuoso } from 'react-virtuoso'; @@ -777,7 +777,12 @@ const AllCatalog = () => { setSelectedFeed(feed.filter((feed) => feed.title === title)); }; + const location = useLocation(); + useEffect(() => { + if (location.state?.scrollToTop) { + lastVirtuosoStates[`allCatalog`] = null; + } const setLastVirtuosoState = () => { virtuosoRef.current?.getState((snapshot) => { if (snapshot?.scrollTop === 0 || snapshot?.ranges?.length) { @@ -788,7 +793,7 @@ const AllCatalog = () => { window.addEventListener('scroll', setLastVirtuosoState); return () => window.removeEventListener('scroll', setLastVirtuosoState); - }, [selectedAddress]); + }, [selectedAddress, location.state?.scrollToTop]); const lastVirtuosoState = lastVirtuosoStates['allCatalog']; @@ -813,14 +818,9 @@ const AllCatalog = () => { <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ] [ {defaultSubplebbits.map((subplebbit, index) => ( @@ -894,10 +894,9 @@ const AllCatalog = () => {   { handleStyleChange({ target: { value: 'Yotsuba' } }); - window.scrollTo(0, 0); }} > Home @@ -933,11 +932,11 @@ const AllCatalog = () => {
- [Return] + [Return]
- Return + Return
{subplebbits.state === 'succeeded' ? null : ( @@ -1008,14 +1007,9 @@ const AllCatalog = () => { > <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ] [ {defaultSubplebbits.map((subplebbit, index) => ( diff --git a/src/components/views/Board.jsx b/src/components/views/Board.jsx index 795e8767..035b2592 100755 --- a/src/components/views/Board.jsx +++ b/src/components/views/Board.jsx @@ -1,7 +1,7 @@ import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { Helmet } from 'react-helmet-async'; -import { Link, useNavigate, useParams } from 'react-router-dom'; +import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; import { confirmAlert } from 'react-confirm-alert'; import { Tooltip } from 'react-tooltip'; import { Virtuoso } from 'react-virtuoso'; @@ -698,7 +698,12 @@ const Board = () => { } }; + const location = useLocation(); + useEffect(() => { + if (location.state?.scrollToTop) { + lastVirtuosoStates[selectedAddress] = null; + } const setLastVirtuosoState = () => { virtuosoRef.current?.getState((snapshot) => { if (snapshot?.scrollTop === 0 || snapshot?.ranges?.length) { @@ -709,7 +714,7 @@ const Board = () => { window.addEventListener('scroll', setLastVirtuosoState); return () => window.removeEventListener('scroll', setLastVirtuosoState); - }, [selectedAddress]); + }, [selectedAddress, location.state?.scrollToTop]); const lastVirtuosoState = lastVirtuosoStates[selectedAddress]; @@ -736,14 +741,9 @@ const Board = () => { <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ] [ {defaultSubplebbits.map((subplebbit, index) => ( @@ -813,10 +813,9 @@ const Board = () => {   { handleStyleChange({ target: { value: 'Yotsuba' } }); - window.scrollTo(0, 0); }} > Home @@ -919,16 +918,8 @@ const Board = () => {
- [ - { - window.scrollTo(0, 0); - }} - > - Catalog - - ]{subplebbit.roles && subplebbit?.roles?.[account?.author?.address]?.role === 'admin' ? : null} + [Catalog] + {subplebbit.roles && subplebbit?.roles?.[account?.author?.address]?.role === 'admin' ? : null}
{subplebbit?.state === 'succeeded' ? ( <> @@ -952,14 +943,7 @@ const Board = () => { )}
- { - window.scrollTo(0, 0); - }} - > - Catalog - + Catalog
@@ -1153,10 +1137,20 @@ const Board = () => { ## Board Mods
  - - Sticky + + Sticky   - Closed + Closed
@@ -1378,10 +1372,20 @@ const Board = () => { ## Board Mods   - - Sticky + + Sticky   - Closed + Closed
@@ -1679,10 +1683,20 @@ const Board = () => { ## Board Mods   - - Sticky + + Sticky   - Closed + Closed
@@ -3317,24 +3331,24 @@ const Board = () => { )  -
- - {thread.pinned ? ( - Sticky - ) : null} - {thread.locked ? ( - Closed - ) : null} + + {thread.pinned ? ( + Sticky + ) : null} + {thread.locked ? ( + Closed + ) : null} +
{thread.title ? ( @@ -3729,7 +3743,6 @@ const Board = () => { to={`/p/${selectedAddress}/c/${thread.cid}`} onClick={() => { setSelectedThread(thread.cid); - window.scrollTo(0, 0); }} className='button-mobile' > @@ -4538,14 +4551,9 @@ const Board = () => { > <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ]  {defaultSubplebbits.map((subplebbit, index) => ( diff --git a/src/components/views/Catalog.jsx b/src/components/views/Catalog.jsx index c6afb37c..706e0e86 100755 --- a/src/components/views/Catalog.jsx +++ b/src/components/views/Catalog.jsx @@ -1,7 +1,7 @@ import React, { useCallback, useLayoutEffect, useEffect, useMemo, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { Helmet } from 'react-helmet-async'; -import { Link, useNavigate, useParams } from 'react-router-dom'; +import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'; import { confirmAlert } from 'react-confirm-alert'; import { Tooltip } from 'react-tooltip'; import { Virtuoso } from 'react-virtuoso'; @@ -1462,7 +1462,12 @@ const Catalog = () => { } }; + const location = useLocation(); + useEffect(() => { + if (location.state?.scrollToTop) { + lastVirtuosoStates[`${selectedAddress}-catalog`] = null; + } const setLastVirtuosoState = () => { virtuosoRef.current?.getState((snapshot) => { if (snapshot?.scrollTop === 0 || snapshot?.ranges?.length) { @@ -1473,7 +1478,7 @@ const Catalog = () => { window.addEventListener('scroll', setLastVirtuosoState); return () => window.removeEventListener('scroll', setLastVirtuosoState); - }, [selectedAddress]); + }, [selectedAddress, location.state?.scrollToTop]); const lastVirtuosoState = lastVirtuosoStates[`${selectedAddress}-catalog`]; @@ -1498,14 +1503,9 @@ const Catalog = () => { <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ] [ {defaultSubplebbits.map((subplebbit, index) => ( @@ -1580,10 +1580,9 @@ const Catalog = () => {   { handleStyleChange({ target: { value: 'Yotsuba' } }); - window.scrollTo(0, 0); }} > Home @@ -1688,16 +1687,8 @@ const Catalog = () => {
- [ - { - window.scrollTo(0, 0); - }} - > - Return - - ]{subplebbit.roles && subplebbit?.roles[account?.author?.address]?.role === 'admin' ? : null} + [Return] + {subplebbit.roles && subplebbit?.roles[account?.author?.address]?.role === 'admin' ? : null}
{subplebbit.state === 'succeeded' ? ( <> @@ -1721,14 +1712,7 @@ const Catalog = () => { )}
- { - window.scrollTo(0, 0); - }} - > - Return - + Return

@@ -1793,14 +1777,9 @@ const Catalog = () => { > <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ]  {defaultSubplebbits.map((subplebbit, index) => ( diff --git a/src/components/views/Description.jsx b/src/components/views/Description.jsx index 8da9376e..9305717e 100644 --- a/src/components/views/Description.jsx +++ b/src/components/views/Description.jsx @@ -160,14 +160,9 @@ const Description = () => { <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ] [ {defaultSubplebbits.map((subplebbit, index) => ( @@ -245,7 +240,6 @@ const Description = () => { to='/' onClick={() => { handleStyleChange({ target: { value: 'Yotsuba' } }); - window.scrollTo(0, 0); }} > Home @@ -275,25 +269,13 @@ const Description = () => {
- { - window.scrollTo(0, 0); - }} - > - Return + + Return
- { - window.scrollTo(0, 0); - }} - > - Catalog - + Catalog
@@ -323,10 +305,10 @@ const Description = () => { - [Return] + [Return] - [Catalog] + [Catalog] [ @@ -605,10 +587,20 @@ const Description = () => { ## Board Mods   - - Sticky + + Sticky   - Closed + Closed
@@ -684,14 +676,9 @@ const Description = () => { > <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ]  {defaultSubplebbits.map((subplebbit, index) => ( diff --git a/src/components/views/Home.jsx b/src/components/views/Home.jsx index dcd6b7a9..d6db7344 100644 --- a/src/components/views/Home.jsx +++ b/src/components/views/Home.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useRef, useState } from 'react'; import { Helmet } from 'react-helmet-async'; -import { useAccount, useComment, useSubplebbit, useSubplebbits } from '@plebbit/plebbit-react-hooks'; +import { useAccount, useAccountSubplebbits, useComment, useSubplebbit, useSubplebbits } from '@plebbit/plebbit-react-hooks'; import { Link, useNavigate } from 'react-router-dom'; import { Container, Header, Logo, Page, Search, About, AboutTitle, AboutContent, BoardsBox, BoardsContent, Footer } from '../styled/views/Home.styled'; import BoardAvatar from '../BoardAvatar'; @@ -13,73 +13,108 @@ import { Tooltip } from 'react-tooltip'; const { version } = packageJson; const commitRef = process?.env?.REACT_APP_COMMIT_REF ? ` ${process.env.REACT_APP_COMMIT_REF.slice(0, 7)}` : ''; -const RecentThread = ({ commentCid }) => { +const PopularThreads = ({ commentCid }) => { const comment = useComment({ commentCid }); const subplebbit = useSubplebbit({ subplebbitAddress: comment?.subplebbitAddress }); const { setSelectedAddress, setSelectedTitle } = useGeneralStore((state) => state); const commentMediaInfo = getCommentMediaInfo(comment); - const isMediaShowed = - comment.link && - commentMediaInfo && - (commentMediaInfo.type === 'image' || - commentMediaInfo.type === 'video' || - (commentMediaInfo.type === 'webpage' && commentMediaInfo.thumbnail) || - (commentMediaInfo.type === 'iframe' && commentMediaInfo.thumbnail)) - ? true - : false; return ( - <> - {isMediaShowed && comment.replyCount > 0 && !comment.removed ? ( -
-
- {subplebbit.title || subplebbit.address} -
-
- { - setSelectedTitle(subplebbit?.title); - setSelectedAddress(comment?.subplebbitAddress); - window.scrollTo(0, 0); - }} - > - {commentMediaInfo?.type === 'webpage' && comment.thumbnailUrl ? ( - post - ) : commentMediaInfo?.type === 'image' ? ( - post - ) : commentMediaInfo?.type === 'video' ? ( -
-
- {comment?.title ? {comment?.title} : null} - {comment?.content ? (comment.content.length > 99 ? `: ${comment.content.substring(0, 99)}...` : `: ${comment.content}`) : null} -
-
- ) : null} - +
+
+ {subplebbit.title || subplebbit.address} +
+
+ { + setSelectedTitle(subplebbit?.title); + setSelectedAddress(comment?.subplebbitAddress); + }} + > + {commentMediaInfo?.type === 'webpage' && commentMediaInfo?.thumbnail ? ( + post + ) : commentMediaInfo?.type === 'image' ? ( + post + ) : commentMediaInfo?.type === 'video' ? ( +
+
+ {comment?.title ? {comment?.title} : null} + {comment?.content ? (comment.content.length > 99 ? `: ${comment.content.substring(0, 99)}...` : `: ${comment.content}`) : null} +
+
); }; const Home = () => { const { bodyStyle, setBodyStyle, defaultSubplebbits, defaultNsfwSubplebbits, setSelectedAddress, selectedStyle, setSelectedStyle } = useGeneralStore((state) => state); + const { accountSubplebbits } = useAccountSubplebbits(); + const accountSubplebbitsAddresses = Object.values(accountSubplebbits).map((subplebbit) => subplebbit.address); const sfwAddresses = defaultSubplebbits.map((subplebbit) => subplebbit.address); const sfwSubs = useSubplebbits({ subplebbitAddresses: sfwAddresses }); const sfwList = sfwSubs.subplebbits.map((subplebbit) => subplebbit?.address); - const sfwListCids = sfwSubs.subplebbits.map((s) => s?.lastPostCid); + const [sfwListCids, setSfwListCids] = useState([]); + + useEffect(() => { + let subplebbitToCid = {}; + + sfwSubs.subplebbits.forEach((s) => { + let maxTimestamp = -Infinity; + let mostRecentCid = null; + + if (s && s.posts && s.posts.pages && s.posts.pages.hot && s.posts.pages.hot.comments) { + for (const comment of Object.values(s.posts.pages.hot.comments)) { + const commentMediaInfo = getCommentMediaInfo(comment); + const isMediaShowed = + comment.link && + commentMediaInfo && + (commentMediaInfo.type === 'image' || + commentMediaInfo.type === 'video' || + (commentMediaInfo.type === 'webpage' && commentMediaInfo.thumbnail) || + (commentMediaInfo.type === 'iframe' && commentMediaInfo.thumbnail)); + + if ( + isMediaShowed && + comment.replyCount > 2 && + !comment.removed && + !comment.locked && + !comment.pinned && + comment.timestamp > Date.now() / 1000 - 60 * 60 * 24 * 30 && + comment.timestamp > maxTimestamp + ) { + maxTimestamp = comment.timestamp; + mostRecentCid = comment.cid; + } + } + + if (mostRecentCid) { + subplebbitToCid[s.address] = { cid: mostRecentCid, timestamp: maxTimestamp }; + } + } + }); + + const newSfwListCids = Object.values(subplebbitToCid) + .map((item) => item.cid) + .slice(0, 8); + + setSfwListCids((prevOrderedCids) => { + const uniqueCids = Array.from(new Set([...prevOrderedCids, ...newSfwListCids])); + return uniqueCids.slice(0, 8); + }); + }, [sfwSubs.subplebbits, sfwListCids]); const account = useAccount(); const navigate = useNavigate(); - const isElectron = window.electron && window.electron.isElectron; const inputRef = useRef(null); const prevStyle = useRef(selectedStyle); @@ -174,8 +209,8 @@ const Home = () => {

Boards

-
-
+
+

{

  - { - window.scrollTo(0, 0); - }} - > + [view all]
    @@ -203,10 +232,10 @@ const Home = () => { { - window.scrollTo(0, 0); + to={{ + pathname: `/p/${address}`, + state: { scrollToTop: true }, }} - to={`/p/${address}`} > {address} @@ -221,7 +250,7 @@ const Home = () => { )}
-
+ - {sfwListCids.map((cid) => ( - + ))} diff --git a/src/components/views/Pending.jsx b/src/components/views/Pending.jsx index 8d7022af..f3c98c6b 100755 --- a/src/components/views/Pending.jsx +++ b/src/components/views/Pending.jsx @@ -120,14 +120,9 @@ const Pending = () => { <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ] [ {defaultSubplebbits.map((subplebbit, index) => ( diff --git a/src/components/views/Rules.jsx b/src/components/views/Rules.jsx index a3c6080a..b7e0b8e3 100644 --- a/src/components/views/Rules.jsx +++ b/src/components/views/Rules.jsx @@ -170,14 +170,9 @@ const Rules = () => { <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ] [ {defaultSubplebbits.map((subplebbit, index) => ( @@ -255,7 +250,6 @@ const Rules = () => { to='/' onClick={() => { handleStyleChange({ target: { value: 'Yotsuba' } }); - window.scrollTo(0, 0); }} > Home @@ -285,25 +279,13 @@ const Rules = () => {
- { - window.scrollTo(0, 0); - }} - > - Return + + Return
- { - window.scrollTo(0, 0); - }} - > - Catalog - + Catalog
@@ -333,10 +315,10 @@ const Rules = () => { - [Return] + [Return] - [Catalog] + [Catalog] [ @@ -539,10 +521,20 @@ const Rules = () => { ## Board Mods   - - Sticky + + Sticky   - Closed + Closed
@@ -620,14 +612,9 @@ const Rules = () => { > <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ]  {defaultSubplebbits.map((subplebbit, index) => ( diff --git a/src/components/views/Subscriptions.jsx b/src/components/views/Subscriptions.jsx index 3f68eabc..bdc87928 100755 --- a/src/components/views/Subscriptions.jsx +++ b/src/components/views/Subscriptions.jsx @@ -2,7 +2,7 @@ import React, { Fragment, useCallback, useEffect, useMemo, useState, useRef } fr import { confirmAlert } from 'react-confirm-alert'; import { createPortal } from 'react-dom'; import { Helmet } from 'react-helmet-async'; -import { Link, useNavigate } from 'react-router-dom'; +import { Link, useLocation, useNavigate } from 'react-router-dom'; import { Tooltip } from 'react-tooltip'; import { Virtuoso } from 'react-virtuoso'; import { useAccount, useAccountComments, useFeed, usePublishCommentEdit, useSubplebbits } from '@plebbit/plebbit-react-hooks'; @@ -540,7 +540,12 @@ const Subscriptions = () => { navigate(`/p/${selected}`); }; + const location = useLocation(); + useEffect(() => { + if (location.state?.scrollToTop) { + lastVirtuosoStates[`subscriptions`] = null; + } const setLastVirtuosoState = () => { virtuosoRef.current?.getState((snapshot) => { if (snapshot?.scrollTop === 0 || snapshot?.ranges?.length) { @@ -551,7 +556,7 @@ const Subscriptions = () => { window.addEventListener('scroll', setLastVirtuosoState); return () => window.removeEventListener('scroll', setLastVirtuosoState); - }, []); + }, [location.state?.scrollToTop]); const lastVirtuosoState = lastVirtuosoStates['subscriptions']; @@ -577,14 +582,9 @@ const Subscriptions = () => { <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ] [ {defaultSubplebbits.map((subplebbit, index) => ( @@ -651,10 +651,9 @@ const Subscriptions = () => {   { handleStyleChange({ target: { value: 'Yotsuba' } }); - window.scrollTo(0, 0); }} > Home diff --git a/src/components/views/SubscriptionsCatalog.jsx b/src/components/views/SubscriptionsCatalog.jsx index 69ec3f70..73c85486 100755 --- a/src/components/views/SubscriptionsCatalog.jsx +++ b/src/components/views/SubscriptionsCatalog.jsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { Helmet } from 'react-helmet-async'; -import { Link, useNavigate } from 'react-router-dom'; +import { Link, useLocation, useNavigate } from 'react-router-dom'; import { confirmAlert } from 'react-confirm-alert'; import { Tooltip } from 'react-tooltip'; import { Virtuoso } from 'react-virtuoso'; @@ -710,7 +710,6 @@ const SubscriptionsCatalog = () => { isSettingsOpen, setIsSettingsOpen, originalCommentContent, - selectedAddress, setSelectedAddress, selectedStyle, setSelectedTitle, @@ -777,18 +776,23 @@ const SubscriptionsCatalog = () => { navigate(`/p/${selected}`); }; + const location = useLocation(); + useEffect(() => { + if (location.state?.scrollToTop) { + lastVirtuosoStates[`subscriptionsCatalog`] = null; + } const setLastVirtuosoState = () => { virtuosoRef.current?.getState((snapshot) => { if (snapshot?.scrollTop === 0 || snapshot?.ranges?.length) { - lastVirtuosoStates[`${selectedAddress}-catalog`] = snapshot; + lastVirtuosoStates[`subscriptionsCatalog`] = snapshot; } }); }; window.addEventListener('scroll', setLastVirtuosoState); return () => window.removeEventListener('scroll', setLastVirtuosoState); - }, [selectedAddress]); + }, [location.state?.scrollToTop]); const lastVirtuosoState = lastVirtuosoStates['subscriptionsCatalog']; @@ -813,14 +817,9 @@ const SubscriptionsCatalog = () => { <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ] [ {defaultSubplebbits.map((subplebbit, index) => ( @@ -894,10 +893,9 @@ const SubscriptionsCatalog = () => {   { handleStyleChange({ target: { value: 'Yotsuba' } }); - window.scrollTo(0, 0); }} > Home @@ -941,11 +939,11 @@ const SubscriptionsCatalog = () => {
- [Return] + [Return]
- Return + Return
{subplebbits.state === 'succeeded' ? null : ( @@ -1015,10 +1013,7 @@ const SubscriptionsCatalog = () => { > <> - [ - window.scrollTo(0, 0)}> - Subscriptions - + [Subscriptions ] [ {defaultSubplebbits.map((subplebbit, index) => ( diff --git a/src/components/views/Thread.jsx b/src/components/views/Thread.jsx index 2be47cfa..5ce6ab46 100755 --- a/src/components/views/Thread.jsx +++ b/src/components/views/Thread.jsx @@ -664,14 +664,9 @@ const Thread = () => { <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ] [ {defaultSubplebbits.map((subplebbit, index) => ( @@ -749,7 +744,6 @@ const Thread = () => { to='/' onClick={() => { handleStyleChange({ target: { value: 'Yotsuba' } }); - window.scrollTo(0, 0); }} > Home @@ -779,25 +773,13 @@ const Thread = () => {
- { - window.scrollTo(0, 0); - }} - > + Return
- { - window.scrollTo(0, 0); - }} - > - Catalog - + Catalog
@@ -898,7 +880,7 @@ const Thread = () => { [Return] - [Catalog] + [Catalog] [ @@ -2204,12 +2186,22 @@ const Thread = () => { )  - + {comment.pinned ? ( - Sticky + Sticky ) : null} {comment.locked ? ( - Closed + Closed ) : null}
@@ -3033,10 +3025,10 @@ const Thread = () => {

- [Return] + [Return] - [Catalog] + [Catalog] [ @@ -3141,7 +3133,7 @@ const Thread = () => {
- Catalog + Catalog
@@ -3222,14 +3214,9 @@ const Thread = () => { > <> - [ - window.scrollTo(0, 0)}> - All - + [All  /  - window.scrollTo(0, 0)}> - Subscriptions - + Subscriptions ]  {defaultSubplebbits.map((subplebbit, index) => ( diff --git a/src/utils/showNewVersionToast.js b/src/utils/showNewVersionToast.js new file mode 100644 index 00000000..a9c6b18e --- /dev/null +++ b/src/utils/showNewVersionToast.js @@ -0,0 +1,29 @@ +export const showNewVersionToast = async (setNewInfoMessage, isElectron, packageJson, commitRef) => { + try { + const packageRes = await fetch('https://raw.githubusercontent.com/plebbit/plebchan/master/package.json', { cache: 'no-cache' }); + const packageData = await packageRes.json(); + + if (packageJson.version !== packageData.version) { + const newVersionInfo = isElectron + ? `New version available, plebchan v${packageData.version}. You are using v${packageJson.version}. Download the latest version here: https://github.com/plebbit/plebchan/releases/latest` + : `New version available, plebchan v${packageData.version}. You are using v${packageJson.version}. Refresh to update.`; + setNewInfoMessage(newVersionInfo); + } + + if (commitRef.length > 0) { + const commitRes = await fetch('https://api.github.com/repos/plebbit/plebchan/commits?per_page=1&sha=development', { cache: 'no-cache' }); + const commitData = await commitRes.json(); + + const latestCommitHash = commitData[0].sha; + + if (latestCommitHash.trim() !== commitRef.trim()) { + const newVersionInfo = `New dev version available, commit ${latestCommitHash.slice(0, 7)}. You are using commit ${commitRef.slice(0, 7)}. Refresh to update.`; + setNewInfoMessage(newVersionInfo); + } + } + } catch (error) { + console.error('Failed to fetch latest version info:', error); + } +}; + +export default showNewVersionToast;