mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
+18
-9
@@ -1,4 +1,4 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Outlet, Route, Routes, useLocation, useParams } from 'react-router-dom';
|
||||
import { isAllView, isSubscriptionsView } from './lib/utils/view-utils';
|
||||
import useIsMobile from './hooks/use-is-mobile';
|
||||
@@ -18,7 +18,6 @@ import SubplebbitStats from './components/subplebbit-stats';
|
||||
import TopBar from './components/topbar';
|
||||
import { timeFilterNames } from './hooks/use-time-filter';
|
||||
import useTheme from './hooks/use-theme';
|
||||
import useInitialTheme from './hooks/use-initial-theme';
|
||||
|
||||
const BoardLayout = () => {
|
||||
const { accountCommentIndex, subplebbitAddress, timeFilterName } = useParams();
|
||||
@@ -62,7 +61,23 @@ const BoardLayout = () => {
|
||||
};
|
||||
|
||||
const GlobalLayout = () => {
|
||||
useTheme();
|
||||
const [theme, setTheme] = useState('');
|
||||
const [currentTheme] = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
if (currentTheme !== theme) {
|
||||
setTheme(currentTheme);
|
||||
}
|
||||
}, [currentTheme, theme]);
|
||||
|
||||
useEffect(() => {
|
||||
if (theme) {
|
||||
document.body.classList.add(theme);
|
||||
return () => {
|
||||
document.body.classList.remove(theme);
|
||||
};
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -73,12 +88,6 @@ const GlobalLayout = () => {
|
||||
};
|
||||
|
||||
const App = () => {
|
||||
const initialTheme = useInitialTheme();
|
||||
|
||||
useEffect(() => {
|
||||
document.body.classList.add(initialTheme);
|
||||
}, [initialTheme]);
|
||||
|
||||
return (
|
||||
<div className={styles.app}>
|
||||
<Routes>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDis
|
||||
import { Comment, PublishCommentEditOptions, usePublishCommentEdit } from '@plebbit/plebbit-react-hooks';
|
||||
import styles from './edit-menu.module.css';
|
||||
import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils';
|
||||
import { formatMarkdown } from '../../lib/utils/post-utils';
|
||||
import useChallengesStore from '../../stores/use-challenges-store';
|
||||
import _ from 'lodash';
|
||||
import useIsMobile from '../../hooks/use-is-mobile';
|
||||
@@ -113,6 +114,11 @@ const EditMenu = ({ isAccountMod, isAccountCommentAuthor, isCommentAuthorMod, po
|
||||
setIsEditMenuOpen(false);
|
||||
};
|
||||
|
||||
const handleContentChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const formattedContent = formatMarkdown(e.target.value);
|
||||
setPublishCommentEditOptions((state) => ({ ...state, content: formattedContent }));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className={`${styles.checkbox} ${isReply && styles.replyCheckbox}`} ref={refs.setReference} {...(cid && getReferenceProps())}>
|
||||
@@ -140,12 +146,7 @@ const EditMenu = ({ isAccountMod, isAccountCommentAuthor, isCommentAuthorMod, po
|
||||
</div>
|
||||
{isContentEditorOpen && (
|
||||
<div>
|
||||
<textarea
|
||||
className={styles.editTextarea}
|
||||
value={publishCommentEditOptions.content ?? ''}
|
||||
onChange={(e) => setPublishCommentEditOptions((state) => ({ ...state, content: e.target.value }))}
|
||||
autoFocus={true}
|
||||
/>
|
||||
<textarea className={styles.editTextarea} defaultValue={publishCommentEditOptions.content ?? ''} onChange={handleContentChange} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -168,12 +168,7 @@ const Markdown = ({ content, title }: MarkdownProps) => {
|
||||
video: ({ src }) => <span>{src}</span>,
|
||||
iframe: ({ src }) => <span>{src}</span>,
|
||||
source: ({ src }) => <span>{src}</span>,
|
||||
hr: () =>
|
||||
!isInCatalogView && (
|
||||
<div className={styles.hrWrapper}>
|
||||
<hr />
|
||||
</div>
|
||||
),
|
||||
hr: () => null,
|
||||
a: ({ href, children }) => {
|
||||
if (href && !isInCatalogView) {
|
||||
const linkMediaInfo = getLinkMediaInfo(href);
|
||||
|
||||
@@ -262,7 +262,7 @@ const PostMedia = ({ post }: PostProps) => {
|
||||
|
||||
const PostMessage = ({ post }: PostProps) => {
|
||||
const { cid, content, deleted, edit, original, parentCid, postCid, reason, removed, state } = post || {};
|
||||
// TODO: commentAuthor is not available outside of editedComment, update when available
|
||||
// TODO: commentAuthor is not yet available outside of editedComment, wait for API to be updated
|
||||
// const banned = !!post?.commentAuthor?.banExpiresAt;
|
||||
const { t } = useTranslation();
|
||||
const params = useParams();
|
||||
@@ -287,17 +287,25 @@ const PostMessage = ({ post }: PostProps) => {
|
||||
<blockquote className={styles.postMessage}>
|
||||
{isReply && !(removed || deleted) && state !== 'failed' && isReplyingToReply && <ReplyQuotePreview isQuotelinkReply={true} quotelinkReply={quotelinkReply} />}
|
||||
{removed ? (
|
||||
<Tooltip
|
||||
children={<span className={styles.removedContent}>({t('this_post_was_removed')})</span>}
|
||||
content={`${_.capitalize(t('reason'))}: "${reason}"`}
|
||||
showTooltip={!!reason}
|
||||
/>
|
||||
reason ? (
|
||||
<>
|
||||
<span className={styles.redEditMessage}>({t('this_post_was_removed')})</span>
|
||||
<br />
|
||||
<br />
|
||||
<span className={styles.grayEditMessage}>{`${_.capitalize(t('reason'))}: "${reason}"`}.</span>
|
||||
</>
|
||||
) : (
|
||||
<span className={styles.grayEditMessage}>{_.capitalize(t('this_post_was_removed'))}.</span>
|
||||
)
|
||||
) : deleted ? (
|
||||
<Tooltip
|
||||
children={<span className={styles.deletedContent}>{t('user_deleted_this_post')}</span>}
|
||||
content={reason && `${t('reason')}: ${reason}`}
|
||||
showTooltip={!!reason}
|
||||
/>
|
||||
reason ? (
|
||||
<>
|
||||
<span className={styles.grayEditMessage}>{t('user_deleted_this_post')}</span>{' '}
|
||||
<span className={styles.grayEditMessage}>{`${_.capitalize(t('reason'))}: "${reason}"`}.</span>
|
||||
</>
|
||||
) : (
|
||||
<span className={styles.grayEditMessage}>{t('user_deleted_this_post')}</span>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
{!showOriginal && <Markdown content={displayContent} />}
|
||||
@@ -329,7 +337,7 @@ const PostMessage = ({ post }: PostProps) => {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{/* TODO: commentAuthor is not available outside of editedComment, update when available */}
|
||||
{/* TODO: commentAuthor is not yet available outside of editedComment, wait for API to be updated */}
|
||||
{/* {banned && (
|
||||
<span className={styles.removedContent}>
|
||||
<br />
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
import { create } from 'zustand';
|
||||
import { alertChallengeVerificationFailed } from '../../lib/utils/challenge-utils';
|
||||
import { getLinkMediaInfo } from '../../lib/utils/media-utils';
|
||||
import { formatMarkdown } from '../../lib/utils/post-utils';
|
||||
import { isValidURL } from '../../lib/utils/url-utils';
|
||||
import { isAllView, isDescriptionView, isPostPageView, isRulesView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
|
||||
@@ -164,7 +165,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
signer: newSigner,
|
||||
author: {
|
||||
address: newSigner.address,
|
||||
displayName: account?.author?.displayName,
|
||||
displayName: displayName || undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -173,11 +174,11 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
signer: undefined,
|
||||
author: {
|
||||
address: account?.author?.address,
|
||||
displayName: account?.author?.displayName,
|
||||
displayName: displayName || undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [anonMode, getNewSigner, account, setSubmitStore]);
|
||||
}, [anonMode, getNewSigner, account, setSubmitStore, displayName]);
|
||||
|
||||
const onPublishPost = async () => {
|
||||
if (!title && !content && !link) {
|
||||
@@ -189,16 +190,6 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!anonMode) {
|
||||
setSubmitStore({
|
||||
signer: undefined,
|
||||
author: {
|
||||
address: account?.author?.address,
|
||||
displayName: account?.author?.displayName,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
publishComment();
|
||||
};
|
||||
|
||||
@@ -235,6 +226,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
signer: existingSigner,
|
||||
author: {
|
||||
address: existingSigner.address,
|
||||
displayName: displayName || undefined,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
@@ -243,11 +235,17 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
signer: newSigner,
|
||||
author: {
|
||||
address: newSigner.address,
|
||||
displayName: displayName || undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [address, getExistingSigner, getNewSigner, setPublishReplyOptions, anonMode]);
|
||||
}, [address, getExistingSigner, getNewSigner, setPublishReplyOptions, anonMode, displayName]);
|
||||
|
||||
const handleContentChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const formattedContent = formatMarkdown(e.target.value);
|
||||
isInPostView ? setPublishReplyOptions({ content: formattedContent }) : setSubmitStore({ content: formattedContent });
|
||||
};
|
||||
|
||||
const onPublishReply = () => {
|
||||
const currentContent = textRef.current?.value || '';
|
||||
@@ -297,9 +295,9 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
onChange={(e) => {
|
||||
setAccount({ ...account, author: { ...account?.author, displayName: e.target.value } });
|
||||
if (isInPostView) {
|
||||
setPublishReplyOptions({ displayName: e.target.value });
|
||||
setPublishReplyOptions({ displayName: e.target.value || undefined });
|
||||
} else {
|
||||
setSubmitStore({ displayName: e.target.value });
|
||||
setSubmitStore({ displayName: e.target.value || undefined });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -324,16 +322,7 @@ const PostFormTable = ({ closeForm, postCid }: { closeForm: () => void; postCid:
|
||||
<tr>
|
||||
<td>{t('comment')}</td>
|
||||
<td>
|
||||
<textarea
|
||||
cols={48}
|
||||
rows={4}
|
||||
wrap='soft'
|
||||
ref={textRef}
|
||||
onChange={(e) => {
|
||||
const content = e.target.value.replace(/\n/g, '\n\n');
|
||||
isInPostView ? setPublishReplyOptions({ content }) : setSubmitStore({ content });
|
||||
}}
|
||||
/>
|
||||
<textarea cols={48} rows={4} wrap='soft' ref={textRef} onChange={handleContentChange} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -210,17 +210,25 @@ const PostMessageMobile = ({ post }: PostProps) => {
|
||||
<blockquote className={`${styles.postMessage} ${!isReply && styles.clampLines}`}>
|
||||
{isReply && !(removed || deleted) && state !== 'failed' && isReplyingToReply && <ReplyQuotePreview isQuotelinkReply={true} quotelinkReply={quotelinkReply} />}
|
||||
{removed ? (
|
||||
<Tooltip
|
||||
children={<span className={styles.removedContent}>({t('this_post_was_removed')})</span>}
|
||||
content={`${_.capitalize(t('reason'))}: "${reason}"`}
|
||||
showTooltip={!!reason}
|
||||
/>
|
||||
reason ? (
|
||||
<>
|
||||
<span className={styles.redEditMessage}>({t('this_post_was_removed')})</span>
|
||||
<br />
|
||||
<br />
|
||||
<span className={styles.grayEditMessage}>{`${_.capitalize(t('reason'))}: "${reason}"`}.</span>
|
||||
</>
|
||||
) : (
|
||||
<span className={styles.grayEditMessage}>{_.capitalize(t('this_post_was_removed'))}.</span>
|
||||
)
|
||||
) : deleted ? (
|
||||
<Tooltip
|
||||
children={<span className={styles.deletedContent}>{t('user_deleted_this_post')}</span>}
|
||||
content={reason && `${t('reason')}: ${reason}`}
|
||||
showTooltip={!!reason}
|
||||
/>
|
||||
reason ? (
|
||||
<>
|
||||
<span className={styles.grayEditMessage}>{t('user_deleted_this_post')}</span>{' '}
|
||||
<span className={styles.grayEditMessage}>{`${_.capitalize(t('reason'))}: "${reason}"`}.</span>
|
||||
</>
|
||||
) : (
|
||||
<span className={styles.grayEditMessage}>{t('user_deleted_this_post')}</span>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
{!showOriginal && <Markdown content={displayContent} />}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import Draggable from 'react-draggable';
|
||||
import { setAccount, useAccount, useComment, useSubplebbit } from '@plebbit/plebbit-react-hooks';
|
||||
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
|
||||
import { formatMarkdown } from '../../lib/utils/post-utils';
|
||||
import { getFormattedTimeAgo } from '../../lib/utils/time-utils';
|
||||
import { isValidURL } from '../../lib/utils/url-utils';
|
||||
import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
|
||||
@@ -154,9 +155,10 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, postCid, scrollY, s
|
||||
};
|
||||
|
||||
const handleContentChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const contentWithoutPrefix = e.target.value.slice(contentPrefix.length).replace(/\n/g, '\n\n');
|
||||
if (textRef.current && textRef.current.value !== contentWithoutPrefix) {
|
||||
setPublishReplyOptions({ content: contentWithoutPrefix });
|
||||
const contentWithoutPrefix = e.target.value.slice(contentPrefix.length);
|
||||
const formattedContent = formatMarkdown(contentWithoutPrefix);
|
||||
if (textRef.current && textRef.current.value !== formattedContent) {
|
||||
setPublishReplyOptions({ content: formattedContent });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import useThemeStore from '../stores/use-theme-store';
|
||||
import useDefaultSubplebbits from './use-default-subplebbits';
|
||||
@@ -17,20 +18,28 @@ const useInitialTheme = () => {
|
||||
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
|
||||
const isInPendingPostView = isPendingPostView(location.pathname, params);
|
||||
|
||||
if (isInPendingPostView) {
|
||||
return currentTheme || 'yotsuba';
|
||||
} else if (isInAllView || isInSubscriptionsView) {
|
||||
return getTheme('sfw') || 'yotsuba-b';
|
||||
} else if (isInHomeView || isInNotFoundView) {
|
||||
return 'yotsuba';
|
||||
} else if (subplebbitAddress) {
|
||||
const subplebbit = subplebbits.find((s) => s.address === subplebbitAddress);
|
||||
if (subplebbit && subplebbit.tags && subplebbit.tags.some((tag) => nsfwTags.includes(tag))) {
|
||||
return getTheme('nsfw') || 'yotsuba';
|
||||
const initialTheme = useMemo(() => {
|
||||
let theme = 'yotsuba';
|
||||
|
||||
if (isInPendingPostView) {
|
||||
theme = currentTheme || 'yotsuba';
|
||||
} else if (isInAllView || isInSubscriptionsView) {
|
||||
theme = getTheme('sfw', false) || 'yotsuba-b'; // Add 'false' parameter
|
||||
} else if (isInHomeView || isInNotFoundView) {
|
||||
theme = 'yotsuba';
|
||||
} else if (subplebbitAddress) {
|
||||
const subplebbit = subplebbits.find((s) => s.address === subplebbitAddress);
|
||||
if (subplebbit && subplebbit.tags && subplebbit.tags.some((tag) => nsfwTags.includes(tag))) {
|
||||
theme = getTheme('nsfw', false) || 'yotsuba'; // Add 'false' parameter
|
||||
} else {
|
||||
theme = getTheme('sfw', false) || 'yotsuba-b'; // Add 'false' parameter
|
||||
}
|
||||
}
|
||||
return getTheme('sfw') || 'yotsuba-b';
|
||||
}
|
||||
return 'yotsuba';
|
||||
|
||||
return theme;
|
||||
}, [isInPendingPostView, isInAllView, isInSubscriptionsView, isInHomeView, isInNotFoundView, subplebbitAddress, getTheme, currentTheme, subplebbits]);
|
||||
|
||||
return initialTheme;
|
||||
};
|
||||
|
||||
export default useInitialTheme;
|
||||
|
||||
@@ -62,8 +62,6 @@ const useReplyStore = create<ReplyState>((set) => ({
|
||||
},
|
||||
};
|
||||
|
||||
console.log('Final publishCommentOptions:', publishCommentOptions);
|
||||
|
||||
return {
|
||||
author: { ...state.author, [parentCid]: updatedAuthor },
|
||||
displayName: { ...state.displayName, [parentCid]: displayName },
|
||||
@@ -117,8 +115,6 @@ const useReply = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddress:
|
||||
signer: anonMode ? signer || options.signer : undefined,
|
||||
};
|
||||
|
||||
console.log('Final options to set in state:', newOptions);
|
||||
|
||||
setReplyStore(newOptions as SetReplyStoreData);
|
||||
},
|
||||
[subplebbitAddress, parentCid, author, displayName, signer, content, link, spoiler, setReplyStore, anonMode],
|
||||
@@ -126,8 +122,6 @@ const useReply = ({ cid, subplebbitAddress }: { cid: string; subplebbitAddress:
|
||||
|
||||
const resetPublishReplyOptions = useCallback(() => resetReplyStore(parentCid), [parentCid, resetReplyStore]);
|
||||
|
||||
console.log('Final options before publishing', publishCommentOptions);
|
||||
|
||||
const { index, publishComment } = usePublishComment(publishCommentOptions);
|
||||
|
||||
return { setPublishReplyOptions, resetPublishReplyOptions, replyIndex: index, publishReply: publishComment, setReplyStore };
|
||||
|
||||
+39
-38
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { isAllView, isSubscriptionsView } from '../lib/utils/view-utils';
|
||||
import useThemeStore from '../stores/use-theme-store';
|
||||
@@ -24,63 +24,64 @@ const useTheme = (): [string, (theme: string) => void] => {
|
||||
const subplebbits = useDefaultSubplebbits();
|
||||
|
||||
const initialTheme = useInitialTheme();
|
||||
const [theme, setLocalTheme] = useState<string>(() => initialTheme);
|
||||
const [themesLoaded, setThemesLoaded] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const loadAndApplyThemes = async () => {
|
||||
await loadThemes();
|
||||
setThemesLoaded(true);
|
||||
};
|
||||
|
||||
loadAndApplyThemes();
|
||||
}, [loadThemes]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!themesLoaded) return;
|
||||
const [currentTheme, setCurrentTheme] = useState(initialTheme);
|
||||
|
||||
const getCurrentTheme = useCallback(() => {
|
||||
const subplebbitAddress = params?.subplebbitAddress;
|
||||
const isInAllView = isAllView(location.pathname, params);
|
||||
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
|
||||
|
||||
let storedTheme = null;
|
||||
if (isInAllView || isInSubscriptionsView) {
|
||||
storedTheme = getTheme('sfw');
|
||||
storedTheme = getTheme('sfw', false);
|
||||
} else if (subplebbitAddress) {
|
||||
const subplebbit = subplebbits.find((s) => s.address === subplebbitAddress);
|
||||
if (subplebbit && subplebbit.tags && subplebbit.tags.some((tag) => nsfwTags.includes(tag))) {
|
||||
storedTheme = getTheme('nsfw');
|
||||
storedTheme = getTheme('nsfw', false);
|
||||
} else {
|
||||
storedTheme = getTheme('sfw');
|
||||
storedTheme = getTheme('sfw', false);
|
||||
}
|
||||
}
|
||||
|
||||
const themeToSet = storedTheme || initialTheme;
|
||||
setLocalTheme(themeToSet);
|
||||
updateThemeClass(themeToSet);
|
||||
}, [initialTheme, location.pathname, params, getTheme, themesLoaded, subplebbits]);
|
||||
return storedTheme || initialTheme;
|
||||
}, [location.pathname, params, getTheme, subplebbits, initialTheme]);
|
||||
|
||||
const setSubplebbitTheme = async (newTheme: string) => {
|
||||
const subplebbitAddress = params?.subplebbitAddress;
|
||||
const isInAllView = isAllView(location.pathname, params);
|
||||
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
|
||||
useEffect(() => {
|
||||
const newTheme = getCurrentTheme();
|
||||
if (newTheme !== currentTheme) {
|
||||
setCurrentTheme(newTheme);
|
||||
updateThemeClass(newTheme);
|
||||
}
|
||||
}, [getCurrentTheme, currentTheme]);
|
||||
|
||||
if (isInAllView || isInSubscriptionsView) {
|
||||
await setThemeStore('sfw', newTheme);
|
||||
} else if (subplebbitAddress) {
|
||||
const subplebbit = subplebbits.find((s) => s.address === subplebbitAddress);
|
||||
if (subplebbit && subplebbit.tags && subplebbit.tags.some((tag) => nsfwTags.includes(tag))) {
|
||||
await setThemeStore('nsfw', newTheme);
|
||||
} else {
|
||||
useEffect(() => {
|
||||
loadThemes();
|
||||
}, [loadThemes]);
|
||||
|
||||
const setSubplebbitTheme = useCallback(
|
||||
async (newTheme: string) => {
|
||||
const subplebbitAddress = params?.subplebbitAddress;
|
||||
const isInAllView = isAllView(location.pathname, params);
|
||||
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
|
||||
|
||||
if (isInAllView || isInSubscriptionsView) {
|
||||
await setThemeStore('sfw', newTheme);
|
||||
} else if (subplebbitAddress) {
|
||||
const subplebbit = subplebbits.find((s) => s.address === subplebbitAddress);
|
||||
if (subplebbit && subplebbit.tags && subplebbit.tags.some((tag) => nsfwTags.includes(tag))) {
|
||||
await setThemeStore('nsfw', newTheme);
|
||||
} else {
|
||||
await setThemeStore('sfw', newTheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setLocalTheme(newTheme);
|
||||
updateThemeClass(newTheme);
|
||||
};
|
||||
setCurrentTheme(newTheme);
|
||||
updateThemeClass(newTheme);
|
||||
},
|
||||
[location.pathname, params, setThemeStore, subplebbits],
|
||||
);
|
||||
|
||||
return [theme, setSubplebbitTheme];
|
||||
return [currentTheme, setSubplebbitTheme];
|
||||
};
|
||||
|
||||
export default useTheme;
|
||||
|
||||
@@ -30,6 +30,10 @@ hr {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.red {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
@@ -20,3 +20,14 @@ export function getTextColorForBackground(rgb: string): string {
|
||||
const brightness = r * 0.299 + g * 0.587 + b * 0.114;
|
||||
return brightness > 125 ? 'black' : 'white';
|
||||
}
|
||||
|
||||
export const formatMarkdown = (content: string): string => {
|
||||
let md = content;
|
||||
if (md) {
|
||||
// Replace single newline with "/n /n" if followed by a newline
|
||||
md = md.replace(/\n(?=\n)/g, '\n \n');
|
||||
// Replace single newline with double newline if between two characters
|
||||
md = md.replace(/\n(?=\S)/g, '\n\n');
|
||||
}
|
||||
return md;
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ interface ThemeState {
|
||||
};
|
||||
currentTheme: string | null;
|
||||
setTheme: (category: keyof ThemeState['themes'], theme: string) => void;
|
||||
getTheme: (category: keyof ThemeState['themes']) => string | null;
|
||||
getTheme: (category: keyof ThemeState['themes'], updateCurrentTheme?: boolean) => string | null;
|
||||
loadThemes: () => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -29,10 +29,12 @@ const useThemeStore = create<ThemeState>((set: StoreApi<ThemeState>['setState'],
|
||||
await themeStore.setItem(category, theme);
|
||||
set({ themes: updatedThemes, currentTheme: theme });
|
||||
},
|
||||
getTheme: (category) => {
|
||||
getTheme: (category, updateCurrentTheme = true) => {
|
||||
const currentThemes = get().themes;
|
||||
const theme = currentThemes[category] || null;
|
||||
set({ currentTheme: theme });
|
||||
if (updateCurrentTheme) {
|
||||
set({ currentTheme: theme });
|
||||
}
|
||||
return theme;
|
||||
},
|
||||
loadThemes: async () => {
|
||||
|
||||
@@ -441,13 +441,14 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.removedContent, .deletedContent {
|
||||
.redEditMessage {
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.removedContent {
|
||||
text-transform: uppercase;
|
||||
.grayEditMessage {
|
||||
color: var(--post-mobile-abbr-text-color);
|
||||
}
|
||||
|
||||
.backlink, .backlinkHash {
|
||||
|
||||
Reference in New Issue
Block a user