diff --git a/public/images/bh2024nostriga.jpg b/public/images/bh2024nostriga.jpg new file mode 100644 index 0000000..e863899 Binary files /dev/null and b/public/images/bh2024nostriga.jpg differ diff --git a/public/images/nostriga.jpg b/public/images/nostriga.jpg deleted file mode 100644 index b84ad5d..0000000 Binary files a/public/images/nostriga.jpg and /dev/null differ diff --git a/src/components/GridView/index.tsx b/src/components/GridView/index.tsx index 5b921fe..c2ebc39 100644 --- a/src/components/GridView/index.tsx +++ b/src/components/GridView/index.tsx @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import { NostrImage, urlFix } from '../nostrImageDownload'; import './GridView.css'; import GridImage from './GridImage'; -import { Settings } from '../../utils/useNav'; +import useNav, { Settings } from '../../utils/useNav'; import AuthorProfile from '../AuthorProfile/AuthorProfile'; import { useSwipeable } from 'react-swipeable'; import { Helmet } from 'react-helmet'; @@ -26,6 +26,7 @@ const GridView = ({ settings, images, currentImage, setCurrentImage, setViewMode const showNextImage = () => { setCurrentImage(idx => (idx !== undefined ? idx + 1 : 0)); }; + const { nav, currentSettings } = useNav(); const showPreviousImage = () => { setCurrentImage(idx => (idx !== undefined && idx > 0 ? idx - 1 : idx)); @@ -38,11 +39,15 @@ const GridView = ({ settings, images, currentImage, setCurrentImage, setViewMode if (event.key === 'ArrowLeft') { showPreviousImage(); } - /* if (event.key === 'Escape') { - setCurrentImage(undefined); + nav({ + ...currentSettings, + topic: undefined, + npubs: [], + tags: [], + list: undefined, + }); } - */ }; const swipeHandlers = useSwipeable({ diff --git a/src/components/MasonryView/MasonryImage.tsx b/src/components/MasonryView/MasonryImage.tsx index a89675c..f22a3ed 100644 --- a/src/components/MasonryView/MasonryImage.tsx +++ b/src/components/MasonryView/MasonryImage.tsx @@ -40,7 +40,12 @@ const MasonryImage = ({ image, onClick, index }: MasonryImageProps) => { const showAuthor = currentSettings.npubs == undefined || currentSettings.npubs.length != 1; // if we are looking at a single profile, don't show the author - const description = image.content && image.content?.substring(0, 60) + (image.content.length > 60 ? ' ... ' : ' '); + const description = useMemo( + () => + image.content && + image.content.replace(/nostr:[a-z0-9]+/, '').substring(0, 60) + (image.content.length > 60 ? ' ... ' : ' '), + [image.content] + ); const showTags = useMemo(() => uniq(image.tags).slice(0, 5), [image.tags]); const now = unixNow(); const showInfo = true; diff --git a/src/components/MasonryView/MasonryView.tsx b/src/components/MasonryView/MasonryView.tsx index 4718721..8446535 100644 --- a/src/components/MasonryView/MasonryView.tsx +++ b/src/components/MasonryView/MasonryView.tsx @@ -80,11 +80,9 @@ const MasonryView = ({ settings, images, currentImage, setCurrentImage, setViewM if (event.key === 'ArrowLeft') { showPreviousImage(); } - /* if (event.key === 'Escape') { setCurrentImage(undefined); } - */ }; const swipeHandlers = useSwipeable({ diff --git a/src/components/SlideShow.tsx b/src/components/SlideShow.tsx index 21f3e37..1e14dfb 100644 --- a/src/components/SlideShow.tsx +++ b/src/components/SlideShow.tsx @@ -10,8 +10,9 @@ import { Post, isReply, isAdultRelated, + hasBlockedTag, } from './nostrImageDownload'; -import { adultContentTags, adultNPubs, blockedPublicKeys, mixedAdultNPubs, topics } from './env'; +import { adultContentTagsMap, adultNPubs, blockedPublicKeysMap, mixedAdultNPubs, topics } from './env'; import Settings from './Settings'; import SlideView from './SlideView'; import { nip19 } from 'nostr-tools'; @@ -135,7 +136,8 @@ const SlideShow = () => { events .filter( event => - !blockedPublicKeys.includes(event.pubkey.toLowerCase()) && // remove blocked authors + !blockedPublicKeysMap[event.pubkey.toLowerCase()] && // remove blocked authors + !hasBlockedTag(event) && (settings.showReplies || !isReply(event)) && (settings.showAdult || !isAdultRelated(event, settings.tags.length > 0)) ) @@ -309,10 +311,9 @@ const SlideShow = () => { const fullScreen = document.fullscreenElement !== null; const showAdultContentWarning = - !settings.showAdult && - (adultContentTags.some(t => settings.tags.includes(t)) || - adultNPubs.some(p => settings.npubs.includes(p)) || - mixedAdultNPubs.some(p => settings.npubs.includes(p))); + !settings.showAdult && (settings.tags.some(t => adultContentTagsMap[t]) || + adultNPubs.some(p => settings.npubs.includes(p)) || + mixedAdultNPubs.some(p => settings.npubs.includes(p))); if (showAdultContentWarning) { return ; diff --git a/src/components/env.ts b/src/components/env.ts index 2983ff7..2aaac34 100644 --- a/src/components/env.ts +++ b/src/components/env.ts @@ -17,7 +17,7 @@ type Topic = { by default. Users can enable this content through the adult content flag in the UI or through a URL parameter. */ -export const adultContentTags = [ +const adultContentTags = [ 'adult', 'ass', 'blowjob', @@ -63,11 +63,15 @@ export const adultContentTags = [ 'masturbation', ]; -export const topics: { [key: string]: Topic } = { - nostriga: { - name: 'Baltic Honey Badger 2024 / NOSTRIGA', - tags: ['nostriga', 'balticbadger', 'honeybadger', 'riga', 'bh2024', 'hb2024'], +export const adultContentTagsMap = adultContentTags.reduce( + (acc, tag) => { + acc[tag.toLocaleLowerCase()] = true; + return acc; }, + {} as Record +); + +export const topics: { [key: string]: Topic } = { art: { name: 'Art', tags: [ @@ -199,9 +203,13 @@ export const topics: { [key: string]: Topic } = { }, btcprague: { name: 'BTC Prague', - tags: ['btcprague', 'BTCPrague', 'devhackday', 'prague', 'praha'], + tags: ['btcprague', 'BTCPrague', 'devhackday'], description: 'All images/videos with related hashtags #btcprague #prague #praha', }, + bh2024nostriga: { + name: 'Baltic Honey Badger 2024 / NOSTRIGA', + tags: ['nostriga', 'balticbadger', 'honeybadger', 'riga', 'bh2024', 'hb2024'], + }, nsfw: { name: 'NSFW / Adult Content', tags: adultContentTags, @@ -381,14 +389,32 @@ export const adultNPubs = [ 'npub1rxsxj8egpr3emylfdld0wgh63w048tjh5zaua84h2qjscswn68ysdlt68s', 'npub13lpdphw06d5hy5h7n0xsun9sfpwqsna9gsg0y0d4ukktks048nrseedtx9', 'npub1lrxxjtq7zyp2d4n44tllwqj4q20kk7dslh2xrq0qkwng5lldxqesmvvgyt', + 'npub163nprvuh3y36l7we66hs6jhl92xymlnyq29mh3p86upe2828x7mqt8xdwf', + 'npub16fsxfvylsvlmv9fz3k7rga3wxl50plm8q708wgkwdr555lczlc5qu7xw8z', ]; -export const adultPublicKeys = adultNPubs.map(npub => (nip19.decode(npub).data as string).toLowerCase()); +const adultPublicKeys = adultNPubs.map(npub => (nip19.decode(npub).data as string).toLowerCase()); -export const mixedAdultPublicKeys = mixedAdultNPubs.map(npub => (nip19.decode(npub).data as string).toLowerCase()); +export const adultPublicKeysMap = adultPublicKeys.reduce( + (acc, key) => { + acc[key.toLocaleLowerCase()] = true; + return acc; + }, + {} as Record +); + +const mixedAdultPublicKeys = mixedAdultNPubs.map(npub => (nip19.decode(npub).data as string).toLowerCase()); + +export const mixedAdultPublicMaps = mixedAdultPublicKeys.reduce( + (acc, key) => { + acc[key.toLocaleLowerCase()] = true; + return acc; + }, + {} as Record +); /* The following profiles have questionable content and are blocked completely on slidestr.net */ -export const blockedNPubs = [ +const blockedNPubs = [ 'npub10m6d9ynzzx0w07spu2n5cx36z77smmyn7rs9gsvta57etrcyh68swace67', 'npub10xrnm6sy804cakmeew4g7kd4fl3dfvvsqfk6m3v4c6j4smrh9mlsdwpz7a', // CISAM 'npub125wcpwdn0zmt3accu3jlkv349jgw9d8htk4cjx2spc9qfvusl7hs6np5pt', @@ -415,9 +441,20 @@ export const blockedNPubs = [ 'npub1tt3n7nm548jf4jsgy9vwt25khz2n47d8um0lam0nmpk034zzlp2sfpc7tq', 'npub1yrqtnr4qxvjqj4zs45sw3xlrflzks86dhy0y4hzj9jweujflksfszhsp06', 'npub13mnfsm49p8hka246khma4gdzd9w8ygyt3udrcxmgmmhd5cyt5y3q879pvy', + 'npub13yn69pulpr6clwpzfj06rshu7kzj7zqvhf7d3mkyppynzrlnjjzqgsma3m', ]; -export const blockedPublicKeys = blockedNPubs.map(npub => (nip19.decode(npub).data as string).toLowerCase()); +const blockedPublicKeys = blockedNPubs.map(npub => (nip19.decode(npub).data as string).toLowerCase()); + +export const blockedPublicKeysMap = blockedPublicKeys.reduce( + (acc, key) => { + acc[key] = true; + return acc; + }, + {} as Record +); + +export const blockedHashtags = ['loli']; export const spamAccounts = []; diff --git a/src/components/nostrImageDownload.ts b/src/components/nostrImageDownload.ts index bfb08f2..f280640 100644 --- a/src/components/nostrImageDownload.ts +++ b/src/components/nostrImageDownload.ts @@ -1,5 +1,5 @@ import { NDKEvent, NDKFilter, NDKTag } from '@nostr-dev-kit/ndk'; -import { adultContentTags, adultPublicKeys, imageProxy, mixedAdultNPubs } from './env'; +import { adultContentTagsMap, adultPublicKeysMap, blockedHashtags, imageProxy, mixedAdultPublicMaps } from './env'; import uniq from 'lodash/uniq'; import { unixNow } from '../ngine/time'; import { ContentType } from '../utils/useNav'; @@ -94,21 +94,26 @@ export const hasContentWarning = ({ tags }: { tags?: NDKTag[] }) => { export const hasAdultTag = ({ tags }: { tags?: NDKTag[] }) => { if (!tags) return false; // ["e", "aab5a68f29d76a04ad79fe7e489087b802ee0f946689d73b0e15931dd40a7af3", "", "reply"] - return tags.filter((t: string[]) => t[0] === 't' && adultContentTags.includes(t[1])).length > 0; + return tags.some((t: string[]) => t[0] === 't' && adultContentTagsMap[t[1]]); +}; + +export const hasBlockedTag = ({ tags }: { tags?: NDKTag[] }) => { + if (!tags) return false; + return tags.filter((t: string[]) => t[0] === 't' && blockedHashtags.includes(t[1])).length > 0; }; export const isAdultRelated = ({ tags, pubkey }: { tags?: NDKTag[]; pubkey: string }, isTagSearch: boolean) => { // if we search for a specific non adult tag and the user in the mixed category // allow as non adult - if (isTagSearch && mixedAdultNPubs.includes(pubkey.toLowerCase()) && !hasAdultTag({ tags })) { + if (isTagSearch && mixedAdultPublicMaps[pubkey.toLowerCase()] && !hasAdultTag({ tags })) { return false; } return ( hasContentWarning({ tags }) || // block content warning hasAdultTag({ tags }) || // block adult tags - mixedAdultNPubs.includes(pubkey.toLowerCase()) || // block mixed adult authors - adultPublicKeys.includes(pubkey.toLowerCase()) // block adult authors + mixedAdultPublicMaps[pubkey.toLowerCase()] || // block mixed adult authors + adultPublicKeysMap[pubkey.toLowerCase()] // block adult authors ); };