feat: Introduced tabs to the settings dialog

This commit is contained in:
florian 2023-08-01 08:00:10 +02:00
parent 82cb432e80
commit 208f70cb7b
10 changed files with 101 additions and 30 deletions

View File

@ -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 (
<>

View File

@ -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;
}

View File

@ -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)} />

View File

@ -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'

View File

@ -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 {

View File

@ -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>
);
};

View File

@ -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());

View File

@ -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

View File

@ -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,