feat(settings): add nsfw filters and filtering community by nsfw tag

This commit is contained in:
Tom (plebeius.eth)
2024-12-19 11:46:18 +01:00
parent 2829e50d06
commit 5a078e772d
8 changed files with 124 additions and 21 deletions

View File

@@ -177,6 +177,7 @@
unicode-bidi: isolate;
font-size: small;
margin-bottom: 10px;
display: inline-block;
}
.alwaysShowNsfwNotice p {

View File

@@ -5,6 +5,7 @@ import Embed from '../embed';
import { CommentMediaInfo } from '../../../lib/utils/media-utils';
import Markdown from '../../markdown';
import { useTranslation } from 'react-i18next';
import useFilterSettingsStore from '../../../stores/use-filter-settings-store';
interface ExpandoProps {
authorEditReason?: string;
@@ -36,8 +37,8 @@ const Expando = ({
toggleExpanded,
}: ExpandoProps) => {
const { t } = useTranslation();
const [hideContent, setHideContent] = useState(true);
const { blurNsfwThumbnails, setBlurNsfwThumbnails } = useFilterSettingsStore();
const [hideContent, setHideContent] = useState(blurNsfwThumbnails);
const [alwaysShowNsfw, setAlwaysShowNsfw] = useState(false);
useEffect(() => {
@@ -46,6 +47,12 @@ const Expando = ({
}
}, [expanded]);
const handleAlwaysShowNsfw = () => {
setBlurNsfwThumbnails(false);
setHideContent(false);
setAlwaysShowNsfw(true);
};
let mediaComponent = null;
if (commentMediaInfo?.type === 'image' || commentMediaInfo?.type === 'gif') {
@@ -64,13 +71,13 @@ const Expando = ({
<div className={expanded ? styles.expando : styles.expandoHidden}>
{link && !removed && commentMediaInfo?.type !== 'webpage' && (
<div className={styles.mediaPreview} onClick={() => setHideContent(false)}>
{(nsfw || spoiler) && hideContent && link && commentMediaInfo?.type !== 'webpage' && !(deleted || removed) && (
{((nsfw && blurNsfwThumbnails) || spoiler) && hideContent && link && commentMediaInfo?.type !== 'webpage' && !(deleted || removed) && (
<>
<div className={styles.blurContent} />
<span className={styles.unblurButton}>{nsfw && spoiler ? 'CLICK TO SEE NSFW SPOILER' : spoiler ? t('view_spoiler') : nsfw ? 'CLICK TO SEE NSFW' : ''}</span>
<span className={styles.unblurButton}>{nsfw && spoiler ? t('see_nsfw_spoiler') : spoiler ? t('view_spoiler') : nsfw ? t('see_nsfw') : ''}</span>
{nsfw && (
<span className={styles.alwaysShowNsfwButton} onClick={() => setAlwaysShowNsfw(!alwaysShowNsfw)}>
Always show NSFW media?
<span className={styles.alwaysShowNsfwButton} onClick={handleAlwaysShowNsfw}>
{t('always_show_nsfw')}
</span>
)}
</>
@@ -89,9 +96,11 @@ const Expando = ({
</div>
)}
{alwaysShowNsfw && (
<div className={styles.alwaysShowNsfwNotice}>
<p>Ok, we changed your preferences to always show NSFW media.</p>
<button onClick={() => setAlwaysShowNsfw(false)}>Undo</button>
<div className={styles.alwaysShowNsfwContainer}>
<div className={styles.alwaysShowNsfwNotice}>
<p>{t('always_show_nsfw_notice')}</p>
<button onClick={() => setAlwaysShowNsfw(false)}>{t('undo')}</button>
</div>
</div>
)}
{content && showContent && (

View File

@@ -96,7 +96,6 @@ const Post = ({ index, post = {} }: PostProps) => {
link,
linkHeight,
linkWidth,
nsfw,
pinned,
reason,
removed,
@@ -108,6 +107,7 @@ const Post = ({ index, post = {} }: PostProps) => {
title,
upvoteCount,
} = post || {};
const nsfw = true;
const { displayName, shortAddress } = author || {};
const { shortAuthorAddress, authorAddressChanged } = useAuthorAddress({ comment: post });

View File

@@ -2,6 +2,7 @@ import styles from './thumbnail.module.css';
import { Link } from 'react-router-dom';
import { CommentMediaInfo } from '../../../lib/utils/media-utils';
import useFetchGifFirstFrame from '../../../hooks/use-fetch-gif-first-frame';
import useFilterSettingsStore from '../../../stores/use-filter-settings-store';
interface ThumbnailProps {
cid?: string;
@@ -84,7 +85,9 @@ const Thumbnail = ({
mediaComponent = <span className={`${styles.iconThumbnail} ${styles.spoilerIcon}`} />;
}
if (isNsfw) {
const { blurNsfwThumbnails } = useFilterSettingsStore();
if (isNsfw && blurNsfwThumbnails) {
mediaComponent = <span className={`${styles.iconThumbnail} ${styles.nsfwIcon}`} />;
}

View File

@@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useAccount } from '@plebbit/plebbit-react-hooks';
import Plebbit from '@plebbit/plebbit-js/dist/browser/index.js';
import useFilterSettingsStore from '../stores/use-filter-settings-store';
interface Subplebbit {
title?: string;
@@ -28,10 +29,9 @@ export const categorizeSubplebbits = (subplebbits: Subplebbit[]) => {
return { plebbitSubs, interestsSubs, randomSubs, internationalSubs, projectsSubs };
};
const nsfwTags = ['gore', 'adult', 'anti'];
export const useDefaultSubplebbits = () => {
const [subplebbits, setSubplebbits] = useState<Subplebbit[]>([]);
const { hideAdultCommunities, hideGoreCommunities, hideAntiCommunities } = useFilterSettingsStore();
useEffect(() => {
if (cache) {
@@ -39,13 +39,16 @@ export const useDefaultSubplebbits = () => {
}
(async () => {
try {
const multisub = await fetch(
'https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/multisub.json',
// { cache: 'no-cache' }
).then((res) => res.json());
const multisub = await fetch('https://raw.githubusercontent.com/plebbit/temporary-default-subplebbits/master/multisub.json').then((res) => res.json());
const filteredSubplebbits = multisub.subplebbits.filter((subplebbit: Subplebbit) => {
return !subplebbit.tags?.some((tag) => nsfwTags.includes(tag));
const tags = subplebbit.tags || [];
if (hideAdultCommunities && tags.includes('adult')) return false;
if (hideGoreCommunities && tags.includes('gore')) return false;
if (hideAntiCommunities && tags.includes('anti')) return false;
return true;
});
cache = filteredSubplebbits;
@@ -54,7 +57,7 @@ export const useDefaultSubplebbits = () => {
console.warn(e);
}
})();
}, []);
}, [hideAdultCommunities, hideGoreCommunities, hideAntiCommunities]);
return cache || subplebbits;
};

View File

@@ -0,0 +1,33 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface FilterSettingsState {
blurNsfwThumbnails: boolean;
hideAdultCommunities: boolean;
hideGoreCommunities: boolean;
hideAntiCommunities: boolean;
setBlurNsfwThumbnails: (blur: boolean) => void;
setHideAdultCommunities: (hide: boolean) => void;
setHideGoreCommunities: (hide: boolean) => void;
setHideAntiCommunities: (hide: boolean) => void;
}
const useFilterSettingsStore = create<FilterSettingsState>()(
persist(
(set) => ({
blurNsfwThumbnails: true,
hideAdultCommunities: true,
hideGoreCommunities: true,
hideAntiCommunities: true,
setBlurNsfwThumbnails: (blur) => set({ blurNsfwThumbnails: blur }),
setHideAdultCommunities: (hide) => set({ hideAdultCommunities: hide }),
setHideGoreCommunities: (hide) => set({ hideGoreCommunities: hide }),
setHideAntiCommunities: (hide) => set({ hideAntiCommunities: hide }),
}),
{
name: 'filter-settings',
},
),
);
export default useFilterSettingsStore;

View File

@@ -104,3 +104,17 @@
.highlightedSetting {
background-color: var(--yellow-highlight);
}
.filterSettingTitle {
font-style: italic;
text-transform: capitalize;
}
.filters input[type='checkbox'] {
margin: 2px 0.5em 0 0;
}
.filters label {
margin-top: 5px;
margin-right: 5px;
}

View File

@@ -3,13 +3,14 @@ import { useLocation } from 'react-router-dom';
import { Trans, useTranslation } from 'react-i18next';
import { setAccount, useAccount } from '@plebbit/plebbit-react-hooks';
import { isSettingsPlebbitOptionsView } from '../../lib/utils/view-utils';
import styles from './settings.module.css';
import useFilterSettingsStore from '../../stores/use-filter-settings-store';
import useTheme from '../../hooks/use-theme';
import AccountSettings from './account-settings';
import AddressSettings from './address-settings';
import AvatarSettings from './avatar-settings';
import PlebbitOptions from './plebbit-options';
import WalletSettings from './wallet-settings';
import useTheme from '../../hooks/use-theme';
import styles from './settings.module.css';
import packageJson from '../../../package.json';
import _ from 'lodash';
@@ -111,6 +112,39 @@ const ThemeSettings = () => {
);
};
const FiltersSettings = () => {
const { t } = useTranslation();
const {
blurNsfwThumbnails,
hideAdultCommunities,
hideGoreCommunities,
hideAntiCommunities,
setBlurNsfwThumbnails,
setHideAdultCommunities,
setHideGoreCommunities,
setHideAntiCommunities,
} = useFilterSettingsStore();
return (
<div className={styles.filters}>
<div className={styles.filterSettingTitle}>{t('nsfw_content')}</div>
<input type='checkbox' id='blurNsfwThumbnails' checked={blurNsfwThumbnails} onChange={(e) => setBlurNsfwThumbnails(e.target.checked)} />
<label htmlFor='blurNsfwThumbnails'>{t('blur_media')}</label>
<br />
<br />
<div className={styles.filterSettingTitle}>{t('nsfw_communities')}</div>
<input type='checkbox' id='hideAdultCommunities' checked={hideAdultCommunities} onChange={(e) => setHideAdultCommunities(e.target.checked)} />
<label htmlFor='hideAdultCommunities'>{t('hide_adult')}</label>
<br />
<input type='checkbox' id='hideGoreCommunities' checked={hideGoreCommunities} onChange={(e) => setHideGoreCommunities(e.target.checked)} />
<label htmlFor='hideGoreCommunities'>{t('hide_gore')}</label>
<br />
<input type='checkbox' id='hideAntiCommunities' checked={hideAntiCommunities} onChange={(e) => setHideAntiCommunities(e.target.checked)} />
<label htmlFor='hideAntiCommunities'>{t('hide_anti')}</label>
</div>
);
};
const DisplayNameSetting = () => {
const { t } = useTranslation();
const account = useAccount();
@@ -192,6 +226,12 @@ const GeneralSettings = () => {
<ThemeSettings />
</span>
</div>
<div className={styles.category}>
<span className={styles.categoryTitle}>{t('filters')}</span>
<span className={styles.categorySettings}>
<FiltersSettings />
</span>
</div>
<div className={styles.category}>
<span className={styles.categoryTitle}>{t('avatar')}</span>
<span className={styles.categorySettings}>