chore: formatting
This commit is contained in:
parent
45181ad29e
commit
a3edb311fd
16
.prettierrc
Normal file
16
.prettierrc
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": false,
|
||||
"arrowParens": "avoid",
|
||||
"rangeStart": 0,
|
||||
"rangeEnd": 9007199254740991,
|
||||
"requirePragma": false,
|
||||
"insertPragma": false,
|
||||
"proseWrap": "preserve"
|
||||
}
|
247
package-lock.json
generated
247
package-lock.json
generated
@ -30,8 +30,10 @@
|
||||
"eslint": "^8.38.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.3.4",
|
||||
"prettier": "^3.0.0",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.3.9"
|
||||
"vite": "^4.3.9",
|
||||
"vite-bundle-visualizer": "^0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aashutoshrathi/word-wrap": {
|
||||
@ -1601,6 +1603,15 @@
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/cac": {
|
||||
"version": "6.7.14",
|
||||
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
|
||||
"integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
@ -1678,6 +1689,20 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
@ -1801,6 +1826,15 @@
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
|
||||
},
|
||||
"node_modules/define-lazy-prop": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
|
||||
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/define-properties": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
|
||||
@ -2760,6 +2794,15 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||
@ -3123,6 +3166,21 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-docker": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
|
||||
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"is-docker": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
@ -3298,6 +3356,18 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-wsl": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
|
||||
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-docker": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
@ -4061,6 +4131,23 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/open": {
|
||||
"version": "8.4.2",
|
||||
"resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
|
||||
"integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"define-lazy-prop": "^2.0.0",
|
||||
"is-docker": "^2.1.1",
|
||||
"is-wsl": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.3",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
|
||||
@ -4259,6 +4346,21 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz",
|
||||
"integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/pretty-format": {
|
||||
"version": "29.6.1",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.1.tgz",
|
||||
@ -4598,6 +4700,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.2",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
|
||||
@ -4661,6 +4772,32 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup-plugin-visualizer": {
|
||||
"version": "5.9.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.9.2.tgz",
|
||||
"integrity": "sha512-waHktD5mlWrYFrhOLbti4YgQCn1uR24nYsNuXxg7LkPH8KdTXVWR9DNY1WU0QqokyMixVXJS4J04HNrVTMP01A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"open": "^8.4.0",
|
||||
"picomatch": "^2.3.1",
|
||||
"source-map": "^0.7.4",
|
||||
"yargs": "^17.5.1"
|
||||
},
|
||||
"bin": {
|
||||
"rollup-plugin-visualizer": "dist/bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "2.x || 3.x"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
@ -4782,6 +4919,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
|
||||
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
@ -5297,6 +5443,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vite-bundle-visualizer": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/vite-bundle-visualizer/-/vite-bundle-visualizer-0.10.0.tgz",
|
||||
"integrity": "sha512-11AwKlkhvw6jjiGbTiCZqBSGg/FQDLc0mVcoLWVov2jU/Ban67l+Sk4Fa0Iyctb5sObqg/dA28HkKCEmSRjw9g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cac": "^6.7.14",
|
||||
"rollup-plugin-visualizer": "^5.9.2"
|
||||
},
|
||||
"bin": {
|
||||
"vite-bundle-visualizer": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/android-arm": {
|
||||
"version": "0.18.13",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.13.tgz",
|
||||
@ -5780,11 +5939,70 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yaeti": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
|
||||
@ -5799,6 +6017,24 @@
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs-parser": {
|
||||
"version": "20.2.9",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
|
||||
@ -5807,6 +6043,15 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs/node_modules/yargs-parser": {
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
|
@ -7,7 +7,9 @@
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"format": "prettier -w src/",
|
||||
"analyze": "vite-bundle-visualizer"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nostr-dev-kit/ndk": "^0.7.7",
|
||||
@ -32,7 +34,9 @@
|
||||
"eslint": "^8.38.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.3.4",
|
||||
"prettier": "^3.0.0",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.3.9"
|
||||
"vite": "^4.3.9",
|
||||
"vite-bundle-visualizer": "^0.10.0"
|
||||
}
|
||||
}
|
||||
|
25
src/App.tsx
25
src/App.tsx
@ -1,22 +1,22 @@
|
||||
import { useParams, useSearchParams } from "react-router-dom";
|
||||
import SlideShow from "./components/SlideShow";
|
||||
import "./App.css";
|
||||
import Disclaimer from "./components/Disclaimer";
|
||||
import useDisclaimerState from "./utils/useDisclaimerState";
|
||||
import { defaultHashTags } from "./components/env";
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
import SlideShow from './components/SlideShow';
|
||||
import './App.css';
|
||||
import Disclaimer from './components/Disclaimer';
|
||||
import useDisclaimerState from './utils/useDisclaimerState';
|
||||
import { defaultHashTags } from './components/env';
|
||||
|
||||
const App = () => {
|
||||
const { disclaimerAccepted, setDisclaimerAccepted } = useDisclaimerState();
|
||||
|
||||
const { tags, npub } = useParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
const nsfw = searchParams.get("nsfw") === "true";
|
||||
const nsfw = searchParams.get('nsfw') === 'true';
|
||||
|
||||
console.log(`tags = ${tags}, npub = ${npub}, nsfw = ${nsfw}`);
|
||||
|
||||
let useTags = tags?.split(",") || [];
|
||||
if (npub == undefined && (useTags == undefined || useTags.length == 0)) {
|
||||
useTags = (defaultHashTags);
|
||||
let useTags = tags?.split(',') || [];
|
||||
if (npub == undefined && (useTags == undefined || useTags.length == 0)) {
|
||||
useTags = defaultHashTags;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -24,10 +24,7 @@ const App = () => {
|
||||
{disclaimerAccepted ? (
|
||||
<SlideShow tags={useTags} npubs={npub ? [npub] : []} showNsfw={nsfw} />
|
||||
) : (
|
||||
<Disclaimer
|
||||
disclaimerAccepted={disclaimerAccepted}
|
||||
setDisclaimerAccepted={setDisclaimerAccepted}
|
||||
/>
|
||||
<Disclaimer disclaimerAccepted={disclaimerAccepted} setDisclaimerAccepted={setDisclaimerAccepted} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import "./Disclaimer.css";
|
||||
import { MouseEvent } from "react";
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import './Disclaimer.css';
|
||||
import { MouseEvent } from 'react';
|
||||
|
||||
const AdultContentInfo = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const proceed = (e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
const nsfwPostfix = "?nsfw=true";
|
||||
const nsfwPostfix = '?nsfw=true';
|
||||
navigate(`${window.location.pathname}${nsfwPostfix}`);
|
||||
};
|
||||
const goBack = (e: MouseEvent<HTMLButtonElement>) => {
|
||||
@ -18,19 +18,19 @@ const AdultContentInfo = () => {
|
||||
return (
|
||||
<div className="disclaimer">
|
||||
<div className="disclaimer-content">
|
||||
<div className="warning" style={{ textAlign: "center" }}>
|
||||
NSFW or Adult Content
|
||||
<div className="warning" style={{ textAlign: 'center' }}>
|
||||
NSFW Content Warning
|
||||
</div>
|
||||
<br />
|
||||
You are trying to access a user profile (npub) or a tag that is marked
|
||||
as NSFW (Not Safe For Work). This means that the content you are about
|
||||
to see may be offensive or inappropriate for some users. If you are
|
||||
under 18 years old, please do not proceed.
|
||||
You are attempting to access a user profile (npub) or a tag that has been flagged as NSFW (Not Safe For Work).
|
||||
This indicates that the content you are about to view might be offensive or inappropriate for certain users. If
|
||||
you are under 18 years old, we kindly request that you refrain from proceeding further.
|
||||
</div>
|
||||
<div className="disclaimer-footer">
|
||||
<button type="submit" className="btn" onClick={proceed}>
|
||||
Continue to content
|
||||
</button>
|
||||
Proceed Anyway
|
||||
</button>
|
||||
|
||||
<button type="submit" className="btn btn-primary" onClick={goBack}>
|
||||
Go back
|
||||
</button>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import "./SlideShow.css";
|
||||
import useImageLoaded from "../utils/useImageLoaded";
|
||||
import './SlideShow.css';
|
||||
import useImageLoaded from '../utils/useImageLoaded';
|
||||
|
||||
type AvatarImageProps = {
|
||||
src?: string;
|
||||
|
@ -30,7 +30,6 @@
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
|
||||
.disclaimer .disclaimer-footer {
|
||||
flex-shrink: 1;
|
||||
display: flex;
|
||||
|
@ -1,14 +1,11 @@
|
||||
import "./Disclaimer.css";
|
||||
import './Disclaimer.css';
|
||||
|
||||
type DisclaimerProps = {
|
||||
disclaimerAccepted: boolean;
|
||||
setDisclaimerAccepted: (accepted: boolean) => void;
|
||||
};
|
||||
|
||||
const Disclaimer = ({
|
||||
disclaimerAccepted,
|
||||
setDisclaimerAccepted,
|
||||
}: DisclaimerProps) => {
|
||||
const Disclaimer = ({ disclaimerAccepted, setDisclaimerAccepted }: DisclaimerProps) => {
|
||||
const onSubmit = () => {
|
||||
setDisclaimerAccepted(true);
|
||||
};
|
||||
@ -20,18 +17,15 @@ const Disclaimer = ({
|
||||
return (
|
||||
<div className="disclaimer">
|
||||
<div className="disclaimer-content">
|
||||
<div className="warning" style={{ textAlign: "center" }}>
|
||||
<div className="warning" style={{ textAlign: 'center' }}>
|
||||
Warning!
|
||||
</div>
|
||||
<br />
|
||||
The content presented on this site is <b>entirely user-generated</b> and
|
||||
remains <b>unmoderated</b>. Images and videos are sourced from the NOSTR
|
||||
platform and are not hosted on this site. Content filtering efforts are
|
||||
made to avoid NSFW (Not Safe For Work) content, but we cannot guarantee
|
||||
complete safety. Please use discretion and be responsible while engaging
|
||||
with the material on this platform. By using this site, you agree not to
|
||||
hold the site owners, operators, and affiliates liable for any
|
||||
content-related experiences.
|
||||
The content presented on this site is <b>entirely user-generated</b> and remains <b>unmoderated</b>. Images and
|
||||
videos are sourced from the NOSTR platform and are not hosted on this site. Content filtering efforts are made
|
||||
to avoid NSFW (Not Safe For Work) content, but we cannot guarantee complete safety. Please use discretion and be
|
||||
responsible while engaging with the material on this platform. By using this site, you agree not to hold the
|
||||
site owners, operators, and affiliates liable for any content-related experiences.
|
||||
</div>
|
||||
<div className="disclaimer-footer">
|
||||
<button type="submit" className="btn btn-primary" onClick={onSubmit}>
|
||||
|
@ -19,13 +19,11 @@
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.imagegrid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.imagegrid .image:hover {
|
||||
filter: brightness(1.1);
|
||||
outline: 1px solid #fff;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import Settings from "../Settings";
|
||||
import { NostrImage } from "../nostrImageDownload";
|
||||
import "./GridView.css";
|
||||
import Slide from "../SlideView/Slide";
|
||||
import { useMemo, useState } from 'react';
|
||||
import Settings from '../Settings';
|
||||
import { NostrImage } from '../nostrImageDownload';
|
||||
import './GridView.css';
|
||||
import Slide from '../SlideView/Slide';
|
||||
|
||||
type GridViewProps = {
|
||||
settings: Settings;
|
||||
@ -10,24 +10,23 @@ type GridViewProps = {
|
||||
};
|
||||
|
||||
const isVideo = (url: string) => {
|
||||
return url.endsWith(".mp4") || url.endsWith(".webm");
|
||||
return url.endsWith('.mp4') || url.endsWith('.webm');
|
||||
};
|
||||
|
||||
const addProxy = (url: string) => {
|
||||
if (
|
||||
url.includes("imgur.com") ||
|
||||
url.includes("cdn.midjourney.com") ||
|
||||
url.includes("wasabisys.com") ||
|
||||
url.includes("files.mastodon.social") ||
|
||||
url.includes("files.mastodon.online") ||
|
||||
url.includes("media.mastodon.scot") ||
|
||||
url.includes("media.mas.to") ||
|
||||
url.includes("smutlandia.com") ||
|
||||
url.includes("file.misskey.design")
|
||||
|
||||
url.includes('imgur.com') ||
|
||||
url.includes('cdn.midjourney.com') ||
|
||||
url.includes('wasabisys.com') ||
|
||||
url.includes('files.mastodon.social') ||
|
||||
url.includes('files.mastodon.online') ||
|
||||
url.includes('media.mastodon.scot') ||
|
||||
url.includes('media.mas.to') ||
|
||||
url.includes('smutlandia.com') ||
|
||||
url.includes('file.misskey.design')
|
||||
)
|
||||
return url;
|
||||
return "https://imgproxy.iris.to/insecure/rs:fill:200:200/plain/" + url;
|
||||
return 'https://imgproxy.iris.to/insecure/rs:fill:200:200/plain/' + url;
|
||||
};
|
||||
|
||||
const GridView = ({ settings, images }: GridViewProps) => {
|
||||
@ -36,7 +35,7 @@ const GridView = ({ settings, images }: GridViewProps) => {
|
||||
const sortedImages = useMemo(
|
||||
() =>
|
||||
images
|
||||
.filter((i) => !isVideo(i.url)) // TODO: filter out video for now, since we don't have a good way to display them
|
||||
.filter(i => !isVideo(i.url)) // TODO: filter out video for now, since we don't have a good way to display them
|
||||
.sort((a, b) => b.timestamp - a.timestamp), // sort by timestamp descending
|
||||
[images]
|
||||
);
|
||||
@ -54,7 +53,7 @@ const GridView = ({ settings, images }: GridViewProps) => {
|
||||
></Slide>
|
||||
)}
|
||||
<div className="imagegrid">
|
||||
{sortedImages.map((image) =>
|
||||
{sortedImages.map(image =>
|
||||
isVideo(image.url) ? (
|
||||
<video
|
||||
className="image"
|
||||
|
@ -1,11 +1,6 @@
|
||||
const IconFullScreen = () => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="1em"
|
||||
viewBox="0 0 448 512"
|
||||
fill="white"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512" fill="white">
|
||||
<path d="M32 32C14.3 32 0 46.3 0 64v96c0 17.7 14.3 32 32 32s32-14.3 32-32V96h64c17.7 0 32-14.3 32-32s-14.3-32-32-32H32zM64 352c0-17.7-14.3-32-32-32s-32 14.3-32 32v96c0 17.7 14.3 32 32 32h96c17.7 0 32-14.3 32-32s-14.3-32-32-32H64V352zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32h64v64c0 17.7 14.3 32 32 32s32-14.3 32-32V64c0-17.7-14.3-32-32-32H320zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32v64H320c-17.7 0-32 14.3-32 32s14.3 32 32 32h96c17.7 0 32-14.3 32-32V352z" />
|
||||
</svg>
|
||||
);
|
||||
|
@ -57,7 +57,7 @@
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.settings .settings-content input[type="text"],
|
||||
.settings .settings-content input[type='text'],
|
||||
.settings .settings-content textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FormEvent, useState } from "react";
|
||||
import "./Settings.css";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { FormEvent, useState } from 'react';
|
||||
import './Settings.css';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
type Settings = {
|
||||
showNsfw: boolean;
|
||||
@ -22,13 +22,13 @@ const Settings = ({ onClose, settings }: SettingsProps) => {
|
||||
|
||||
const onSubmit = (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
const nsfwPostfix = showNsfw ? "?nsfw=true" : "";
|
||||
const nsfwPostfix = showNsfw ? '?nsfw=true' : '';
|
||||
|
||||
const validTags = tags.filter((t) => t.length > 0);
|
||||
const validNpubs = npubs.filter((t) => t.length > 0);
|
||||
const validTags = tags.filter(t => t.length > 0);
|
||||
const validNpubs = npubs.filter(t => t.length > 0);
|
||||
|
||||
if (validTags.length > 0) {
|
||||
navigate(`/tags/${validTags.join("%2C")}${nsfwPostfix}`);
|
||||
navigate(`/tags/${validTags.join('%2C')}${nsfwPostfix}`);
|
||||
} else if (validNpubs.length == 1) {
|
||||
navigate(`/p/${validNpubs[0]}${nsfwPostfix}`);
|
||||
} else {
|
||||
@ -38,7 +38,7 @@ const Settings = ({ onClose, settings }: SettingsProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="settings" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="settings" onClick={e => e.stopPropagation()}>
|
||||
<h2>Settings</h2>
|
||||
|
||||
<div className="settings-content">
|
||||
@ -47,12 +47,8 @@ const Settings = ({ onClose, settings }: SettingsProps) => {
|
||||
name="tags"
|
||||
rows={4}
|
||||
id="tags"
|
||||
value={tags.join(", ")}
|
||||
onChange={(e) =>
|
||||
setTags(
|
||||
e.target.value.split(",").map((t) => t.trim().toLowerCase())
|
||||
)
|
||||
}
|
||||
value={tags.join(', ')}
|
||||
onChange={e => setTags(e.target.value.split(',').map(t => t.trim().toLowerCase()))}
|
||||
></textarea>
|
||||
|
||||
<label htmlFor="npub">User Profile (Npub):</label>
|
||||
@ -60,28 +56,15 @@ const Settings = ({ onClose, settings }: SettingsProps) => {
|
||||
type="text"
|
||||
name="npub"
|
||||
id="npub"
|
||||
value={npubs.join(", ")}
|
||||
onChange={(e) =>
|
||||
setNpubs(
|
||||
e.target.value.split(",").map((t) => t.trim().toLowerCase())
|
||||
)
|
||||
}
|
||||
value={npubs.join(', ')}
|
||||
onChange={e => setNpubs(e.target.value.split(',').map(t => t.trim().toLowerCase()))}
|
||||
/>
|
||||
|
||||
<div className="content-warning">
|
||||
<div>
|
||||
<input
|
||||
name="nsfw"
|
||||
type="checkbox"
|
||||
checked={showNsfw}
|
||||
onChange={(e) => setShowNsfw(e.target.checked)}
|
||||
/>
|
||||
<input name="nsfw" type="checkbox" checked={showNsfw} onChange={e => setShowNsfw(e.target.checked)} />
|
||||
</div>
|
||||
<label
|
||||
htmlFor="nsfw"
|
||||
onClick={() => setShowNsfw((n) => !n)}
|
||||
style={{ userSelect: "none" }}
|
||||
>
|
||||
<label htmlFor="nsfw" onClick={() => setShowNsfw(n => !n)} style={{ userSelect: 'none' }}>
|
||||
<div className="warning">NSFW Content</div>
|
||||
Allow NSFW to be shown and ignore content warnings.
|
||||
</label>
|
||||
|
@ -153,11 +153,7 @@
|
||||
z-index: 200;
|
||||
height: 150px;
|
||||
padding-top: 120px;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(0, 0, 0, 0) 0%,
|
||||
rgba(0, 0, 0, 1) 100%
|
||||
);
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 100%);
|
||||
padding-left: 100px;
|
||||
opacity: 0;
|
||||
width: 100%;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useNDK } from "@nostr-dev-kit/ndk-react";
|
||||
import "./SlideShow.css";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useNDK } from '@nostr-dev-kit/ndk-react';
|
||||
import './SlideShow.css';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
NostrImage,
|
||||
buildFilter,
|
||||
@ -9,46 +9,33 @@ import {
|
||||
hasNsfwTag,
|
||||
isReply,
|
||||
prepareContent,
|
||||
} from "./nostrImageDownload";
|
||||
import { nfswTags, nsfwNPubs, nsfwPubKeys } from "./env";
|
||||
import Settings from "./Settings";
|
||||
import SlideView from "./SlideView";
|
||||
import GridView from "./GridView";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import IconFullScreen from "./IconFullScreen";
|
||||
import { uniqBy } from "lodash";
|
||||
import AdultContentInfo from "./AdultContentInfo";
|
||||
} from './nostrImageDownload';
|
||||
import { nfswTags, nsfwNPubs, nsfwPubKeys } from './env';
|
||||
import Settings from './Settings';
|
||||
import SlideView from './SlideView';
|
||||
import GridView from './GridView';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import IconFullScreen from './IconFullScreen';
|
||||
import uniqBy from 'lodash/uniqBy';
|
||||
import AdultContentInfo from './AdultContentInfo';
|
||||
|
||||
/*
|
||||
FEATURES:
|
||||
- dedupe urls
|
||||
- controls lighter
|
||||
- info for nsfw acc / tags
|
||||
- preview for videos
|
||||
- details view for the grid
|
||||
|
||||
|
||||
--------
|
||||
- show tags
|
||||
- show content text (how to beautify?, crop?)
|
||||
- show tags
|
||||
- preview for videos
|
||||
- jump to note
|
||||
- negative hashtag filter
|
||||
- login to use your own feed
|
||||
- login to use your your blocked/muted list
|
||||
- Keypoard shortcuts, arrow, spacebar
|
||||
- jump tu next image
|
||||
- jump to previous image????
|
||||
- pause?
|
||||
- Save-Mode and block NSFW content??
|
||||
- Block certain authors / npbs? Maybe trust lookup @ nostr.band?
|
||||
- Add warning start page with localStorge to remember
|
||||
- Add config/settigns dialog
|
||||
- Support people lists and note lists
|
||||
- flag/mute button?
|
||||
- Add to album button? Favorite button?
|
||||
- Prevent duplicates (shuffle?), prevent same author twice in a row
|
||||
- show content warning?
|
||||
- Support Deleted Events
|
||||
- Support reposts and replies (incl. filter in settings)
|
||||
- Prevent duplicate images (shuffle? histroy?)
|
||||
*/
|
||||
|
||||
@ -67,17 +54,15 @@ const SlideShow = (settings: Settings) => {
|
||||
const fetch = () => {
|
||||
eventsReceived = 0;
|
||||
|
||||
const postSubscription = ndk.subscribe(
|
||||
buildFilter(settings.tags, settings.npubs)
|
||||
);
|
||||
const postSubscription = ndk.subscribe(buildFilter(settings.tags, settings.npubs));
|
||||
|
||||
postSubscription.on("event", (event) => {
|
||||
postSubscription.on('event', event => {
|
||||
eventsReceived++;
|
||||
|
||||
setPosts((oldPosts) => {
|
||||
setPosts(oldPosts => {
|
||||
if (
|
||||
!isReply(event) &&
|
||||
oldPosts.findIndex((p) => p.id === event.id) === -1 &&
|
||||
oldPosts.findIndex(p => p.id === event.id) === -1 &&
|
||||
(settings.showNsfw ||
|
||||
(!hasContentWarning(event) && // only allow content warnings on profile content
|
||||
!hasNsfwTag(event) && // only allow nsfw on profile content
|
||||
@ -89,8 +74,8 @@ const SlideShow = (settings: Settings) => {
|
||||
});
|
||||
});
|
||||
|
||||
postSubscription.on("notice", (notice) => {
|
||||
console.log("NOTICE: ", notice);
|
||||
postSubscription.on('notice', notice => {
|
||||
console.log('NOTICE: ', notice);
|
||||
});
|
||||
|
||||
return () => {
|
||||
@ -100,15 +85,15 @@ const SlideShow = (settings: Settings) => {
|
||||
|
||||
useEffect(() => {
|
||||
loadNdk([
|
||||
"wss://relay.damus.io",
|
||||
"wss://relay.nostr.band",
|
||||
"wss://nos.lol",
|
||||
"wss://relay.mostr.pub",
|
||||
"wss://relay.shitforce.one/",
|
||||
'wss://relay.damus.io',
|
||||
'wss://relay.nostr.band',
|
||||
'wss://nos.lol',
|
||||
'wss://relay.mostr.pub',
|
||||
'wss://relay.shitforce.one/',
|
||||
|
||||
//"wss://nostr.wine",
|
||||
// "wss://nostr1.current.fyi/",
|
||||
"wss://purplepag.es/", // needed for user profiles
|
||||
'wss://purplepag.es/', // needed for user profiles
|
||||
//"wss://feeds.nostr.band/pics",
|
||||
]);
|
||||
}, []);
|
||||
@ -127,32 +112,29 @@ const SlideShow = (settings: Settings) => {
|
||||
|
||||
useEffect(() => {
|
||||
images.current = uniqBy(
|
||||
posts.flatMap((p) => {
|
||||
posts.flatMap(p => {
|
||||
return extractImageUrls(p.content)
|
||||
.filter(
|
||||
(url) =>
|
||||
url.endsWith(".jpg") ||
|
||||
url.endsWith(".png") ||
|
||||
url.endsWith(".gif") ||
|
||||
url.endsWith(".jpeg") ||
|
||||
url.endsWith(".webp") ||
|
||||
url.endsWith(".webm") ||
|
||||
url.endsWith(".mp4")
|
||||
url =>
|
||||
url.endsWith('.jpg') ||
|
||||
url.endsWith('.png') ||
|
||||
url.endsWith('.gif') ||
|
||||
url.endsWith('.jpeg') ||
|
||||
url.endsWith('.webp') ||
|
||||
url.endsWith('.webm') ||
|
||||
url.endsWith('.mp4')
|
||||
)
|
||||
.map((url) => ({
|
||||
.map(url => ({
|
||||
url,
|
||||
author: p.author.npub,
|
||||
content: prepareContent(p.content),
|
||||
type:
|
||||
url.endsWith(".mp4") || url.endsWith(".webm") ? "video" : "image",
|
||||
type: url.endsWith('.mp4') || url.endsWith('.webm') ? 'video' : 'image',
|
||||
timestamp: p.created_at,
|
||||
noteId: 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"
|
||||
'url'
|
||||
);
|
||||
console.log(images.current.length);
|
||||
}, [posts]);
|
||||
@ -160,11 +142,11 @@ const SlideShow = (settings: Settings) => {
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (showSettings) return;
|
||||
|
||||
if (event.key === "g" || event.key === "G") {
|
||||
setShowGrid((p) => !p);
|
||||
if (event.key === 'g' || event.key === 'G') {
|
||||
setShowGrid(p => !p);
|
||||
}
|
||||
if (event.key === "Escape") {
|
||||
setShowSettings((s) => !s);
|
||||
if (event.key === 'Escape') {
|
||||
setShowSettings(s => !s);
|
||||
}
|
||||
/*
|
||||
if (event.key === "f" || event.key === "F") {
|
||||
@ -174,18 +156,17 @@ const SlideShow = (settings: Settings) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keydown", onKeyDown);
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
return () => {
|
||||
window.removeEventListener("keydown", onKeyDown);
|
||||
window.removeEventListener('keydown', onKeyDown);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const fullScreen = document.fullscreenElement !== null;
|
||||
|
||||
const showAdultContentWarning =
|
||||
!settings.showNsfw && (
|
||||
nfswTags.some((t) => settings.tags.includes(t)) ||
|
||||
nsfwNPubs.some((p) => settings.npubs.includes(p)));
|
||||
!settings.showNsfw &&
|
||||
(nfswTags.some(t => settings.tags.includes(t)) || nsfwNPubs.some(p => settings.npubs.includes(p)));
|
||||
|
||||
if (showAdultContentWarning) {
|
||||
return <AdultContentInfo></AdultContentInfo>;
|
||||
@ -193,53 +174,29 @@ const SlideShow = (settings: Settings) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{showSettings && (
|
||||
<Settings
|
||||
onClose={() => setShowSettings(false)}
|
||||
settings={settings}
|
||||
></Settings>
|
||||
)}
|
||||
{showSettings && <Settings onClose={() => setShowSettings(false)} settings={settings}></Settings>}
|
||||
|
||||
<div className="controls">
|
||||
<button
|
||||
onClick={() => setShowGrid((g) => !g)}
|
||||
title={showGrid ? "Play random slideshow (G)" : "view grid (G)"}
|
||||
>
|
||||
<button onClick={() => setShowGrid(g => !g)} title={showGrid ? 'Play random slideshow (G)' : 'view grid (G)'}>
|
||||
{showGrid ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="1em"
|
||||
viewBox="0 0 384 512"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 384 512">
|
||||
<path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="1em"
|
||||
viewBox="0 0 448 512"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512">
|
||||
<path d="M128 136c0-22.1-17.9-40-40-40L40 96C17.9 96 0 113.9 0 136l0 48c0 22.1 17.9 40 40 40H88c22.1 0 40-17.9 40-40l0-48zm0 192c0-22.1-17.9-40-40-40H40c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40H88c22.1 0 40-17.9 40-40V328zm32-192v48c0 22.1 17.9 40 40 40h48c22.1 0 40-17.9 40-40V136c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40zM288 328c0-22.1-17.9-40-40-40H200c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40h48c22.1 0 40-17.9 40-40V328zm32-192v48c0 22.1 17.9 40 40 40h48c22.1 0 40-17.9 40-40V136c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40zM448 328c0-22.1-17.9-40-40-40H360c-22.1 0-40 17.9-40 40v48c0 22.1 17.9 40 40 40h48c22.1 0 40-17.9 40-40V328z" />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
|
||||
<button onClick={() => setShowSettings((s) => !s)}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="1em"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<button onClick={() => setShowSettings(s => !s)}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512">
|
||||
<path d="M495.9 166.6c3.2 8.7 .5 18.4-6.4 24.6l-43.3 39.4c1.1 8.3 1.7 16.8 1.7 25.4s-.6 17.1-1.7 25.4l43.3 39.4c6.9 6.2 9.6 15.9 6.4 24.6c-4.4 11.9-9.7 23.3-15.8 34.3l-4.7 8.1c-6.6 11-14 21.4-22.1 31.2c-5.9 7.2-15.7 9.6-24.5 6.8l-55.7-17.7c-13.4 10.3-28.2 18.9-44 25.4l-12.5 57.1c-2 9.1-9 16.3-18.2 17.8c-13.8 2.3-28 3.5-42.5 3.5s-28.7-1.2-42.5-3.5c-9.2-1.5-16.2-8.7-18.2-17.8l-12.5-57.1c-15.8-6.5-30.6-15.1-44-25.4L83.1 425.9c-8.8 2.8-18.6 .3-24.5-6.8c-8.1-9.8-15.5-20.2-22.1-31.2l-4.7-8.1c-6.1-11-11.4-22.4-15.8-34.3c-3.2-8.7-.5-18.4 6.4-24.6l43.3-39.4C64.6 273.1 64 264.6 64 256s.6-17.1 1.7-25.4L22.4 191.2c-6.9-6.2-9.6-15.9-6.4-24.6c4.4-11.9 9.7-23.3 15.8-34.3l4.7-8.1c6.6-11 14-21.4 22.1-31.2c5.9-7.2 15.7-9.6 24.5-6.8l55.7 17.7c13.4-10.3 28.2-18.9 44-25.4l12.5-57.1c2-9.1 9-16.3 18.2-17.8C227.3 1.2 241.5 0 256 0s28.7 1.2 42.5 3.5c9.2 1.5 16.2 8.7 18.2 17.8l12.5 57.1c15.8 6.5 30.6 15.1 44 25.4l55.7-17.7c8.8-2.8 18.6-.3 24.5 6.8c8.1 9.8 15.5 20.2 22.1 31.2l4.7 8.1c6.1 11 11.4 22.4 15.8 34.3zM256 336a80 80 0 1 0 0-160 80 80 0 1 0 0 160z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{!fullScreen && (
|
||||
<button
|
||||
onClick={() =>
|
||||
document?.getElementById("root")?.requestFullscreen()
|
||||
}
|
||||
>
|
||||
<button onClick={() => document?.getElementById('root')?.requestFullscreen()}>
|
||||
<IconFullScreen />
|
||||
</button>
|
||||
)}
|
||||
|
@ -1,24 +1,17 @@
|
||||
import { useEffect } from "react";
|
||||
import SlideImage from "./SlideImage";
|
||||
import SlideVideo from "./SlideVideo";
|
||||
import { useEffect } from 'react';
|
||||
import SlideImage from './SlideImage';
|
||||
import SlideVideo from './SlideVideo';
|
||||
|
||||
type SlideProps = {
|
||||
url: string;
|
||||
paused: boolean;
|
||||
type: "image" | "video";
|
||||
type: 'image' | 'video';
|
||||
onAnimationEnded?: () => void;
|
||||
animationDuration?: number;
|
||||
noteId: string;
|
||||
};
|
||||
|
||||
const Slide = ({
|
||||
url,
|
||||
paused,
|
||||
type,
|
||||
noteId,
|
||||
onAnimationEnded,
|
||||
animationDuration = 12,
|
||||
}: SlideProps) => {
|
||||
const Slide = ({ url, paused, type, noteId, onAnimationEnded, animationDuration = 12 }: SlideProps) => {
|
||||
useEffect(() => {
|
||||
const handle = setTimeout(() => {
|
||||
onAnimationEnded && onAnimationEnded();
|
||||
@ -28,20 +21,10 @@ const Slide = ({
|
||||
};
|
||||
}, []);
|
||||
|
||||
return type === "image" ? (
|
||||
<SlideImage
|
||||
url={url}
|
||||
noteId={noteId}
|
||||
paused={paused}
|
||||
style={{ animationDuration: `${animationDuration}s` }}
|
||||
/>
|
||||
return type === 'image' ? (
|
||||
<SlideImage url={url} noteId={noteId} paused={paused} style={{ animationDuration: `${animationDuration}s` }} />
|
||||
) : (
|
||||
<SlideVideo
|
||||
url={url}
|
||||
noteId={noteId}
|
||||
paused={paused}
|
||||
style={{ animationDuration: `${animationDuration}s` }}
|
||||
/>
|
||||
<SlideVideo url={url} noteId={noteId} paused={paused} style={{ animationDuration: `${animationDuration}s` }} />
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import useImageLoaded from "../../utils/useImageLoaded";
|
||||
import useImageLoaded from '../../utils/useImageLoaded';
|
||||
|
||||
type SlideImageProps = {
|
||||
url: string;
|
||||
@ -12,7 +12,7 @@ const SlideImage = ({ url, paused, style, noteId }: SlideImageProps) => {
|
||||
return (
|
||||
loaded && (
|
||||
<div
|
||||
className={`slide ${paused ? "paused" : ""}`}
|
||||
className={`slide ${paused ? 'paused' : ''}`}
|
||||
data-node-id={noteId}
|
||||
style={{
|
||||
backgroundImage: `url(${url})`,
|
||||
|
@ -7,7 +7,7 @@ type SlideVideoProps = {
|
||||
|
||||
const SlideVideo = ({ url, paused, style, noteId }: SlideVideoProps) => {
|
||||
return (
|
||||
<div className={`slide ${paused ? "paused" : ""}`} style={style}>
|
||||
<div className={`slide ${paused ? 'paused' : ''}`} style={style}>
|
||||
<video src={url} autoPlay loop muted playsInline data-node-id={noteId} />
|
||||
</div>
|
||||
);
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import AuthorProfile from "../AuthorProfile";
|
||||
import Slide from "./Slide";
|
||||
import { NostrImage, urlFix } from "../nostrImageDownload";
|
||||
import { appName } from "../env";
|
||||
import { useNDK } from "@nostr-dev-kit/ndk-react";
|
||||
import useDebouncedEffect from "../../utils/useDebouncedEffect";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import { Helmet } from "react-helmet";
|
||||
import Settings from "../Settings";
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import AuthorProfile from '../AuthorProfile';
|
||||
import Slide from './Slide';
|
||||
import { NostrImage, urlFix } from '../nostrImageDownload';
|
||||
import { appName } from '../env';
|
||||
import { useNDK } from '@nostr-dev-kit/ndk-react';
|
||||
import useDebouncedEffect from '../../utils/useDebouncedEffect';
|
||||
import { useSwipeable } from 'react-swipeable';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Settings from '../Settings';
|
||||
|
||||
type SlideViewProps = {
|
||||
settings: Settings;
|
||||
@ -25,13 +25,11 @@ const SlideView = ({ settings, images }: SlideViewProps) => {
|
||||
const [title, setTitle] = useState(appName);
|
||||
const [activeNpub, setActiveNpub] = useState<string | undefined>(undefined);
|
||||
const [slideShowStarted, setSlideShowStarted] = useState(false);
|
||||
const [activeContent, setActiveContent] = useState<string | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [activeContent, setActiveContent] = useState<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (settings.tags && settings.tags.length > 0) {
|
||||
setTitle("#" + settings.tags.join(" #") + ` | ${appName}`);
|
||||
setTitle('#' + settings.tags.join(' #') + ` | ${appName}`);
|
||||
} else {
|
||||
setTitle(`Random photos from popular hashtags | ${appName}`);
|
||||
}
|
||||
@ -81,7 +79,7 @@ const SlideView = ({ settings, images }: SlideViewProps) => {
|
||||
const animateImages = () => {
|
||||
console.log(`animateImages ${images.length}`);
|
||||
|
||||
setActiveImages((activeImages) => {
|
||||
setActiveImages(activeImages => {
|
||||
const newActiveImages = [...activeImages];
|
||||
console.log(`newActiveImages = ${JSON.stringify(newActiveImages)}`);
|
||||
if (newActiveImages.length > 2) {
|
||||
@ -94,7 +92,7 @@ const SlideView = ({ settings, images }: SlideViewProps) => {
|
||||
const randomImage = images[Math.floor(Math.random() * images.length)];
|
||||
console.log(`randomImage = ${randomImage.url}`);
|
||||
// TODO this creates potential duplicates when images are loaded from multiple relays
|
||||
images = images.filter((i) => i !== randomImage);
|
||||
images = images.filter(i => i !== randomImage);
|
||||
|
||||
history.current.push(randomImage);
|
||||
newActiveImages.push(randomImage);
|
||||
@ -105,34 +103,32 @@ const SlideView = ({ settings, images }: SlideViewProps) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
console.log(
|
||||
`slideShowStarted = ${slideShowStarted}, images = ${images.length}`
|
||||
);
|
||||
console.log(`slideShowStarted = ${slideShowStarted}, images = ${images.length}`);
|
||||
// Make sure we have an image to start with but only trigger once
|
||||
if (!slideShowStarted && images.length > 2) {
|
||||
setSlideShowStarted(true);
|
||||
console.log("******* queueNextImage");
|
||||
console.log('******* queueNextImage');
|
||||
queueNextImage(10);
|
||||
}
|
||||
}, [images]);
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
// console.log(event);
|
||||
if (event.key === "ArrowRight") {
|
||||
if (event.key === 'ArrowRight') {
|
||||
nextImage();
|
||||
}
|
||||
if (event.key === "ArrowLeft") {
|
||||
if (event.key === 'ArrowLeft') {
|
||||
previousImage();
|
||||
}
|
||||
if (event.key === "p" || event.key === " " || event.key === "P") {
|
||||
setPaused((p) => !p);
|
||||
if (event.key === 'p' || event.key === ' ' || event.key === 'P') {
|
||||
setPaused(p => !p);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.body.addEventListener("keydown", onKeyDown);
|
||||
document.body.addEventListener('keydown', onKeyDown);
|
||||
return () => {
|
||||
window.removeEventListener("keydown", onKeyDown);
|
||||
window.removeEventListener('keydown', onKeyDown);
|
||||
console.log(`cleaining timeout in useEffect[] destructor `);
|
||||
clearTimeout(viewTimeoutHandle.current);
|
||||
};
|
||||
@ -161,34 +157,20 @@ const SlideView = ({ settings, images }: SlideViewProps) => {
|
||||
const activeProfile = activeNpub && getProfile(activeNpub);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
settings.npubs.length>0 &&
|
||||
activeProfile &&
|
||||
(activeProfile.displayName || activeProfile.name)
|
||||
) {
|
||||
setTitle(
|
||||
activeProfile.displayName || activeProfile.name + ` | ${appName}`
|
||||
);
|
||||
if (settings.npubs.length > 0 && activeProfile && (activeProfile.displayName || activeProfile.name)) {
|
||||
setTitle(activeProfile.displayName || activeProfile.name + ` | ${appName}`);
|
||||
}
|
||||
}, [activeProfile]);
|
||||
|
||||
return (
|
||||
<div
|
||||
{...swipeHandlers}
|
||||
onClick={() => setPaused((p) => !p)}
|
||||
style={{ overflow: "hidden" }}
|
||||
>
|
||||
<div {...swipeHandlers} onClick={() => setPaused(p => !p)} style={{ overflow: 'hidden' }}>
|
||||
<Helmet>
|
||||
<title>{title}</title>
|
||||
</Helmet>
|
||||
|
||||
{paused && (
|
||||
<div className="centerSymbol">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="1em"
|
||||
viewBox="0 0 320 512"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 320 512">
|
||||
<path d="M48 64C21.5 64 0 85.5 0 112V400c0 26.5 21.5 48 48 48H80c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48H48zm192 0c-26.5 0-48 21.5-48 48V400c0 26.5 21.5 48 48 48h32c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48H240z" />
|
||||
</svg>
|
||||
</div>
|
||||
@ -196,11 +178,7 @@ const SlideView = ({ settings, images }: SlideViewProps) => {
|
||||
|
||||
{loading && (
|
||||
<div className="centerSymbol spin">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="1em"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512">
|
||||
<path d="M256 96c38.4 0 73.7 13.5 101.3 36.1l-32.6 32.6c-4.6 4.6-5.9 11.5-3.5 17.4s8.3 9.9 14.8 9.9H448c8.8 0 16-7.2 16-16V64c0-6.5-3.9-12.3-9.9-14.8s-12.9-1.1-17.4 3.5l-34 34C363.4 52.6 312.1 32 256 32c-10.9 0-21.5 .8-32 2.3V99.2c10.3-2.1 21-3.2 32-3.2zM132.1 154.7l32.6 32.6c4.6 4.6 11.5 5.9 17.4 3.5s9.9-8.3 9.9-14.8V64c0-8.8-7.2-16-16-16H64c-6.5 0-12.3 3.9-14.8 9.9s-1.1 12.9 3.5 17.4l34 34C52.6 148.6 32 199.9 32 256c0 10.9 .8 21.5 2.3 32H99.2c-2.1-10.3-3.2-21-3.2-32c0-38.4 13.5-73.7 36.1-101.3zM477.7 224H412.8c2.1 10.3 3.2 21 3.2 32c0 38.4-13.5 73.7-36.1 101.3l-32.6-32.6c-4.6-4.6-11.5-5.9-17.4-3.5s-9.9 8.3-9.9 14.8V448c0 8.8 7.2 16 16 16H448c6.5 0 12.3-3.9 14.8-9.9s1.1-12.9-3.5-17.4l-34-34C459.4 363.4 480 312.1 480 256c0-10.9-.8-21.5-2.3-32zM256 416c-38.4 0-73.7-13.5-101.3-36.1l32.6-32.6c4.6-4.6 5.9-11.5 3.5-17.4s-8.3-9.9-14.8-9.9H64c-8.8 0-16 7.2-16 16l0 112c0 6.5 3.9 12.3 9.9 14.8s12.9 1.1 17.4-3.5l34-34C148.6 459.4 199.9 480 256 480c10.9 0 21.5-.8 32-2.3V412.8c-10.3 2.1-21 3.2-32 3.2z" />
|
||||
</svg>
|
||||
</div>
|
||||
@ -213,20 +191,14 @@ const SlideView = ({ settings, images }: SlideViewProps) => {
|
||||
)}
|
||||
{activeProfile && (
|
||||
<AuthorProfile
|
||||
src={urlFix(activeProfile.image || "")}
|
||||
src={urlFix(activeProfile.image || '')}
|
||||
author={activeProfile.displayName || activeProfile.name}
|
||||
npub={activeNpub}
|
||||
></AuthorProfile>
|
||||
)}
|
||||
|
||||
{activeImages.map((image) => (
|
||||
<Slide
|
||||
key={image.url}
|
||||
noteId={image.noteId}
|
||||
url={image.url}
|
||||
paused={paused}
|
||||
type={image.type}
|
||||
/>
|
||||
{activeImages.map(image => (
|
||||
<Slide key={image.url} noteId={image.noteId} url={image.url} paused={paused} type={image.type} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,91 +1,89 @@
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { nip19 } from 'nostr-tools';
|
||||
|
||||
export const appName = "slidestr.net";
|
||||
export const appName = 'slidestr.net';
|
||||
|
||||
export const defaultHashTags = [
|
||||
"art",
|
||||
"artstr",
|
||||
"catstr",
|
||||
"dogstr",
|
||||
"nature",
|
||||
"naturephotography",
|
||||
"photography",
|
||||
"photostr",
|
||||
"streetphotography",
|
||||
"tavelstr",
|
||||
"gardening",
|
||||
"gardenstr",
|
||||
'art',
|
||||
'artstr',
|
||||
'catstr',
|
||||
'dogstr',
|
||||
'nature',
|
||||
'naturephotography',
|
||||
'photography',
|
||||
'photostr',
|
||||
'streetphotography',
|
||||
'tavelstr',
|
||||
'gardening',
|
||||
'gardenstr',
|
||||
];
|
||||
|
||||
export const nfswTags = [
|
||||
"ass",
|
||||
"blowjob",
|
||||
"boobstr",
|
||||
"buttstr",
|
||||
"erostr",
|
||||
"erotic",
|
||||
"freethenipple",
|
||||
"friskyfriday",
|
||||
"humpday",
|
||||
"kink",
|
||||
"kinkstr",
|
||||
"milf",
|
||||
"naked",
|
||||
"nakedart",
|
||||
"nasstr",
|
||||
"nsfw",
|
||||
"nude",
|
||||
"nudeart",
|
||||
"orgasm",
|
||||
"pornhub",
|
||||
"pornstr",
|
||||
"pussy",
|
||||
"sex",
|
||||
"suicidegirls",
|
||||
"thighstr",
|
||||
"tits",
|
||||
"titstr",
|
||||
'ass',
|
||||
'blowjob',
|
||||
'boobstr',
|
||||
'buttstr',
|
||||
'erostr',
|
||||
'erotic',
|
||||
'freethenipple',
|
||||
'friskyfriday',
|
||||
'humpday',
|
||||
'kink',
|
||||
'kinkstr',
|
||||
'milf',
|
||||
'naked',
|
||||
'nakedart',
|
||||
'nasstr',
|
||||
'nsfw',
|
||||
'nude',
|
||||
'nudeart',
|
||||
'orgasm',
|
||||
'pornhub',
|
||||
'pornstr',
|
||||
'pussy',
|
||||
'sex',
|
||||
'suicidegirls',
|
||||
'thighstr',
|
||||
'tits',
|
||||
'titstr',
|
||||
];
|
||||
|
||||
export const nsfwNPubs = [
|
||||
"npub12jedfuhk2wfr7syr38t2f55652khuyz9f88r63ftm0j2vudxq9sqq7677r", // Erikha
|
||||
"npub13806pd9p833wkgyemeqddjzdksunlq9gszq4yjnhw4l57sjjhwlq6m79nj", // Orvalho
|
||||
"npub13n6ednsew67xk7hgse670z7849q5h8su5rgydxtl4lq3r5cx4ecqsd9af4", // Everybody, Every Body
|
||||
"npub177wu03dgx6zt9a9hdey079prfw5lvj5dhm20z4k96tf6um6zjk7qyzdumj", // Eoun
|
||||
"npub19xwjw7f23nsmnsd0j72mvhrdswt4cp6urc5el2zuu8se3yfu87ess524je", // Gone Wild (NSFW)
|
||||
"npub1af9lxfzeq5rxmu9zz7d85tn2ex8zvvlx0duqcemcdhkz9cvlt29st3rcgd", // Happy Nut
|
||||
"npub1e4n8nah09he25slv00dz3kav3jsu5jvp83aya234ejumcmu2xseqwrp6pl", // Svenno(NSFW)
|
||||
"npub1femd0mrawr0jmtjr2jwa2nm90haxrpglzdt6tt0djrsav39e53asf74aer", // FemDom Raw
|
||||
"npub1ga79p6qsjh0xd343q3du2unf2gl6gk0rde36c06mafxkrssmnnesxyzcss", // Orange Incest 🔞
|
||||
"npub1grssdyrmdgy5gw5umg50u6rrl9nk738lw4qg2thpcqqaf3lypkqsxt7lhg", // Shades of Red
|
||||
"npub1j0xvl8l2s4w25vcavf3jv6fgyakyc0hplxc9we8hc8mja0ct7epss0qlkj", // Thicc Pics (NSFW)
|
||||
"npub1j0y6f9gl9w39ggarr9x76lyh2swv7mpgddguv49mhmzqlz8tm69qcwpl55", // NeoMobius
|
||||
"npub1j2u3lfkhl95e6qwswr32hx6h36arlw8p2cl6hy0wgnmxmekrhx8qx93uvh", // Ay Papi
|
||||
"npub1jge7z2kpmpdra6g58vg95uznve8ctcenmlyp9ntr3kjymscyuqpqty2cdh", // Storm
|
||||
"npub1jjtzhxzu8dlf7yn480sz67tesnfl7gpzfpkgpez05d2z9y3lya5sxvky0y", // Selfie Girls
|
||||
"npub1jp9v034z3a26cp5hajwyuzl0hety5akdpwdnjaqgfd7pm2ts4dwsc29va8", // curatedbliss
|
||||
"npub1kade5vf37snr4hv5hgstav6j5ygry6z09kkq0flp47p8cmeuz5zs7zz2an", // Aeontropy
|
||||
"npub1klxseqx4et3grzgvajtlm47tz7tqmxygwj49kx3frsuls9cf8lhqhhhr5q", // Riley_R_Fan
|
||||
"npub1kul999wnt8gwa6l2vyuewhnmmp25gq7dly9zmgsw52x8csmqjgts7278rx", // 𝓟𝓮𝓽𝓲𝓽𝓮 𝓟𝓻𝓲𝓷𝓬𝓮𝓼𝓼
|
||||
"npub1m5fdz9gqa2qeudpy47zllmv9gqe3zzj44dkt9lh2kes3mlex7e6se348vy", // Marble Sculture
|
||||
"npub1mgusda7ujnyuhhudwkyrp763k4dd9xspktekl0tg5v0j76yph8ssyrfdpm", // anisyia
|
||||
"npub1nme4074q6yrqexdn5z625vhvv9j9e2qwwfcgdyg2utffhvdgrxfqn5ztgm", // Ay Papi
|
||||
"npub1pl0qa9x3n8wt55em0x3zwuy02rtl5t3jsretr0egjqgkx2f6jztqt0xwew", // nude
|
||||
"npub1rv08kght99a7xwckm0qpmzw09m5gwppequgqd8lwu74eakgaavwsp5cjtw", // CuratedNSFW
|
||||
"npub1suddec4n2jv50pgn9eea35r4k83ahr4mcj0zv2uec36w6jeuwagq82xjgl", // quiet.enjoyer
|
||||
"npub1t252vm7u5qmfwv3k70g6rl2ue7ctvtvrnd60vy8jh5suglv8pw2snyyzfq", // 20th Century Foxes (NSFW)
|
||||
"npub1thsprukxnc8rxqggnesqp2wg2temhaadzhhg7n4pttpveyqedlwsqgge9q", // Harmony. Corrupted.
|
||||
"npub1ulafm4d3n7ukl7yzg4hfnhfjut74nym5p83e3d67l3j62yc6ysqqrancw2", // naked
|
||||
"npub1ve4ztpqvlgu3v6hgrvc4lrdl2ernue7lq2h8tcgaksrkxlm7gnsqkjmz4e", // bluntkaraoke
|
||||
"npub1wmsn8fch7kwt987jcdx06uuapn6pwzau59pvy0ql5d3xlmnxa2csj3c5p4", // StefsPicks
|
||||
"npub1xfu7047thly6aghl79z97kckkvwfvtcx88n6wq7c2tlng484d8xqv0kuvv", // Erandis Vol
|
||||
"npub1y77j6jm5hw34xl5m85aumltv88arh2s7q383allkpfe4muarzc5qzfgru0", // sexy-models
|
||||
"npub1ylrnf0xfp9wsmqthxlqjqyqj9yy27pnchjwjq93v3mq66ts7ftjs6x7dcq", // Welcome To The Jungle
|
||||
"npub1csk2wg33ee9kutyps4nmevyv3putfegj7yd0emsp44ph32wvmamqs7uyan", // Lilura
|
||||
'npub12jedfuhk2wfr7syr38t2f55652khuyz9f88r63ftm0j2vudxq9sqq7677r', // Erikha
|
||||
'npub13806pd9p833wkgyemeqddjzdksunlq9gszq4yjnhw4l57sjjhwlq6m79nj', // Orvalho
|
||||
'npub13n6ednsew67xk7hgse670z7849q5h8su5rgydxtl4lq3r5cx4ecqsd9af4', // Everybody, Every Body
|
||||
'npub177wu03dgx6zt9a9hdey079prfw5lvj5dhm20z4k96tf6um6zjk7qyzdumj', // Eoun
|
||||
'npub19xwjw7f23nsmnsd0j72mvhrdswt4cp6urc5el2zuu8se3yfu87ess524je', // Gone Wild (NSFW)
|
||||
'npub1af9lxfzeq5rxmu9zz7d85tn2ex8zvvlx0duqcemcdhkz9cvlt29st3rcgd', // Happy Nut
|
||||
'npub1e4n8nah09he25slv00dz3kav3jsu5jvp83aya234ejumcmu2xseqwrp6pl', // Svenno(NSFW)
|
||||
'npub1femd0mrawr0jmtjr2jwa2nm90haxrpglzdt6tt0djrsav39e53asf74aer', // FemDom Raw
|
||||
'npub1ga79p6qsjh0xd343q3du2unf2gl6gk0rde36c06mafxkrssmnnesxyzcss', // Orange Incest 🔞
|
||||
'npub1grssdyrmdgy5gw5umg50u6rrl9nk738lw4qg2thpcqqaf3lypkqsxt7lhg', // Shades of Red
|
||||
'npub1j0xvl8l2s4w25vcavf3jv6fgyakyc0hplxc9we8hc8mja0ct7epss0qlkj', // Thicc Pics (NSFW)
|
||||
'npub1j0y6f9gl9w39ggarr9x76lyh2swv7mpgddguv49mhmzqlz8tm69qcwpl55', // NeoMobius
|
||||
'npub1j2u3lfkhl95e6qwswr32hx6h36arlw8p2cl6hy0wgnmxmekrhx8qx93uvh', // Ay Papi
|
||||
'npub1jge7z2kpmpdra6g58vg95uznve8ctcenmlyp9ntr3kjymscyuqpqty2cdh', // Storm
|
||||
'npub1jjtzhxzu8dlf7yn480sz67tesnfl7gpzfpkgpez05d2z9y3lya5sxvky0y', // Selfie Girls
|
||||
'npub1jp9v034z3a26cp5hajwyuzl0hety5akdpwdnjaqgfd7pm2ts4dwsc29va8', // curatedbliss
|
||||
'npub1kade5vf37snr4hv5hgstav6j5ygry6z09kkq0flp47p8cmeuz5zs7zz2an', // Aeontropy
|
||||
'npub1klxseqx4et3grzgvajtlm47tz7tqmxygwj49kx3frsuls9cf8lhqhhhr5q', // Riley_R_Fan
|
||||
'npub1kul999wnt8gwa6l2vyuewhnmmp25gq7dly9zmgsw52x8csmqjgts7278rx', // 𝓟𝓮𝓽𝓲𝓽𝓮 𝓟𝓻𝓲𝓷𝓬𝓮𝓼𝓼
|
||||
'npub1m5fdz9gqa2qeudpy47zllmv9gqe3zzj44dkt9lh2kes3mlex7e6se348vy', // Marble Sculture
|
||||
'npub1mgusda7ujnyuhhudwkyrp763k4dd9xspktekl0tg5v0j76yph8ssyrfdpm', // anisyia
|
||||
'npub1nme4074q6yrqexdn5z625vhvv9j9e2qwwfcgdyg2utffhvdgrxfqn5ztgm', // Ay Papi
|
||||
'npub1pl0qa9x3n8wt55em0x3zwuy02rtl5t3jsretr0egjqgkx2f6jztqt0xwew', // nude
|
||||
'npub1rv08kght99a7xwckm0qpmzw09m5gwppequgqd8lwu74eakgaavwsp5cjtw', // CuratedNSFW
|
||||
'npub1suddec4n2jv50pgn9eea35r4k83ahr4mcj0zv2uec36w6jeuwagq82xjgl', // quiet.enjoyer
|
||||
'npub1t252vm7u5qmfwv3k70g6rl2ue7ctvtvrnd60vy8jh5suglv8pw2snyyzfq', // 20th Century Foxes (NSFW)
|
||||
'npub1thsprukxnc8rxqggnesqp2wg2temhaadzhhg7n4pttpveyqedlwsqgge9q', // Harmony. Corrupted.
|
||||
'npub1ulafm4d3n7ukl7yzg4hfnhfjut74nym5p83e3d67l3j62yc6ysqqrancw2', // naked
|
||||
'npub1ve4ztpqvlgu3v6hgrvc4lrdl2ernue7lq2h8tcgaksrkxlm7gnsqkjmz4e', // bluntkaraoke
|
||||
'npub1wmsn8fch7kwt987jcdx06uuapn6pwzau59pvy0ql5d3xlmnxa2csj3c5p4', // StefsPicks
|
||||
'npub1xfu7047thly6aghl79z97kckkvwfvtcx88n6wq7c2tlng484d8xqv0kuvv', // Erandis Vol
|
||||
'npub1y77j6jm5hw34xl5m85aumltv88arh2s7q383allkpfe4muarzc5qzfgru0', // sexy-models
|
||||
'npub1ylrnf0xfp9wsmqthxlqjqyqj9yy27pnchjwjq93v3mq66ts7ftjs6x7dcq', // Welcome To The Jungle
|
||||
'npub1csk2wg33ee9kutyps4nmevyv3putfegj7yd0emsp44ph32wvmamqs7uyan', // Lilura
|
||||
];
|
||||
|
||||
export const nsfwPubKeys = nsfwNPubs.map((npub) =>
|
||||
(nip19.decode(npub).data as string).toLowerCase()
|
||||
);
|
||||
export const nsfwPubKeys = nsfwNPubs.map(npub => (nip19.decode(npub).data as string).toLowerCase());
|
||||
|
||||
export const spamAccounts = [];
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { NDKFilter } from "@nostr-dev-kit/ndk";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { nfswTags } from "./env";
|
||||
import { NDKFilter } from '@nostr-dev-kit/ndk';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { nfswTags } from './env';
|
||||
|
||||
export type NostrImage = {
|
||||
url: string;
|
||||
@ -9,7 +9,7 @@ export type NostrImage = {
|
||||
content?: string;
|
||||
timestamp: number;
|
||||
noteId: string;
|
||||
type: "image" | "video";
|
||||
type: 'image' | 'video';
|
||||
};
|
||||
|
||||
export const buildFilter = (tags: string[], npubs: string[]) => {
|
||||
@ -18,55 +18,47 @@ export const buildFilter = (tags: string[], npubs: string[]) => {
|
||||
};
|
||||
|
||||
if (npubs && npubs.length > 0) {
|
||||
filter.authors = npubs.map((p) => nip19.decode(p).data as string);
|
||||
filter.authors = npubs.map(p => nip19.decode(p).data as string);
|
||||
} else {
|
||||
if (tags && tags.length > 0) {
|
||||
filter["#t"] = tags;
|
||||
}
|
||||
filter['#t'] = tags;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("filter", filter);
|
||||
console.log('filter', filter);
|
||||
return filter;
|
||||
};
|
||||
|
||||
export const prepareContent = (content: string) => {
|
||||
return content
|
||||
.replace(/https?:\/\/[^\s]+/g, "") // remove all urls
|
||||
.replace(/#[^\s]+/g, ""); // remove all tags
|
||||
.replace(/https?:\/\/[^\s]+/g, '') // remove all urls
|
||||
.replace(/#[^\s]+/g, ''); // remove all tags
|
||||
};
|
||||
|
||||
export const urlFix = (url: string) => {
|
||||
// dont use cdn for mp4/webm
|
||||
if (url == undefined || url.endsWith(".mp4") || url.endsWith(".webm")) return url;
|
||||
if (url == undefined || url.endsWith('.mp4') || url.endsWith('.webm')) return url;
|
||||
|
||||
// use cdn for nostr.build
|
||||
return url.replace(/https?:\/\/nostr.build/, "https://cdn.nostr.build");
|
||||
return url.replace(/https?:\/\/nostr.build/, 'https://cdn.nostr.build');
|
||||
};
|
||||
|
||||
export const extractImageUrls = (text: string): string[] => {
|
||||
const urlRegex = /(https?:\/\/[^\s]+)/g;
|
||||
return (text.match(urlRegex) || []).map((u) => urlFix(u));
|
||||
return (text.match(urlRegex) || []).map(u => urlFix(u));
|
||||
};
|
||||
|
||||
export const isReply = (event: any) => {
|
||||
// ["e", "aab5a68f29d76a04ad79fe7e489087b802ee0f946689d73b0e15931dd40a7af3", "", "reply"]
|
||||
return (
|
||||
event.tags.filter((t: string[]) => t[0] === "e" && t[3] === "reply")
|
||||
.length > 0
|
||||
);
|
||||
return event.tags.filter((t: string[]) => t[0] === 'e' && t[3] === 'reply').length > 0;
|
||||
};
|
||||
|
||||
export const hasContentWarning = (event: any) => {
|
||||
// ["content-warning", "NSFW: implied nudity"]
|
||||
return (
|
||||
event.tags.filter((t: string[]) => t[0] === "content-warning").length > 0
|
||||
);
|
||||
return event.tags.filter((t: string[]) => t[0] === 'content-warning').length > 0;
|
||||
};
|
||||
|
||||
export const hasNsfwTag = (event: any) => {
|
||||
// ["e", "aab5a68f29d76a04ad79fe7e489087b802ee0f946689d73b0e15931dd40a7af3", "", "reply"]
|
||||
return (
|
||||
event.tags.filter((t: string[]) => t[0] === "t" && nfswTags.includes(t[1]))
|
||||
.length > 0
|
||||
);
|
||||
return event.tags.filter((t: string[]) => t[0] === 't' && nfswTags.includes(t[1])).length > 0;
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
:root {
|
||||
font-family: "Outfit", sans-serif;
|
||||
font-family: 'Outfit', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
line-height: 1.5;
|
||||
@ -64,8 +64,7 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
input[type="checkbox"] {
|
||||
input[type='checkbox'] {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
@ -76,8 +75,8 @@ input[type="checkbox"] {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:after {
|
||||
content: " ";
|
||||
input[type='checkbox']:after {
|
||||
content: ' ';
|
||||
position: relative;
|
||||
left: 40%;
|
||||
top: 20%;
|
||||
@ -89,7 +88,7 @@ input[type="checkbox"]:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked:after {
|
||||
input[type='checkbox']:checked:after {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@ -102,7 +101,6 @@ div.paper {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
.btn {
|
||||
border: none;
|
||||
outline: none;
|
||||
@ -121,13 +119,15 @@ div.paper {
|
||||
.btn-border {
|
||||
border: 1px solid transparent;
|
||||
color: inherit;
|
||||
background: linear-gradient(black, black) padding-box,
|
||||
background:
|
||||
linear-gradient(black, black) padding-box,
|
||||
linear-gradient(94.73deg, #2bd9ff 0%, #f838d9 100%) border-box;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.btn-border:hover {
|
||||
background: linear-gradient(black, black) padding-box,
|
||||
background:
|
||||
linear-gradient(black, black) padding-box,
|
||||
linear-gradient(94.73deg, #14b4d8 0%, #ba179f 100%) border-box;
|
||||
}
|
||||
|
||||
@ -155,4 +155,4 @@ div.paper {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
26
src/main.tsx
26
src/main.tsx
@ -1,38 +1,38 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { NDKProvider } from "@nostr-dev-kit/ndk-react";
|
||||
import App from "./App";
|
||||
import "./index.css";
|
||||
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { NDKProvider } from '@nostr-dev-kit/ndk-react';
|
||||
import App from './App';
|
||||
import './index.css';
|
||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
path: '/',
|
||||
element: <App />,
|
||||
},
|
||||
{
|
||||
path: "global",
|
||||
path: 'global',
|
||||
element: <App />,
|
||||
},
|
||||
{
|
||||
path: "tags/:tags",
|
||||
path: 'tags/:tags',
|
||||
element: <App />,
|
||||
},
|
||||
{
|
||||
path: "profile/:npub",
|
||||
path: 'profile/:npub',
|
||||
element: <App />,
|
||||
},
|
||||
{
|
||||
path: "p/:npub",
|
||||
path: 'p/:npub',
|
||||
element: <App />,
|
||||
},
|
||||
{
|
||||
path: "/:npub",
|
||||
path: '/:npub',
|
||||
element: <App />,
|
||||
},
|
||||
]);
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<NDKProvider>
|
||||
<RouterProvider router={router} />
|
||||
</NDKProvider>
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { DependencyList, EffectCallback, useEffect } from "react";
|
||||
import { DependencyList, EffectCallback, useEffect } from 'react';
|
||||
|
||||
const useDebouncedEffect = (effect: EffectCallback, deps?: DependencyList, delay?: number) => {
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => effect(), delay);
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => effect(), delay);
|
||||
|
||||
return () => clearTimeout(handler);
|
||||
return () => clearTimeout(handler);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [...(deps || []), delay]);
|
||||
}
|
||||
}, [...(deps || []), delay]);
|
||||
};
|
||||
|
||||
export default useDebouncedEffect;
|
||||
export default useDebouncedEffect;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@ -10,9 +10,7 @@ const useDisclaimerState = () => {
|
||||
const [disclaimerAccepted, setDisclaimerAccepted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const disclaimerAcceptedPreviously = JSON.parse(
|
||||
localStorage.getItem("disclaimerAccepted") as string
|
||||
);
|
||||
const disclaimerAcceptedPreviously = JSON.parse(localStorage.getItem('disclaimerAccepted') as string);
|
||||
if (disclaimerAcceptedPreviously === true) {
|
||||
setDisclaimerAccepted(true);
|
||||
}
|
||||
@ -22,7 +20,7 @@ const useDisclaimerState = () => {
|
||||
disclaimerAccepted,
|
||||
setDisclaimerAccepted: (accepted: boolean) => {
|
||||
setDisclaimerAccepted(accepted);
|
||||
localStorage.setItem("disclaimerAccepted", JSON.stringify(accepted));
|
||||
localStorage.setItem('disclaimerAccepted', JSON.stringify(accepted));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const useImageLoaded = (src?: string) => {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
|
Loading…
Reference in New Issue
Block a user