diff --git a/src/app.tsx b/src/app.tsx
index a22a7ab1..a8bdb425 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
-import { Outlet, Route, Routes, useLocation, useParams } from 'react-router-dom';
-import { useAccountComment } from '@plebbit/plebbit-react-hooks';
+import { Navigate, Outlet, Route, Routes, useLocation, useParams } from 'react-router-dom';
+import { useAccount, useAccountComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
+import useAccountsStore from '@plebbit/plebbit-react-hooks/dist/stores/accounts';
import { initSnow, removeSnow } from './lib/snow';
import { isAllView, isModView, isSubscriptionsView } from './lib/utils/view-utils';
import { preloadThemeAssets } from './lib/utils/preload-utils';
@@ -10,12 +11,14 @@ import useSpecialThemeStore from './stores/use-special-theme-store';
import useIsMobile from './hooks/use-is-mobile';
import useTheme from './hooks/use-theme';
import { useDirectories } from './hooks/use-directories';
+import { useResolvedSubplebbitAddress } from './hooks/use-resolved-subplebbit-address';
import { getSubplebbitAddress, isPostRoute, isPendingPostRoute, isModQueueRoute } from './lib/utils/route-utils';
import styles from './app.module.css';
import FAQ from './views/faq';
import Home from './views/home';
import Rules from './views/rules';
import NotFound from './views/not-found';
+import NotAllowed from './views/not-allowed';
import PendingPost from './views/pending-post';
import Post from './views/post';
import ModQueueView from './views/mod-queue';
@@ -37,6 +40,8 @@ import SettingsModal from './components/settings-modal';
// to prevent visible loading delays when switching themes
preloadThemeAssets();
+const hasModQueueAccessRole = (role?: string): boolean => role === 'admin' || role === 'owner' || role === 'moderator';
+
const BoardLayout = () => {
const { accountCommentIndex, boardIdentifier } = useParams();
const location = useLocation();
@@ -147,6 +152,49 @@ const GlobalLayout = () => {
);
};
+const ModQueueRoute = () => {
+ const { boardIdentifier } = useParams();
+ const account = useAccount();
+ const accountAddress = account?.author?.address;
+ const subplebbitAddress = useResolvedSubplebbitAddress();
+ const subplebbit = useSubplebbit({ subplebbitAddress });
+
+ const accountSubplebbitAddresses = useAccountsStore(
+ (state) => {
+ const activeAccountId = state.activeAccountId;
+ const activeAccount = activeAccountId ? state.accounts[activeAccountId] : undefined;
+ const accountSubplebbits = activeAccount?.subplebbits || {};
+ return Object.keys(accountSubplebbits);
+ },
+ (prev, next) => {
+ if (prev.length !== next.length) return false;
+ return prev.every((val, idx) => val === next[idx]);
+ },
+ );
+
+ if (!account) {
+ return null;
+ }
+
+ if (!accountAddress) {
+ return ;
+ }
+
+ if (!boardIdentifier) {
+ return accountSubplebbitAddresses.length > 0 ? : ;
+ }
+
+ // Wait for board role metadata before enforcing access to avoid false redirects during initial load.
+ const boardState = subplebbit?.state;
+ const isBoardLoading = !subplebbit || !boardState || (boardState !== 'succeeded' && boardState !== 'failed');
+ if (isBoardLoading) {
+ return null;
+ }
+
+ const accountRole = subplebbit?.roles?.[accountAddress]?.role;
+ return hasModQueueAccessRole(accountRole) ? : ;
+};
+
const App = () => (
@@ -170,16 +218,16 @@ const App = () => (
- } />
- } />
+ } />
+ } />
- } />
- } />
+ } />
+ } />
} />
} />
@@ -187,6 +235,7 @@ const App = () => (
} />
} />
+ } />
} />
} />
diff --git a/src/components/not-found-image/index.ts b/src/components/not-found-image/index.ts
new file mode 100644
index 00000000..98637f42
--- /dev/null
+++ b/src/components/not-found-image/index.ts
@@ -0,0 +1 @@
+export { default } from './not-found-image';
diff --git a/src/components/not-found-image/not-found-image.tsx b/src/components/not-found-image/not-found-image.tsx
new file mode 100644
index 00000000..05c5f46a
--- /dev/null
+++ b/src/components/not-found-image/not-found-image.tsx
@@ -0,0 +1,10 @@
+import { useState } from 'react';
+import { NOT_FOUND_IMAGES } from '../../generated/asset-manifest';
+
+const NotFoundImage = () => {
+ const [imagePath] = useState(() => NOT_FOUND_IMAGES[Math.floor(Math.random() * NOT_FOUND_IMAGES.length)]);
+
+ return
;
+};
+
+export default NotFoundImage;
diff --git a/src/views/not-allowed/index.ts b/src/views/not-allowed/index.ts
new file mode 100644
index 00000000..4b780e62
--- /dev/null
+++ b/src/views/not-allowed/index.ts
@@ -0,0 +1 @@
+export { default } from './not-allowed';
diff --git a/src/views/not-allowed/not-allowed.tsx b/src/views/not-allowed/not-allowed.tsx
new file mode 100644
index 00000000..032a9f22
--- /dev/null
+++ b/src/views/not-allowed/not-allowed.tsx
@@ -0,0 +1,30 @@
+import { Link } from 'react-router-dom';
+import { HomeLogo } from '../home';
+import NotFoundImage from '../../components/not-found-image';
+import styles from '../not-found/not-found.module.css';
+
+const NotAllowed = () => {
+ return (
+
+
+
+
+
+
+
Not Allowed
+
+
+
+
+
+ [Back to home]
+
+
+
+
+
+
+ );
+};
+
+export default NotAllowed;
diff --git a/src/views/not-found/not-found.tsx b/src/views/not-found/not-found.tsx
index b2a471eb..f5a50173 100644
--- a/src/views/not-found/not-found.tsx
+++ b/src/views/not-found/not-found.tsx
@@ -1,17 +1,10 @@
-import { useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { useSubplebbitField } from '../../hooks/use-stable-subplebbit';
import { useDirectories } from '../../hooks/use-directories';
import { getSubplebbitAddress } from '../../lib/utils/route-utils';
import { HomeLogo } from '../home';
+import NotFoundImage from '../../components/not-found-image';
import styles from './not-found.module.css';
-import { NOT_FOUND_IMAGES } from '../../generated/asset-manifest';
-
-const NotFoundImage = () => {
- const [imagePath] = useState(() => NOT_FOUND_IMAGES[Math.floor(Math.random() * NOT_FOUND_IMAGES.length)]);
-
- return
;
-};
const NotFound = () => {
const location = useLocation();
@@ -31,7 +24,7 @@ const NotFound = () => {
-
404 Not Found
+ Not Found