From 252612e7df1ebb8c1a94266c9fe1c040c608cb57 Mon Sep 17 00:00:00 2001 From: florian <> Date: Tue, 5 Mar 2024 14:02:42 +0000 Subject: [PATCH] feat: Moved global and nsfw section to home page --- package.json | 20 ++++----- src/App.css | 1 - src/components/GridView/index.tsx | 1 - src/components/Home/index.tsx | 45 +++++++++++++++++++-- src/components/Login/Login.css | 15 +++---- src/components/MasonryView/MasonryImage.tsx | 2 +- src/components/MasonryView/MasonryView.css | 16 ++++++-- src/components/Settings.css | 14 ++++++- src/components/Settings.tsx | 2 +- src/components/SlideShow.tsx | 29 +++++++++---- src/components/env.ts | 16 +++++++- src/components/nostrImageDownload.ts | 11 +++-- src/ngine/hooks/useEvents.ts | 4 +- src/utils/useAuthorsFromList.ts | 21 ++++++---- src/utils/useLists.ts | 8 +++- src/utils/useNav.ts | 3 +- 16 files changed, 154 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index c770fe6..05e0e10 100644 --- a/package.json +++ b/package.json @@ -12,36 +12,36 @@ "analyze": "vite-bundle-visualizer" }, "dependencies": { - "@nostr-dev-kit/ndk": "^2.4.2", - "@nostr-dev-kit/ndk-cache-dexie": "^2.2.6", - "@tanstack/react-query": "^5.22.2", + "@nostr-dev-kit/ndk": "^2.5.0", + "@nostr-dev-kit/ndk-cache-dexie": "^2.2.7", + "@tanstack/react-query": "^5.24.8", "bech32": "^2.0.0", - "jotai": "^2.6.5", + "jotai": "^2.7.0", "lodash": "^4.17.21", "nostr-tools": "^2.3.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-helmet": "^6.1.0", "react-lazy-load": "^4.0.1", - "react-router-dom": "^6.22.1", + "react-router-dom": "^6.22.2", "react-swipeable": "^7.0.1" }, "devDependencies": { "@types/lodash": "^4.14.202", - "@types/react": "^18.2.57", + "@types/react": "^18.2.63", "@types/react-dom": "^18.2.19", "@types/react-helmet": "^6.1.11", "@types/react-swipeable": "^5.2.0", - "@typescript-eslint/eslint-plugin": "^7.0.2", - "@typescript-eslint/parser": "^7.0.2", + "@typescript-eslint/eslint-plugin": "^7.1.1", + "@typescript-eslint/parser": "^7.1.1", "@vitejs/plugin-react": "^4.2.1", "@webbtc/webln-types": "^3.0.0", - "eslint": "^8.56.0", + "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", "prettier": "^3.2.5", "typescript": "^5.3.3", - "vite": "^5.1.4", + "vite": "^5.1.5", "vite-bundle-visualizer": "^1.0.1" } } diff --git a/src/App.css b/src/App.css index c288b5a..2ada74d 100644 --- a/src/App.css +++ b/src/App.css @@ -48,7 +48,6 @@ h1 { button { border-radius: 8px; border: 1px solid transparent; - padding: 0.6em 1.2em; font-size: 1em; font-weight: 500; font-family: inherit; diff --git a/src/components/GridView/index.tsx b/src/components/GridView/index.tsx index c3def0f..b441d35 100644 --- a/src/components/GridView/index.tsx +++ b/src/components/GridView/index.tsx @@ -30,7 +30,6 @@ const GridView = ({ settings, images, currentImage, setCurrentImage, setViewMode }; const onKeyDown = (event: KeyboardEvent) => { - console.log(event); if (event.key === 'ArrowRight') { showNextImage(); diff --git a/src/components/Home/index.tsx b/src/components/Home/index.tsx index f072417..e0dbca8 100644 --- a/src/components/Home/index.tsx +++ b/src/components/Home/index.tsx @@ -4,9 +4,11 @@ import './Home.css'; import { useGlobalState } from '../../utils/globalState'; import usePeopleLists from '../../utils/useLists'; import { createImgProxyUrl } from '../nostrImageDownload'; +import { useState } from 'react'; const Home = () => { const { nav, currentSettings } = useNav(); + const [showAdult, setShowAdult] = useState(currentSettings.showAdult || false); const [state] = useGlobalState(); const topicKeys = Object.keys(topics); const lists = usePeopleLists(state.userNPub); @@ -18,17 +20,34 @@ const Home = () => {
{topicKeys.map(tk => (
- nav({ ...currentSettings, topic: tk, npubs: [], tags: [], list: undefined, follows: false }) + nav({ ...currentSettings, topic: tk, npubs: [], tags: [], list: undefined, follows: false, showAdult }) } >
{topics[tk].name || tk}
))} +
+ nav({ + tags: [], + npubs: [], + showReplies: false, + showReposts: false, + follows: false, + showAdult, + }) + } + > +
Global
+
All content posted on nostr. Use with care!
+
{state.userNPub && ( <> @@ -47,6 +66,7 @@ const Home = () => { follows: true, showReplies: false, showReposts: true, + showAdult, }) } > @@ -60,18 +80,37 @@ const Home = () => {
{lists.map(l => (
- nav({ ...currentSettings, tags: [], npubs: [], list: l.nevent, topic: undefined, follows: false }) + nav({ + ...currentSettings, + tags: [], + npubs: [], + list: l.nevent, + topic: undefined, + follows: false, + showAdult, + }) } >
{l.name}
+ {l.description &&
{l.description}
}
))}
)} +
+
+ setShowAdult(sa => !sa)} /> +
+ +
Made with 💜 by{' '} diff --git a/src/components/Login/Login.css b/src/components/Login/Login.css index 980a995..11a8378 100644 --- a/src/components/Login/Login.css +++ b/src/components/Login/Login.css @@ -55,16 +55,17 @@ flex-direction: row; gap: 8px; } -.login-dialog .login-address button { + +.login-dialog button, +.login-dialog button:visited, +.login-dialog button:active { background-color: rgb(99, 19, 173); -} -.login-dialog .login-extension { - align-self: center; + color: #fff; + height: 3em; } -.login-dialog .login-extension button { - height: 3em; - background-color: rgb(99, 19, 173); +.login-dialog .login-extension { + align-self: center; } .login-dialog .close-button { diff --git a/src/components/MasonryView/MasonryImage.tsx b/src/components/MasonryView/MasonryImage.tsx index af8e142..3769d89 100644 --- a/src/components/MasonryView/MasonryImage.tsx +++ b/src/components/MasonryView/MasonryImage.tsx @@ -82,7 +82,7 @@ const MasonryImage = ({ image, onClick, index }: MasonryImageProps) => { )} {(showAuthor || description || showTags.length > 0) && ( -
+
{image.timestamp && timeDifference(now, image.timestamp)}
{showAuthor && (
diff --git a/src/components/MasonryView/MasonryView.css b/src/components/MasonryView/MasonryView.css index fa0d51a..d118813 100644 --- a/src/components/MasonryView/MasonryView.css +++ b/src/components/MasonryView/MasonryView.css @@ -64,6 +64,14 @@ outline: 1px solid #fff; } +.mason-imagegrid .info-section { + display: block; + line-height: 1.4em; + padding-bottom: 0.5em; + padding-top: 0.5em; + position: relative; +} + .profile-header { padding: 1.2em; padding-bottom: 0.6em; @@ -95,9 +103,9 @@ .mason-imagegrid .time { position: absolute; right: 0px; - top:0px; + top: 0px; color: #666; - padding-top: .5em; + padding-top: 0.5em; background-color: #111; - padding-left: .4em; -} \ No newline at end of file + padding-left: 0.4em; +} diff --git a/src/components/Settings.css b/src/components/Settings.css index 34bc4a6..a8c5956 100644 --- a/src/components/Settings.css +++ b/src/components/Settings.css @@ -81,15 +81,25 @@ .content-warning { padding: 16px; border-radius: 16px; - border: 1px solid #ff563f; + border: 1px solid #444; + display: flex; gap: 12px; } -.warning { +.content-warning.active { + border: 1px solid #ff563f; +} + +.content-warning.active .warning { color: #ff563f; +} + +.content-warning .warning { + color: #444; font-weight: 500; font-size: 1.2rem; + cursor: pointer; } .settings .replies, diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index 5a891a5..3871875 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -182,7 +182,7 @@ const SettingsDialog = ({ onClose, setViewMode }: SettingsProps) => {
)} -
+
setShowAdult(e.target.checked)} />
diff --git a/src/components/SlideShow.tsx b/src/components/SlideShow.tsx index 2b3a4e1..8b9ac52 100644 --- a/src/components/SlideShow.tsx +++ b/src/components/SlideShow.tsx @@ -91,19 +91,34 @@ const SlideShow = () => { const listAuthors = useAuthorsFromList(settings.list); const [contacts] = useAtom(followsAtom); - const authorsToQuery = settings.follows - ? contacts?.tags.filter(t => t[0] === 'p').map(t => t[1]) || [] - : listAuthors && listAuthors.length > 0 - ? listAuthors - : settings.npubs.map(p => nip19.decode(p).data as string); + const filter = useMemo(() => { + const authorsToQuery = settings.follows + ? contacts?.tags.filter(t => t[0] === 'p').map(t => t[1]) || [] + : listAuthors && listAuthors.length > 0 + ? listAuthors + : settings.npubs.map(p => nip19.decode(p).data as string); - const filterTags = settings.topic ? topics[settings.topic].tags : settings.tags; + const filterTags = settings.topic ? topics[settings.topic].tags : settings.tags; - const { events } = useEvents(buildFilter(filterTags, authorsToQuery, settings.showReposts), { + return buildFilter(filterTags, authorsToQuery, settings.showReposts); + }, [ + contacts?.tags, + listAuthors, + settings.follows, + settings.npubs, + settings.showReposts, + settings.tags, + settings.topic, + ]); + + const { events } = useEvents(filter, { cacheUsage: NDKSubscriptionCacheUsage.PARALLEL, + // when seeing global, close stream because of too many updates. + closeOnEose: settings.npubs.length == 0 && settings.tags.length == 0 }); useEffect(() => { + // console.log('set posts'); setPosts( events .filter( diff --git a/src/components/env.ts b/src/components/env.ts index e175cd3..42306a7 100644 --- a/src/components/env.ts +++ b/src/components/env.ts @@ -47,7 +47,19 @@ export const topics: { [key: string]: Topic } = { }, lifestyle: { name: 'Lifestyle', - tags: ['fashion', 'flowerstr', 'foodstr', 'style', 'weedstr', 'travel', 'travelstr', 'happy', 'life', 'love'], + tags: [ + 'fashion', + 'flowerstr', + 'foodstr', + 'style', + 'weedstr', + 'travel', + 'travelstr', + 'happy', + 'life', + 'love', + 'hiking', + ], }, gardenandfarm: { name: 'Gardening und Farming', tags: ['gardening', 'gardenstr', 'nature', 'farming', 'farmstr'] }, }; @@ -132,6 +144,7 @@ export const adultContentTags = [ 'nakedart', 'nasstr', 'nodestr', + 'naughty', 'nsfw', 'nude', 'nudeart', @@ -240,6 +253,7 @@ export const adultNPubs = [ 'npub1ylrnf0xfp9wsmqthxlqjqyqj9yy27pnchjwjq93v3mq66ts7ftjs6x7dcq', // Welcome To The Jungle 'npub1z0xv9t5w6evrcg860kmgqq5tfj55mz84ta40uszjnfp9uhw2clkq63yrak', // ??? 'npub1f3n7hq0a6vyfsjrv9vfdwtasa0g98ve96he68rxsvq9x6cl8tvxqmv6ca4', // Lady Sex (nude anime) + 'npub1ylq5s3xsdmzgzvgzll6ghcs3qa8a9ajl955hj4tcpmyruvjsl8nq5wqhd8', // Dnera 'npub1t07mr7m65lg3ecr5eapu6qe4ayt2wgjpqjs8x58m5kx2r2cutsyqyzzzs9', // NOT NSFW but spammy ai pictures 'npub1curnt7jtq8mhl9fcswnwvuvc9ccm6lvsdv4kzydx75v92kldrvdqh7sq09', // NOT NSFW but spammy ai pictures diff --git a/src/components/nostrImageDownload.ts b/src/components/nostrImageDownload.ts index e65f7cf..18013ce 100644 --- a/src/components/nostrImageDownload.ts +++ b/src/components/nostrImageDownload.ts @@ -1,6 +1,7 @@ import { NDKEvent, NDKFilter, NDKTag } from '@nostr-dev-kit/ndk'; import { adultContentTags, adultPublicKeys, mixedAdultNPubs } from './env'; import uniq from 'lodash/uniq'; +import { unixNow } from '../ngine/time'; export type Post = { event: NDKEvent; @@ -23,7 +24,7 @@ export type NostrImage = { export const buildFilter = (tags: string[], authors: string[], withReposts = false) => { const filter: NDKFilter = { kinds: [1, 1063] as number[], - limit: authors.length > 0 ? 1000 : 500, + limit: authors.length > 0 ? 1000 : tags.length > 0 ? 500 : 500, }; if (withReposts) { @@ -32,10 +33,10 @@ export const buildFilter = (tags: string[], authors: string[], withReposts = fal if (authors && authors.length > 0) { filter.authors = authors; + } else if (tags && tags.length > 0) { + filter['#t'] = tags; } else { - if (tags && tags.length > 0) { - filter['#t'] = tags; - } + filter.since = unixNow() - 60 * 60 * 24; // 24h } // console.log('filter', filter); @@ -67,6 +68,8 @@ export const urlFix = (url: string) => { }; export const extractImageUrls = (text: string): string[] => { + if (text == undefined) return []; + const urlRegex = /(https?:\/\/[^\s]+)/g; const matchedUrls = (text.match(urlRegex) || []).map(u => urlFix(u)); return uniq(matchedUrls); diff --git a/src/ngine/hooks/useEvents.ts b/src/ngine/hooks/useEvents.ts index 49631f7..be02f93 100644 --- a/src/ngine/hooks/useEvents.ts +++ b/src/ngine/hooks/useEvents.ts @@ -15,16 +15,18 @@ export default function useEvents(filter: NDKFilter | NDKFilter[], opts?: Subscr const [eose, setEose] = useState(false); const [events, setEvents] = useState([]); const id = useMemo(() => { + console.warn('new ID!!!'); return hashSha256(filter); }, [filter]); useEffect(() => { if (filter && !opts?.disable) { - console.log('useEvents: new Subscription'); + // console.log('useEvents: new Subscription', filter, opts); setEvents([]); const relaySet = relays?.length ?? 0 > 0 ? NDKRelaySet.fromRelayUrls(relays as string[], ndk) : undefined; const sub = ndk.subscribe(filter, opts, relaySet); sub.on('event', (ev: NDKEvent) => { + // console.log('new event '); setEvents(evs => { const newEvents = evs.concat([ev]).sort((a, b) => (b.created_at ?? 0) - (a.created_at ?? 0)); return uniqBy(newEvents, (e: NDKEvent) => e.tagId()); diff --git a/src/utils/useAuthorsFromList.ts b/src/utils/useAuthorsFromList.ts index fd25415..5313756 100644 --- a/src/utils/useAuthorsFromList.ts +++ b/src/utils/useAuthorsFromList.ts @@ -1,7 +1,8 @@ +import { useMemo } from 'react'; import useEvent from '../ngine/hooks/useEvent'; import { nip19 } from 'nostr-tools'; -const useAuthorsFromList = (listAddr?: string) => { +const useAuthorsFromList = (listAddr?: string): string[] => { const validListAttr = listAddr?.indexOf('naddr') == 0; const addr = listAddr ? (nip19.decode(listAddr).data as nip19.AddressPointer) : undefined; const addrIsDefined = addr && addr.pubkey && addr.identifier; @@ -13,13 +14,17 @@ const useAuthorsFromList = (listAddr?: string) => { { kinds: [30000], authors: authorFilter, '#d': identFilter }, { disable: !validListAttr && !addrIsDefined } ); - const authors: string[] = - (validListAttr && - listEvent - ?.getMatchingTags('p') - .map(t => t[1]) - .flat()) || - []; + + const authors: string[] = useMemo(() => { + return ( + (validListAttr && + listEvent + ?.getMatchingTags('p') + .map(t => t[1]) + .flat()) || + [] + ); + }, [listEvent, validListAttr]); return authors; }; diff --git a/src/utils/useLists.ts b/src/utils/useLists.ts index 74d8665..ec3bfff 100644 --- a/src/utils/useLists.ts +++ b/src/utils/useLists.ts @@ -16,10 +16,14 @@ const usePeopleLists = (npub?: string) => { return eventsWithName.map(e => { const nameTag = e.getMatchingTags('d').slice(0, 1).flat(); - const name = nameTag ? nameTag[1] : 'unknown'; + const titleTag = e.getMatchingTags('title').slice(0, 1).flat(); + const descriptionTag = e.getMatchingTags('description').slice(0, 1).flat(); + + const name = titleTag.length>0 ? titleTag[1] : nameTag ? nameTag[1] : 'unknown'; + const description = descriptionTag.length > 0 && descriptionTag[1]; const people = e.tags.filter(t => t[0] === 'p')?.map(t => t[1]); - return { id: e.id, nevent: e.encode(), name, people }; + return { id: e.id, nevent: e.encode(), name, people, description }; }); }, [events]); diff --git a/src/utils/useNav.ts b/src/utils/useNav.ts index d74a11d..edf3198 100644 --- a/src/utils/useNav.ts +++ b/src/utils/useNav.ts @@ -52,6 +52,7 @@ const useNav = () => { } const postfix = searchParams.length > 0 ? `?${searchParams.join('&')}` : ''; + console.log(settings); if (settings.topic) { navigate(`/topic/${settings.topic}${postfix}`); } else if (settings.follows) { @@ -63,7 +64,7 @@ const useNav = () => { } else if (validNpubs.length == 1) { navigate(`/p/${validNpubs[0]}${postfix}`); } else { - navigate(`/${postfix}`); + navigate(`/global${postfix}`); } };