diff --git a/package.json b/package.json
index a43351d3..990d4f51 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
"react-scripts": "5.0.1",
"react-toastify": "9.1.1",
"react-tooltip": "5.10.0",
- "react-virtuoso": "4.3.2",
+ "react-virtuoso": "4.5.0",
"rehype-sanitize": "5.0.1",
"remark-breaks": "3.0.2",
"styled-components": "5.3.9",
@@ -120,7 +120,7 @@
}
},
"devDependencies": {
- "@babel/plugin-proposal-private-property-in-object": "7.21.11",
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@capacitor/cli": "3.6.0",
"concurrently": "8.0.1",
"cordova-res": "0.15.4",
diff --git a/src/components/Post.jsx b/src/components/Post.jsx
index 431c05f6..4d94e10c 100644
--- a/src/components/Post.jsx
+++ b/src/components/Post.jsx
@@ -6,7 +6,7 @@ import ForwardRefLink from './ForwardRefLink';
const Post = ({ content, postQuoteOnClick, postQuoteOnOver, postQuoteOnLeave, postQuoteRef }) => {
- const doubleNewlineContent = useMemo(() => content?.replaceAll(/\n(?!\n)/g, '\n\n'), [content]);
+ const doubleNewlineContent = content?.replace(/\n/g, ' \n\n');
const customSchema = useMemo(() => ({
...defaultSchema,
diff --git a/src/components/modals/ModerationModal.jsx b/src/components/modals/ModerationModal.jsx
index 9188516a..0c560045 100755
--- a/src/components/modals/ModerationModal.jsx
+++ b/src/components/modals/ModerationModal.jsx
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react";
import Modal from "react-modal";
import { Link } from "react-router-dom";
-import { usePublishCommentEdit } from "@plebbit/plebbit-react-hooks";
+import { useComment, usePublishCommentEdit } from "@plebbit/plebbit-react-hooks";
import { StyledModal } from "../styled/modals/ModerationModal.styled";
import useGeneralStore from "../../hooks/stores/useGeneralStore";
import useError from "../../hooks/useError";
@@ -20,9 +20,11 @@ const ModerationModal = ({ isOpen, closeModal, deletePost }) => {
setResolveCaptchaPromise,
} = useGeneralStore(state => state);
- const [pin, setPin] = useState(false);
+ const comment = useComment({commentCid: moderatingCommentCid});
+
+ const [pin, setPin] = useState(comment?.pinned);
const [deleteThread, setDeleteThread] = useState(deletePost);
- const [close, setClose] = useState(false);
+ const [close, setClose] = useState(comment?.locked);
const [reason, setReason] = useState('');
const [triggerPublishCommentEdit, setTriggerPublishCommentEdit] = useState(false);
diff --git a/src/components/modals/SettingsModal.jsx b/src/components/modals/SettingsModal.jsx
index 061a5c56..fceb7528 100755
--- a/src/components/modals/SettingsModal.jsx
+++ b/src/components/modals/SettingsModal.jsx
@@ -1,7 +1,6 @@
import React, { useState, useEffect, useRef } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import Modal from "react-modal";
-import { confirmAlert } from "react-confirm-alert";
import { deleteAccount, deleteCaches, exportAccount, importAccount, setAccount, setActiveAccount, useAccount, useAccounts, useResolvedAuthorAddress } from "@plebbit/plebbit-react-hooks";
import { StyledModal } from "../styled/modals/SettingsModal.styled";
import useError from "../../hooks/useError";
diff --git a/src/components/views/All.jsx b/src/components/views/All.jsx
index 01a647c5..3bfee60e 100755
--- a/src/components/views/All.jsx
+++ b/src/components/views/All.jsx
@@ -29,6 +29,7 @@ import SettingsModal from '../modals/SettingsModal';
import findShortParentCid from '../../utils/findShortParentCid';
import getCommentMediaInfo from '../../utils/getCommentMediaInfo';
import getDate from '../../utils/getDate';
+import getFormattedTime from '../../utils/getFormattedTime';
import handleAddressClick from '../../utils/handleAddressClick';
import handleImageClick from '../../utils/handleImageClick';
import handleQuoteClick from '../../utils/handleQuoteClick';
@@ -851,7 +852,10 @@ const All = () => {
{{({ shortAuthorAddress }) => (shortAuthorAddress)} }
)
- {getDate(thread.timestamp)}
+ {getDate(thread.timestamp)}
c/
@@ -1179,7 +1183,10 @@ const All = () => {
- {getDate(reply.timestamp)}
+ {getDate(reply.timestamp)}
c/
@@ -1778,7 +1785,10 @@ const All = () => {
tooltipPlace="top"
className="offline-mobile-sub" />
-
+
{getDate(thread.timestamp)}
c/
@@ -2016,7 +2026,10 @@ const All = () => {
tooltipPlace="top"
className="offline-mobile-sub-reply" />
-
+
{getDate(reply.timestamp)}
c/
{reply.shortCid ? (
diff --git a/src/components/views/Board.jsx b/src/components/views/Board.jsx
index 4c39ceec..15e82c62 100755
--- a/src/components/views/Board.jsx
+++ b/src/components/views/Board.jsx
@@ -30,6 +30,7 @@ import SettingsModal from '../modals/SettingsModal';
import findShortParentCid from '../../utils/findShortParentCid';
import getCommentMediaInfo from '../../utils/getCommentMediaInfo';
import getDate from '../../utils/getDate';
+import getFormattedTime from '../../utils/getFormattedTime';
import handleAddressClick from '../../utils/handleAddressClick';
import handleImageClick from '../../utils/handleImageClick';
import handleQuoteClick from '../../utils/handleQuoteClick';
@@ -786,6 +787,9 @@ const Board = () => {
All
Subscriptions
+ {!defaultSubplebbits.some(subplebbit => subplebbit.address === selectedAddress) && (
+ {selectedAddress}
+ )}
{defaultSubplebbits.map(subplebbit => (
{subplebbit.title ? subplebbit.title : subplebbit.address}
@@ -950,7 +954,10 @@ const Board = () => {
onClick={() => {setIsAdminListOpen(!isAdminListOpen)}}
>## Board Admins
- {getDate(subplebbit.createdAt)}
+ {getDate(subplebbit.createdAt)}
@@ -1040,7 +1047,10 @@ const Board = () => {
style={{marginBottom: "-15px"}}>Rules
- {getDate(subplebbit.createdAt)}
+ {getDate(subplebbit.createdAt)}
@@ -1075,7 +1085,10 @@ const Board = () => {
onClick={() => {setIsAdminListOpen(!isAdminListOpen)}}
>## Board Admins
- {getDate(subplebbit.createdAt)}
+ {getDate(subplebbit.createdAt)}
@@ -1165,7 +1178,10 @@ const Board = () => {
style={{marginBottom: "-15px"}}>Rules
- {getDate(subplebbit.createdAt)}
+ {getDate(subplebbit.createdAt)}
@@ -1222,7 +1238,10 @@ const Board = () => {
onClick={() => {setIsAdminListOpen(!isAdminListOpen)}}
>## Board Admins
- {getDate(subplebbit.createdAt)}
+ {getDate(subplebbit.createdAt)}
@@ -1310,7 +1329,10 @@ const Board = () => {
style={{marginBottom: "-15px"}}>Welcome to {subplebbit.title || subplebbit.address}
- {getDate(subplebbit.createdAt)}
+ {getDate(subplebbit.createdAt)}
{subplebbit.suggested?.avatarUrl ? (
@@ -1510,7 +1532,12 @@ const Board = () => {
{({ shortAuthorAddress }) => (shortAuthorAddress)}
)
- {getDate(thread.timestamp)}
+
+ {getDate(thread.timestamp)}
+
c/
@@ -1853,7 +1880,10 @@ const Board = () => {
- {getDate(reply.timestamp)}
+ {getDate(reply.timestamp)}
c/
@@ -2426,7 +2456,10 @@ const Board = () => {
)
: null}
-
+
{getDate(thread.timestamp)}
c/
@@ -2653,7 +2686,10 @@ const Board = () => {
-
+
{getDate(reply.timestamp)}
c/
{reply.shortCid ? (
diff --git a/src/components/views/Catalog.jsx b/src/components/views/Catalog.jsx
index af75dba7..b65d4730 100755
--- a/src/components/views/Catalog.jsx
+++ b/src/components/views/Catalog.jsx
@@ -1148,6 +1148,9 @@ const Catalog = () => {
All
Subscriptions
+ {!defaultSubplebbits.some(subplebbit => subplebbit.address === selectedAddress) && (
+ {selectedAddress}
+ )}
{defaultSubplebbits.map(subplebbit => (
{subplebbit.title ? subplebbit.title : subplebbit.address}
diff --git a/src/components/views/Description.jsx b/src/components/views/Description.jsx
index c2af6868..19aa2605 100644
--- a/src/components/views/Description.jsx
+++ b/src/components/views/Description.jsx
@@ -28,7 +28,6 @@ const Description = () => {
isSettingsOpen, setIsSettingsOpen,
selectedAddress, setSelectedAddress,
selectedStyle,
- selectedThread,
setSelectedTitle,
} = useGeneralStore(state => state);
@@ -181,7 +180,7 @@ const Description = () => {
}>Create Board
]
[
- setIsSettingsOpen(true)}>Settings
+ setIsSettingsOpen(true)}>Settings
]
[
Home
@@ -195,6 +194,9 @@ const Description = () => {
All
Subscriptions
+ {!defaultSubplebbits.some(subplebbit => subplebbit.address === selectedAddress) && (
+ {selectedAddress}
+ )}
{defaultSubplebbits.map(subplebbit => (
{subplebbit.title ? subplebbit.title : subplebbit.address}
@@ -207,7 +209,7 @@ const Description = () => {
}>Create Board
- setIsSettingsOpen(true)}>Settings
+ setIsSettingsOpen(true)}>Settings
{handleStyleChange({target: {value: "Yotsuba"}}); window.scrollTo(0, 0);}}>Home
@@ -493,7 +495,7 @@ const Description = () => {
}>Create Board
]
[
- setIsSettingsOpen(true)}>Settings
+ setIsSettingsOpen(true)}>Settings
]
[
handleStyleChange({target: {value: "Yotsuba"}}
diff --git a/src/components/views/Home.jsx b/src/components/views/Home.jsx
index 246ad53a..1af4c395 100644
--- a/src/components/views/Home.jsx
+++ b/src/components/views/Home.jsx
@@ -108,8 +108,7 @@ const Home = () => {
-
Plebchan is a serverless, adminless, decentralized 4chan alternative that uses the plebbit protocol . Users do not need to register an account before participating in a community; anyone can post comments, share media links, and Plebchan is a serverless, adminless, decentralized 4chan alternative that uses the plebbit protocol . Users do not need to register an account before participating in a community; anyone can post comments, share media links, and {
window.electron && window.electron.isElectron ? (
setIsCreateBoardOpen(true)
@@ -119,7 +118,13 @@ const Home = () => {
)
)
}}
- >create a board . All the content is just text, and plebchan's speed depends on how many users are seeding . Search for any board address above, or feel free to click on a popular board below that interests you and jump right in!
+ >create a board. All the content is just text, and plebchan's speed depends on how many users are
{
+ alert(
+ 'You can seed automatically simply by using the plebchan desktop app, which is a plebbit full node. Download it here:\nhttps://github.com/plebbit/plebchan/releases/latest\n\nIf you are comfortable with the command line, you can use plebbit-cli with the --seed flag:\nhttps://github.com/plebbit/plebbit-cli\n\n'
+ )
+ }}
+ >seeding . Search for any board address above, or feel free to click on a popular board below that interests you and jump right in!
There are no global rules, each board is completely independent and its owner decides how/if it should be moderated.
diff --git a/src/components/views/Pending.jsx b/src/components/views/Pending.jsx
index 6bb2849b..a9a92310 100755
--- a/src/components/views/Pending.jsx
+++ b/src/components/views/Pending.jsx
@@ -177,6 +177,9 @@ const Pending = () => {
All
Subscriptions
+ {!defaultSubplebbits.some(subplebbit => subplebbit.address === selectedAddress) && (
+ {selectedAddress}
+ )}
{defaultSubplebbits.map(subplebbit => (
{subplebbit.title ? subplebbit.title : subplebbit.address}
diff --git a/src/components/views/Rules.jsx b/src/components/views/Rules.jsx
index 78a98873..ecdb6952 100644
--- a/src/components/views/Rules.jsx
+++ b/src/components/views/Rules.jsx
@@ -26,7 +26,6 @@ const Rules = () => {
isSettingsOpen, setIsSettingsOpen,
selectedAddress, setSelectedAddress,
selectedStyle,
- selectedThread,
setSelectedTitle,
} = useGeneralStore(state => state);
@@ -187,7 +186,7 @@ const Rules = () => {
}>Create Board
]
[
- setIsSettingsOpen(true)}>Settings
+ setIsSettingsOpen(true)}>Settings
]
[
Home
@@ -201,6 +200,9 @@ const Rules = () => {
All
Subscriptions
+ {!defaultSubplebbits.some(subplebbit => subplebbit.address === selectedAddress) && (
+ {selectedAddress}
+ )}
{defaultSubplebbits.map(subplebbit => (
{subplebbit.title ? subplebbit.title : subplebbit.address}
@@ -213,7 +215,7 @@ const Rules = () => {
}>Create Board
- setIsSettingsOpen(true)}>Settings
+ setIsSettingsOpen(true)}>Settings
{handleStyleChange({target: {value: "Yotsuba"}}); window.scrollTo(0, 0);}}>Home
@@ -491,7 +493,7 @@ const Rules = () => {
}>Create Board
]
[
- setIsSettingsOpen(true)}>Settings
+ setIsSettingsOpen(true)}>Settings
]
[
handleStyleChange({target: {value: "Yotsuba"}}
diff --git a/src/components/views/Subscriptions.jsx b/src/components/views/Subscriptions.jsx
index 40829f72..6e674085 100755
--- a/src/components/views/Subscriptions.jsx
+++ b/src/components/views/Subscriptions.jsx
@@ -30,6 +30,7 @@ import SettingsModal from '../modals/SettingsModal';
import findShortParentCid from '../../utils/findShortParentCid';
import getCommentMediaInfo from '../../utils/getCommentMediaInfo';
import getDate from '../../utils/getDate';
+import getFormattedTime from '../../utils/getFormattedTime';
import handleAddressClick from '../../utils/handleAddressClick';
import handleImageClick from '../../utils/handleImageClick';
import handleQuoteClick from '../../utils/handleQuoteClick';
@@ -862,7 +863,10 @@ const Subscriptions = () => {
{{({ shortAuthorAddress }) => (shortAuthorAddress)} }
)
- {getDate(thread.timestamp)}
+ {getDate(thread.timestamp)}
c/
@@ -1190,7 +1194,10 @@ const Subscriptions = () => {
- {getDate(reply.timestamp)}
+ {getDate(reply.timestamp)}
c/
@@ -1789,7 +1796,10 @@ const Subscriptions = () => {
tooltipPlace="top"
className="offline-mobile-sub" />
-
+
{getDate(thread.timestamp)}
c/
@@ -2027,7 +2037,10 @@ const Subscriptions = () => {
tooltipPlace="top"
className="offline-mobile-sub-reply" />
-
+
{getDate(reply.timestamp)}
c/
{reply.shortCid ? (
diff --git a/src/components/views/Thread.jsx b/src/components/views/Thread.jsx
index baf00ec2..541530f8 100755
--- a/src/components/views/Thread.jsx
+++ b/src/components/views/Thread.jsx
@@ -28,6 +28,7 @@ import SettingsModal from '../modals/SettingsModal';
import findShortParentCid from '../../utils/findShortParentCid';
import getCommentMediaInfo from '../../utils/getCommentMediaInfo';
import getDate from '../../utils/getDate';
+import getFormattedTime from '../../utils/getFormattedTime';
import handleAddressClick from '../../utils/handleAddressClick';
import handleImageClick from '../../utils/handleImageClick';
import handleQuoteClick from '../../utils/handleQuoteClick';
@@ -688,6 +689,9 @@ const Thread = () => {
All
Subscriptions
+ {!defaultSubplebbits.some(subplebbit => subplebbit.address === selectedAddress) && (
+ {selectedAddress}
+ )}
{defaultSubplebbits.map(subplebbit => (
{subplebbit.title ? subplebbit.title : subplebbit.address}
@@ -995,7 +999,12 @@ const Thread = () => {
)
- {getDate(comment?.timestamp)}
+
+ {getDate(comment?.timestamp)}
+
c/
@@ -1292,7 +1301,10 @@ const Thread = () => {
-
+
{getDate(reply?.timestamp)}
c/
@@ -1747,7 +1759,10 @@ const Thread = () => {
{comment.title}
) : null}
-
+
{getDate(comment?.timestamp)}
c/
@@ -1940,7 +1955,10 @@ const Thread = () => {
-
+
{getDate(reply?.timestamp)}
c/
{reply.shortCid ? (
diff --git a/src/utils/getFormattedTime.js b/src/utils/getFormattedTime.js
new file mode 100644
index 00000000..82545135
--- /dev/null
+++ b/src/utils/getFormattedTime.js
@@ -0,0 +1,39 @@
+const pluralize = (unit, value) => {
+ return `${value} ${unit}${value > 1 ? 's' : ''}`;
+};
+
+const getFormattedTime = (timestamp) => {
+ try {
+ const currentTime = new Date().getTime();
+ const differenceInMilliseconds = currentTime - (timestamp * 1000);
+
+ const years = Math.floor(differenceInMilliseconds / (1000 * 60 * 60 * 24 * 365.25));
+ const months = Math.floor((differenceInMilliseconds % (1000 * 60 * 60 * 24 * 365.25)) / (1000 * 60 * 60 * 24 * 30));
+ const days = Math.floor((differenceInMilliseconds % (1000 * 60 * 60 * 24 * 30)) / (1000 * 60 * 60 * 24));
+ const hours = Math.floor((differenceInMilliseconds % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
+ const minutes = Math.floor((differenceInMilliseconds % (1000 * 60 * 60)) / (1000 * 60));
+ const seconds = Math.floor((differenceInMilliseconds % (1000 * 60)) / 1000);
+
+ if (years > 0) {
+ return months > 0 ? `${pluralize('year', years)} and ${pluralize('month', months)} ago` : `${pluralize('year', years)} ago`;
+ } else if (months > 0) {
+ return `${pluralize('month', months)} and ${pluralize('day', days)} ago`;
+ } else if (days > 0) {
+ return `${pluralize('day', days)} and ${pluralize('hour', hours)} ago`;
+ } else if (hours > 0) {
+ return `${pluralize('hour', hours)} and ${pluralize('minute', minutes)} ago`;
+ } else if (minutes > 0) {
+ return `${pluralize('minute', minutes)} and ${pluralize('second', seconds)} ago`;
+ } else if (seconds > 30) {
+ return `${pluralize('second', seconds)} ago`;
+ } else {
+ return "just now";
+ }
+ }
+ catch (e) {
+ console.error("Error in getFormattedTime:", e);
+ return "[error]";
+ }
+};
+
+export default getFormattedTime;
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index ab7eeb01..62e757e4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -399,7 +399,7 @@
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703"
integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==
-"@babel/plugin-proposal-private-property-in-object@7.21.11":
+"@babel/plugin-proposal-private-property-in-object@^7.21.11":
version "7.21.11"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz#69d597086b6760c4126525cfa154f34631ff272c"
integrity sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==
@@ -12496,10 +12496,10 @@ react-tooltip@5.10.0:
"@floating-ui/dom" "1.2.3"
classnames "^2.3.2"
-react-virtuoso@4.3.2:
- version "4.3.2"
- resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.3.2.tgz#91d4926c67f635cf7efc2dea29e7cd6de83575d1"
- integrity sha512-n51V4fMk36VM6NapfaMbfs4w/IaVodp5asp+rtzlvz0yurG6uVrqA040ke43gpYtoOL6j6TxeXWo4b6GpO9N0A==
+react-virtuoso@4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.5.0.tgz#0bc043b4e3e928e7891aa541dd7e55d5b46db8c8"
+ integrity sha512-OMP6XrzJMMos1vbJZC16RxGW7utAxUMP7i5PNPi6epBNVH7nz+CF/DlmecNBep5wyjLud51dQ5epjb2A0w9W/Q==
react@18.2.0:
version "18.2.0"