From 208f70cb7b5bb0c1f985f877e0b910c8cff39f07 Mon Sep 17 00:00:00 2001 From: Florian Maul Date: Tue, 1 Aug 2023 08:00:10 +0200 Subject: [PATCH] feat: Introduced tabs to the settings dialog --- src/App.tsx | 10 +++++ src/components/Settings.css | 20 ++++++++++ src/components/Settings.tsx | 59 ++++++++++++++++++++-------- src/components/SlideShow.tsx | 2 +- src/components/SlideView/index.tsx | 2 +- src/components/TagEditor.css | 6 +++ src/components/TagEditor.tsx | 13 ++++-- src/components/env.ts | 1 + src/components/nostrImageDownload.ts | 13 +++--- src/utils/useNav.ts | 5 +-- 10 files changed, 101 insertions(+), 30 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 809865c..939ea1b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,9 +2,19 @@ import SlideShow from './components/SlideShow'; import './App.css'; import Disclaimer from './components/Disclaimer'; import useDisclaimerState from './utils/useDisclaimerState'; +import useNav from './utils/useNav'; +import { useEffect } from 'react'; +import { defaultHashTags } from './components/env'; const App = () => { const { disclaimerAccepted, setDisclaimerAccepted } = useDisclaimerState(); + const { nav, currentSettings } = useNav(); + + useEffect(() => { + if (currentSettings.npubs.length == 0 && currentSettings.tags.length == 0) { + nav({ ...currentSettings, tags: defaultHashTags, showNsfw: false }); + } + }, []); return ( <> diff --git a/src/components/Settings.css b/src/components/Settings.css index 3495f49..28d7b8b 100644 --- a/src/components/Settings.css +++ b/src/components/Settings.css @@ -103,4 +103,24 @@ .settings .settings-content > * { margin-bottom: 1em; + width: 28em; +} + +.settings .settings-mode { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + align-items: center; + text-align: center; + width: 100%; + margin-bottom: 2em; +} + +.settings .settings-mode > div { + padding: 0.5em; + cursor: pointer; + border-bottom: 1px solid transparent; +} + +.settings .settings-mode > div.active { + border-bottom: 1px solid #7600ff; } diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index 1aac5fe..df7e3da 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -3,11 +3,14 @@ import './Settings.css'; import useNav from '../utils/useNav'; import CloseButton from './CloseButton/CloseButton'; import TagEditor, { Tag } from './TagEditor'; +import { defaultHashTags } from './env'; type SettingsProps = { onClose: () => void; }; +type Mode = 'all' | 'tags' | 'user'; + const SettingsDialog = ({ onClose }: SettingsProps) => { const { nav, currentSettings } = useNav(); const [showNsfw, setShowNsfw] = useState(currentSettings.showNsfw || false); @@ -15,18 +18,25 @@ const SettingsDialog = ({ onClose }: SettingsProps) => { currentSettings.tags.map(tag => ({ name: tag, selected: true, deletable: false })) ); const [npubs, setNpubs] = useState(currentSettings.npubs || []); + const [mode, setMode] = useState( + currentSettings.npubs.length == 1 ? 'user' : currentSettings.tags.length > 0 ? 'tags' : 'all' + ); const onSubmit = (e: FormEvent) => { e.preventDefault(); const validNpubs = npubs.filter(t => t.length > 0); const validTags = selectedTags.filter(t => t.selected).map(t => t.name); - if (validNpubs.length == 1) { + + if (mode == 'user' && validNpubs.length == 1) { nav({ ...currentSettings, tags: [], npubs: validNpubs, showNsfw }); - } else if (validTags.length > 0) { + } else if (mode == 'tags' && validTags.length > 0) { nav({ ...currentSettings, tags: validTags, npubs: [], showNsfw }); + } else if (mode == 'tags') { + nav({ ...currentSettings, tags: defaultHashTags, npubs: [], showNsfw }); } else { nav({ ...currentSettings, tags: [], npubs: [], showNsfw }); } + onClose(); }; @@ -36,20 +46,37 @@ const SettingsDialog = ({ onClose }: SettingsProps) => {
- - - - - setNpubs(e.target.value.split(',').map(t => t.trim().toLowerCase()))} - onKeyDown={e => e.stopPropagation()} - onKeyUp={e => e.stopPropagation()} - /> - +
+
setMode('tags')}> + By tags +
+
setMode('user')}> + By user profile +
+
setMode('all')}> + All of nostr +
+
+ {mode == 'tags' && ( + <> + + + + )} + {mode == 'user' && ( + <> + + setNpubs(e.target.value.split(',').map(t => t.trim().toLowerCase()))} + onKeyDown={e => e.stopPropagation()} + onKeyUp={e => e.stopPropagation()} + /> + + )}
setShowNsfw(e.target.checked)} /> diff --git a/src/components/SlideShow.tsx b/src/components/SlideShow.tsx index f66d03f..6efa010 100644 --- a/src/components/SlideShow.tsx +++ b/src/components/SlideShow.tsx @@ -121,7 +121,7 @@ const SlideShow = () => { type: isVideo(url) ? 'video' : 'image', timestamp: p.created_at, noteId: p.id ? nip19.noteEncode(p.id) : '', - tags: p.tags.filter((t: string[]) => t[0] === 't').map((t: string[]) => t[1].toLowerCase()), + tags: p.tags?.filter((t: string[]) => t[0] === 't').map((t: string[]) => t[1].toLowerCase()) || [], })); }), 'url' diff --git a/src/components/SlideView/index.tsx b/src/components/SlideView/index.tsx index 0d9e485..1b76d29 100644 --- a/src/components/SlideView/index.tsx +++ b/src/components/SlideView/index.tsx @@ -101,7 +101,7 @@ const SlideView = ({ settings, images, setShowGrid }: SlideViewProps) => { useEffect(() => { // console.log(`slideShowStarted = ${slideShowStarted}, images = ${images.current.length}`); - + // Make sure we have an image to start with but only trigger once if (!slideShowStarted && images.current.length > 2) { setSlideShowStarted(true); diff --git a/src/components/TagEditor.css b/src/components/TagEditor.css index 41a3f22..e8a0142 100644 --- a/src/components/TagEditor.css +++ b/src/components/TagEditor.css @@ -25,11 +25,17 @@ background-color: #7322ff; } +.tag-editor .tag.action { + background-color: white; + color: black; +} + .tag-editor .tag input[type='text'] { display: inline-block; margin: 0.3em; box-sizing: border-box; width: initial; + outline: none; } .tag-editor .delete-tag { diff --git a/src/components/TagEditor.tsx b/src/components/TagEditor.tsx index 3639604..cd6b7fd 100644 --- a/src/components/TagEditor.tsx +++ b/src/components/TagEditor.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import './TagEditor.css'; import useNav from '../utils/useNav'; import uniq from 'lodash/uniq'; @@ -7,7 +7,7 @@ import useUserTags from '../utils/useUserTags'; type TagEditorProps = { selectedTags: Tag[]; - setSelectedTags: (tags: Tag[]) => void; + setSelectedTags: React.Dispatch>; }; export type Tag = { @@ -60,6 +60,8 @@ const TagEditor = ({ selectedTags, setSelectedTags }: TagEditorProps) => { setSelectedTags([...tags]); }; + const isATagSelected = useMemo(() => selectedTags.some(tag => tag.selected), [selectedTags]); + return (
{selectedTags.map(tag => ( @@ -72,7 +74,7 @@ const TagEditor = ({ selectedTags, setSelectedTags }: TagEditorProps) => { )}
))} -
setEditMode(true)}> +
setEditMode(true)}> + {editMode && ( { /> )}
+ {isATagSelected && ( +
setSelectedTags(tags => tags.map(s => ({ ...s, selected: false })))}> + none +
+ )}
); }; diff --git a/src/components/env.ts b/src/components/env.ts index 8a47c81..30a3b27 100644 --- a/src/components/env.ts +++ b/src/components/env.ts @@ -132,6 +132,7 @@ export const nsfwNPubs = [ 'npub1y77j6jm5hw34xl5m85aumltv88arh2s7q383allkpfe4muarzc5qzfgru0', // sexy-models 'npub1ylrnf0xfp9wsmqthxlqjqyqj9yy27pnchjwjq93v3mq66ts7ftjs6x7dcq', // Welcome To The Jungle 'npub1z0xv9t5w6evrcg860kmgqq5tfj55mz84ta40uszjnfp9uhw2clkq63yrak', // ??? + 'npub1j70jp36nshq4zknnwgeamux8hdgzhf0yw50rpll0egw6cvnglalsuldjwe', // cyâ‚żerleolao ]; export const nsfwPublicKeys = nsfwNPubs.map(npub => (nip19.decode(npub).data as string).toLowerCase()); diff --git a/src/components/nostrImageDownload.ts b/src/components/nostrImageDownload.ts index 2f248b4..1517e96 100644 --- a/src/components/nostrImageDownload.ts +++ b/src/components/nostrImageDownload.ts @@ -15,7 +15,7 @@ export type NostrImage = { export interface NostrEvent { created_at: number; content: string; - tags: NDKTag[]; + tags?: NDKTag[]; kind?: NDKKind | number; pubkey: string; id?: string; @@ -60,22 +60,25 @@ export const extractImageUrls = (text: string): string[] => { return (text.match(urlRegex) || []).map(u => urlFix(u)); }; -export const isReply = ({ tags }: { tags: NDKTag[] }) => { +export const isReply = ({ tags }: { tags?: NDKTag[] }) => { + if (!tags) return false; // ["e", "aab5a68f29d76a04ad79fe7e489087b802ee0f946689d73b0e15931dd40a7af3", "", "reply"] return tags.filter((t: string[]) => t[0] === 'e' && t[3] === 'reply').length > 0; }; -export const hasContentWarning = ({ tags }: { tags: NDKTag[] }) => { +export const hasContentWarning = ({ tags }: { tags?: NDKTag[] }) => { + if (!tags) return false; // ["content-warning", "NSFW: implied nudity"] return tags.filter((t: string[]) => t[0] === 'content-warning').length > 0; }; -export const hasNsfwTag = ({ tags }: { tags: NDKTag[] }) => { +export const hasNsfwTag = ({ tags }: { tags?: NDKTag[] }) => { + if (!tags) return false; // ["e", "aab5a68f29d76a04ad79fe7e489087b802ee0f946689d73b0e15931dd40a7af3", "", "reply"] return tags.filter((t: string[]) => t[0] === 't' && nfswTags.includes(t[1])).length > 0; }; -export const isNsfwRelated = ({ tags, pubkey }: { tags: NDKTag[]; pubkey: string }) => { +export const isNsfwRelated = ({ tags, pubkey }: { tags?: NDKTag[]; pubkey: string }) => { return ( hasContentWarning({ tags }) || // block content warning hasNsfwTag({ tags }) || // block nsfw tags diff --git a/src/utils/useNav.ts b/src/utils/useNav.ts index 18a113b..3152da1 100644 --- a/src/utils/useNav.ts +++ b/src/utils/useNav.ts @@ -18,10 +18,7 @@ const useNav = () => { console.log(`tags = ${tags}, npub = ${npub}, nsfw = ${nsfw}`); - let useTags = tags?.split(',') || []; - if (npub == undefined && (useTags == undefined || useTags.length == 0)) { - useTags = defaultHashTags; - } + const useTags = tags?.split(',') || []; return { tags: useTags,