feat: Introduced tabs to the settings dialog
This commit is contained in:
parent
82cb432e80
commit
208f70cb7b
10
src/App.tsx
10
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 (
|
||||
<>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<Mode>(
|
||||
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,9 +46,25 @@ const SettingsDialog = ({ onClose }: SettingsProps) => {
|
||||
<CloseButton onClick={onClose}></CloseButton>
|
||||
|
||||
<div className="settings-content">
|
||||
<label htmlFor="tags">Images for tags:</label>
|
||||
<div className="settings-mode">
|
||||
<div className={mode == 'tags' ? 'active' : ''} onClick={() => setMode('tags')}>
|
||||
By tags
|
||||
</div>
|
||||
<div className={mode == 'user' ? 'active' : ''} onClick={() => setMode('user')}>
|
||||
By user profile
|
||||
</div>
|
||||
<div className={mode == 'all' ? 'active' : ''} onClick={() => setMode('all')}>
|
||||
All of nostr
|
||||
</div>
|
||||
</div>
|
||||
{mode == 'tags' && (
|
||||
<>
|
||||
<label htmlFor="tags"></label>
|
||||
<TagEditor selectedTags={selectedTags} setSelectedTags={setSelectedTags} />
|
||||
|
||||
</>
|
||||
)}
|
||||
{mode == 'user' && (
|
||||
<>
|
||||
<label htmlFor="npub">Images for user profile (npub):</label>
|
||||
<input
|
||||
type="text"
|
||||
@ -49,7 +75,8 @@ const SettingsDialog = ({ onClose }: SettingsProps) => {
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
onKeyUp={e => e.stopPropagation()}
|
||||
/>
|
||||
|
||||
</>
|
||||
)}
|
||||
<div className="content-warning">
|
||||
<div>
|
||||
<input name="nsfw" type="checkbox" checked={showNsfw} onChange={e => setShowNsfw(e.target.checked)} />
|
||||
|
@ -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'
|
||||
|
@ -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 {
|
||||
|
@ -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<React.SetStateAction<Tag[]>>;
|
||||
};
|
||||
|
||||
export type Tag = {
|
||||
@ -60,6 +60,8 @@ const TagEditor = ({ selectedTags, setSelectedTags }: TagEditorProps) => {
|
||||
setSelectedTags([...tags]);
|
||||
};
|
||||
|
||||
const isATagSelected = useMemo(() => selectedTags.some(tag => tag.selected), [selectedTags]);
|
||||
|
||||
return (
|
||||
<div className="tag-editor">
|
||||
{selectedTags.map(tag => (
|
||||
@ -72,7 +74,7 @@ const TagEditor = ({ selectedTags, setSelectedTags }: TagEditorProps) => {
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div className="tag" onClick={() => setEditMode(true)}>
|
||||
<div className="tag action" onClick={() => setEditMode(true)}>
|
||||
+
|
||||
{editMode && (
|
||||
<input
|
||||
@ -88,6 +90,11 @@ const TagEditor = ({ selectedTags, setSelectedTags }: TagEditorProps) => {
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{isATagSelected && (
|
||||
<div className="tag action" onClick={() => setSelectedTags(tags => tags.map(s => ({ ...s, selected: false })))}>
|
||||
none
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user