diff --git a/index.html b/index.html index 733e207..3feb0e1 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,7 @@ slidestr.net +
diff --git a/public/fonts/outfit/outfit.css b/public/fonts/outfit/outfit.css new file mode 100644 index 0000000..9e4a910 --- /dev/null +++ b/public/fonts/outfit/outfit.css @@ -0,0 +1,72 @@ +/* latin-ext */ +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(outfit_400_latin-ext.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(outfit_400_latin.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* latin-ext */ +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(outfit_500_latin-ext.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(outfit_500_latin.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* latin-ext */ +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(outfit_600_latin-ext.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(outfit_600_latin.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* latin-ext */ +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(outfit_700_latin-ext.woff2) format('woff2'); + unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Outfit'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(outfit_700_latin.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/public/fonts/outfit/outfit_400_latin-ext.woff2 b/public/fonts/outfit/outfit_400_latin-ext.woff2 new file mode 100644 index 0000000..9428a7d Binary files /dev/null and b/public/fonts/outfit/outfit_400_latin-ext.woff2 differ diff --git a/public/fonts/outfit/outfit_400_latin.woff2 b/public/fonts/outfit/outfit_400_latin.woff2 new file mode 100644 index 0000000..b4b889e Binary files /dev/null and b/public/fonts/outfit/outfit_400_latin.woff2 differ diff --git a/public/fonts/outfit/outfit_500_latin-ext.woff2 b/public/fonts/outfit/outfit_500_latin-ext.woff2 new file mode 100644 index 0000000..9428a7d Binary files /dev/null and b/public/fonts/outfit/outfit_500_latin-ext.woff2 differ diff --git a/public/fonts/outfit/outfit_500_latin.woff2 b/public/fonts/outfit/outfit_500_latin.woff2 new file mode 100644 index 0000000..b4b889e Binary files /dev/null and b/public/fonts/outfit/outfit_500_latin.woff2 differ diff --git a/public/fonts/outfit/outfit_600_latin-ext.woff2 b/public/fonts/outfit/outfit_600_latin-ext.woff2 new file mode 100644 index 0000000..9428a7d Binary files /dev/null and b/public/fonts/outfit/outfit_600_latin-ext.woff2 differ diff --git a/public/fonts/outfit/outfit_600_latin.woff2 b/public/fonts/outfit/outfit_600_latin.woff2 new file mode 100644 index 0000000..b4b889e Binary files /dev/null and b/public/fonts/outfit/outfit_600_latin.woff2 differ diff --git a/public/fonts/outfit/outfit_700_latin-ext.woff2 b/public/fonts/outfit/outfit_700_latin-ext.woff2 new file mode 100644 index 0000000..9428a7d Binary files /dev/null and b/public/fonts/outfit/outfit_700_latin-ext.woff2 differ diff --git a/public/fonts/outfit/outfit_700_latin.woff2 b/public/fonts/outfit/outfit_700_latin.woff2 new file mode 100644 index 0000000..b4b889e Binary files /dev/null and b/public/fonts/outfit/outfit_700_latin.woff2 differ diff --git a/src/App.tsx b/src/App.tsx index ae9f619..ace4d9c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,11 +1,13 @@ -import { useParams } from "react-router-dom"; +import { useParams, useSearchParams } from "react-router-dom"; import SlideShow from "./components/SlideShow"; import "./App.css"; const App = () => { const { tags, npub } = useParams(); + const [ searchParams ] = useSearchParams(); + const nsfw = searchParams.get("nsfw") === "true"; - return ; + return ; }; export default App; diff --git a/src/components/Settings.css b/src/components/Settings.css new file mode 100644 index 0000000..317d580 --- /dev/null +++ b/src/components/Settings.css @@ -0,0 +1,73 @@ +@keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +.settings { + position: fixed; + width: 80vw; + height: 80vh; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(0, 0, 0, 0.8); + backdrop-filter: blur(10px); + justify-content: center; + align-items: center; + border-radius: 10px; + border: 1px solid rgba(255, 255, 255, 0.18); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + font-size: 1.2rem; + animation: fadeIn 0.5s ease-in-out; + z-index: 500; + padding: 2em; +} + +.settings form { + display: flex; + flex-direction: column; + gap: 24px; +} + +@media (max-width: 768px) { + .settings { + width: 90vw; + height: 90vh; + } +} + +@media (max-width: 576px) { + .settings { + width: 95vw; + height: 95vh; + } +} + +.settings .settings-content { + flex-grow: 0; +} + +.settings .settings-footer { + flex-shrink: 1; + display: flex; + justify-content: end; +} + +.content-warning { + padding: 16px; + border-radius: 16px; + border: 1px solid #ff563f; + display: flex; + gap: 12px; +} + +.warning { + color: #ff563f; + font-weight: 500; + font-size: 1.2rem; + margin-bottom: 0.5em; +} diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx new file mode 100644 index 0000000..f7d3a72 --- /dev/null +++ b/src/components/Settings.tsx @@ -0,0 +1,48 @@ +import { FormEvent, useState } from "react"; +import "./Settings.css"; +import { useNavigate, useSearchParams } from "react-router-dom"; + +const Settings = () => { + const [showNsfw, setShowNsfw] = useState(false); + const navigate = useNavigate(); + const [_, setSearchParams] = useSearchParams(); + + const onSubmit = (e: FormEvent) => { + e.preventDefault(); + //navigate(`/tags/foodstr?nsfw=${showNsfw}`); + setSearchParams({ nsfw: showNsfw.toString() }); + }; + + return ( +
+
+ {/* + + + + + */} +
+
+ setShowNsfw(e.target.checked)} + /> +
+
+
NSFW Content
+ Allow NSFW to be shown and ignore content warnings. +
+
+
+
+ +
+
+ ); +}; + +export default Settings; diff --git a/src/components/SlideShow.tsx b/src/components/SlideShow.tsx index cae7dc7..8ee66b8 100644 --- a/src/components/SlideShow.tsx +++ b/src/components/SlideShow.tsx @@ -17,6 +17,7 @@ import { urlFix, } from "./nostrImageDownload"; import { appName, nsfwPubKeys } from "./env"; +import Settings from "./Settings"; /* FEATURES: @@ -50,18 +51,21 @@ let eventsReceived = 0; type SlideShowProps = { tags?: string; npub?: string; + showNsfw: boolean; }; -const SlideShow = ({ tags, npub }: SlideShowProps) => { +const SlideShow = ({ tags, npub, showNsfw = false }: SlideShowProps) => { const { ndk, getProfile, loadNdk } = useNDK(); const [posts, setPosts] = useState([]); const images = useRef([]); const [activeImages, setActiveImages] = useState([]); const [history, setHistory] = useState([]); + const [paused, setPaused] = useState(false); + const [showSettings, setShowSettings] = useState(false); + const upcommingImage = useRef(); const [title, setTitle] = useState(appName); - const [paused, setPaused] = useState(false); const [loading, setLoading] = useState(true); const [activeNpub, setActiveNpub] = useState(undefined); const [activeContent, setActiveContent] = useState( @@ -91,10 +95,11 @@ const SlideShow = ({ tags, npub }: SlideShowProps) => { setPosts((oldPosts) => { if ( !isReply(event) && - (npub !== undefined || !hasContentWarning(event)) && // only allow content warnings on profile content - (npub !== undefined || !hasNsfwTag(event)) && // only allow nsfw on profile content - (npub !== undefined || !nsfwPubKeys.includes(event.pubkey.toLowerCase()) ) && // block nsfw authors - oldPosts.findIndex((p) => p.id === event.id) === -1 + oldPosts.findIndex((p) => p.id === event.id) === -1 && + (showNsfw || + (!hasContentWarning(event) && // only allow content warnings on profile content + !hasNsfwTag(event))) && // only allow nsfw on profile content + !nsfwPubKeys.includes(event.pubkey.toLowerCase()) // block nsfw authors ) { return [...oldPosts, event]; } @@ -119,7 +124,6 @@ const SlideShow = ({ tags, npub }: SlideShowProps) => { }; useEffect(() => { - loadNdk([ "wss://relay.nostr.band", "wss://nos.lol", @@ -141,7 +145,10 @@ const SlideShow = ({ tags, npub }: SlideShowProps) => { if (images.current.length > 0) { const randomImage = images.current[Math.floor(Math.random() * images.current.length)]; + + // TODO this creates potential duplicates when images are loaded from multiple relays images.current = images.current.filter((i) => i !== randomImage); + setHistory((oldHistory) => [...oldHistory, randomImage]); newActiveImages.push(randomImage); upcommingImage.current = randomImage; @@ -164,7 +171,9 @@ const SlideShow = ({ tags, npub }: SlideShowProps) => { url, author: p.author.npub, content: prepareContent(p.content), - 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()), })); }); console.log(images.current.length); @@ -185,6 +194,9 @@ const SlideShow = ({ tags, npub }: SlideShowProps) => { if (event.key === "p" || event.key === " " || event.key === "P") { setPaused((p) => !p); } + if (event.key === "Escape") { + setShowSettings((s) => !s); + } }; useEffect(() => { @@ -240,6 +252,9 @@ const SlideShow = ({ tags, npub }: SlideShowProps) => { {title} + + {showSettings && } + {!fullScreen && (