feat: blogs #118

Merged
enes merged 20 commits from feature/blogs into staging 2024-11-11 12:00:59 +00:00
25 changed files with 865 additions and 718 deletions
Showing only changes of commit 847aab29d7 - Show all commits

View File

@ -12,3 +12,6 @@ VITE_FALLBACK_MOD_IMAGE=https://image.nostr.build/1fa7afd3489302c2da8957993ac0fd
# if there's no image, or if the image breaks somewhere down the line, then it should default to this image # if there's no image, or if the image breaks somewhere down the line, then it should default to this image
VITE_FALLBACK_GAME_IMAGE=https://image.nostr.build/1fa7afd3489302c2da8957993ac0fd6c4308eedd4b1b95b24ecfabe3651b2183.png VITE_FALLBACK_GAME_IMAGE=https://image.nostr.build/1fa7afd3489302c2da8957993ac0fd6c4308eedd4b1b95b24ecfabe3651b2183.png
# A comma separated list of npubs, this list is used to fetch just the posts from the admin
VITE_BLOG_NPUBS= <A comma separated list of npubs>

372
package-lock.json generated
View File

@ -12,10 +12,11 @@
"@nostr-dev-kit/ndk": "2.10.0", "@nostr-dev-kit/ndk": "2.10.0",
"@nostr-dev-kit/ndk-cache-dexie": "2.5.1", "@nostr-dev-kit/ndk-cache-dexie": "2.5.1",
"@reduxjs/toolkit": "2.2.6", "@reduxjs/toolkit": "2.2.6",
"@tiptap/core": "2.6.6", "@tiptap/core": "2.9.1",
"@tiptap/extension-link": "2.6.6", "@tiptap/extension-image": "^2.9.1",
"@tiptap/react": "2.6.6", "@tiptap/extension-link": "2.9.1",
"@tiptap/starter-kit": "2.6.6", "@tiptap/react": "2.9.1",
"@tiptap/starter-kit": "2.9.1",
"@types/react-helmet": "^6.1.11", "@types/react-helmet": "^6.1.11",
"axios": "1.7.3", "axios": "1.7.3",
"bech32": "2.0.0", "bech32": "2.0.0",
@ -26,6 +27,7 @@
"file-saver": "2.0.5", "file-saver": "2.0.5",
"fslightbox-react": "1.7.6", "fslightbox-react": "1.7.6",
"lodash": "4.17.21", "lodash": "4.17.21",
"marked": "^14.1.3",
"nostr-login": "1.5.2", "nostr-login": "1.5.2",
"nostr-tools": "2.7.1", "nostr-tools": "2.7.1",
"papaparse": "5.4.1", "papaparse": "5.4.1",
@ -1160,6 +1162,7 @@
"version": "2.11.8", "version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"license": "MIT",
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/popperjs" "url": "https://opencollective.com/popperjs"
@ -1189,9 +1192,10 @@
} }
}, },
"node_modules/@remirror/core-constants": { "node_modules/@remirror/core-constants": {
"version": "2.0.2", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-2.0.2.tgz", "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz",
"integrity": "sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==" "integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
"license": "MIT"
}, },
"node_modules/@remix-run/router": { "node_modules/@remix-run/router": {
"version": "1.17.1", "version": "1.17.1",
@ -1487,45 +1491,49 @@
} }
}, },
"node_modules/@tiptap/core": { "node_modules/@tiptap/core": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.9.1.tgz",
"integrity": "sha512-VO5qTsjt6rwworkuo0s5AqYMfDA0ZwiTiH6FHKFSu2G/6sS7HKcc/LjPq+5Legzps4QYdBDl3W28wGsGuS1GdQ==", "integrity": "sha512-tifnLL/ARzQ6/FGEJjVwj9UT3v+pENdWHdk9x6F3X0mB1y0SeCjV21wpFLYESzwNdBPAj8NMp8Behv7dBnhIfw==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/pm": "^2.6.6" "@tiptap/pm": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-blockquote": { "node_modules/@tiptap/extension-blockquote": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.9.1.tgz",
"integrity": "sha512-hAdsNlMfzzxld154hJqPqtWqO5i4/7HoDfuxmyqBxdMJ+e2UMaIGBGwoLRXG0V9UoRwJusjqlpyD7pIorxNlgA==", "integrity": "sha512-Y0jZxc/pdkvcsftmEZFyG+73um8xrx6/DMfgUcNg3JAM63CISedNcr+OEI11L0oFk1KFT7/aQ9996GM6Kubdqg==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6" "@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-bold": { "node_modules/@tiptap/extension-bold": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.9.1.tgz",
"integrity": "sha512-CD6gBhdQtCoqYSmx8oAV8gvKtVOGZSyyvuNYo7by9eZ56DqLYnd7kbUj0RH7o9Ymf/iJTOUJ6XcvrsWwo4lubg==", "integrity": "sha512-e2P1zGpnnt4+TyxTC5pX/lPxPasZcuHCYXY0iwQ3bf8qRQQEjDfj3X7EI+cXqILtnhOiviEOcYmeu5op2WhQDg==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6" "@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-bubble-menu": { "node_modules/@tiptap/extension-bubble-menu": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.9.1.tgz",
"integrity": "sha512-IkfmlZq67aaegym5sBddBc/xXWCArxn5WJEl1oxKEayjQhybKSaqI7tk0lOx/x7fa5Ml1WlGpCFh+KKXbQTG0g==", "integrity": "sha512-DWUF6NG08/bZDWw0jCeotSTvpkyqZTi4meJPomG9Wzs/Ol7mEwlNCsCViD999g0+IjyXFatBk4DfUq1YDDu++Q==",
"license": "MIT",
"dependencies": { "dependencies": {
"tippy.js": "^6.3.7" "tippy.js": "^6.3.7"
}, },
@ -1534,76 +1542,82 @@
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6", "@tiptap/core": "^2.7.0",
"@tiptap/pm": "^2.6.6" "@tiptap/pm": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-bullet-list": { "node_modules/@tiptap/extension-bullet-list": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.9.1.tgz",
"integrity": "sha512-WEKxbVSYuvmX2wkHWP8HXk5nzA7stYwtdaubwWH/R17kGI3IGScJuMQ9sEN82uzJU8bfgL9yCbH2bY8Fj/Q4Ow==", "integrity": "sha512-0hizL/0j9PragJObjAWUVSuGhN1jKjCFnhLQVRxtx4HutcvS/lhoWMvFg6ZF8xqWgIa06n6A7MaknQkqhTdhKA==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6" "@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-code": { "node_modules/@tiptap/extension-code": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.9.1.tgz",
"integrity": "sha512-JrEFKsZiLvfvOFhOnnrpA0TzCuJjDeysfbMeuKUZNV4+DhYOL28d39H1++rEtJAX0LcbBU60oC5/PrlU9SpvRQ==", "integrity": "sha512-WQqcVGe7i/E+yO3wz5XQteU1ETNZ00euUEl4ylVVmH2NM4Dh0KDjEhbhHlCM0iCfLUo7jhjC7dmS+hMdPUb+Tg==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6" "@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-code-block": { "node_modules/@tiptap/extension-code-block": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.9.1.tgz",
"integrity": "sha512-1YLp/zHMHSkE2xzht8nPR6T4sQJJ3ket798czxWuQEbetFv/l0U/mpiPpYSLObj6oTAoqYZ0kWXZj5eQSpPB8Q==", "integrity": "sha512-A/50wPWDqEUUUPhrwRKILP5gXMO5UlQ0F6uBRGYB9CEVOREam9yIgvONOnZVJtszHqOayjIVMXbH/JMBeq11/g==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6", "@tiptap/core": "^2.7.0",
"@tiptap/pm": "^2.6.6" "@tiptap/pm": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-document": { "node_modules/@tiptap/extension-document": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.9.1.tgz",
"integrity": "sha512-6qlH5VWzLHHRVeeciRC6C4ZHpMsAGPNG16EF53z0GeMSaaFD/zU3B239QlmqXmLsAl8bpf8Bn93N0t2ABUvScw==", "integrity": "sha512-1a+HCoDPnBttjqExfYLwfABq8MYdiowhy/wp8eCxVb6KGFEENO53KapstISvPzqH7eOi+qRjBB1KtVYb/ZXicg==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6" "@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-dropcursor": { "node_modules/@tiptap/extension-dropcursor": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.9.1.tgz",
"integrity": "sha512-O6CeKriA9uyHsg7Ui4z5ZjEWXQxrIL+1zDekffW0wenGC3G4LUsCzAiFS4LSrR9a3u7tnwqGApW10rdkmCGF4w==", "integrity": "sha512-wJZspSmJRkDBtPkzFz1g7gvZOEOayk8s93UHsgbJxcV4VWHYleZ5XhT74sZunSjefNDm3qC6v2BSgLp3vNHVKQ==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6", "@tiptap/core": "^2.7.0",
"@tiptap/pm": "^2.6.6" "@tiptap/pm": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-floating-menu": { "node_modules/@tiptap/extension-floating-menu": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.9.1.tgz",
"integrity": "sha512-lPkESOfAUxgmXRiNqUU23WSyja5FUfSWjsW4hqe+BKNjsUt1OuFMEtYJtNc+MCGhhtPfFvM3Jg6g9jd6g5XsLQ==", "integrity": "sha512-MxZ7acNNsoNaKpetxfwi3Z11Bgrh0T2EJlCV77v9N1vWK38+st3H1WJanmLbPNtc2ocvhHJrz+DjDz3CWxQ9rQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"tippy.js": "^6.3.7" "tippy.js": "^6.3.7"
}, },
@ -1612,89 +1626,109 @@
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6", "@tiptap/core": "^2.7.0",
"@tiptap/pm": "^2.6.6" "@tiptap/pm": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-gapcursor": { "node_modules/@tiptap/extension-gapcursor": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.9.1.tgz",
"integrity": "sha512-O2lQ2t0X0Vsbn3yLWxFFHrXY6C2N9Y6ZF/M7LWzpcDTUZeWuhoNkFE/1yOM0h6ZX1DO2A9hNIrKpi5Ny8yx+QA==", "integrity": "sha512-jsRBmX01vr+5H02GljiHMo0n5H1vzoMLmFarxe0Yq2d2l9G/WV2VWX2XnGliqZAYWd1bI0phs7uLQIN3mxGQTw==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6", "@tiptap/core": "^2.7.0",
"@tiptap/pm": "^2.6.6" "@tiptap/pm": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-hard-break": { "node_modules/@tiptap/extension-hard-break": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.9.1.tgz",
"integrity": "sha512-bsUuyYBrMDEiudx1dOQSr9MzKv13m0xHWrOK+DYxuIDYJb5g+c9un5cK7Js+et/HEYYSPOoH/iTW6h+4I5YeUg==", "integrity": "sha512-fCuaOD/b7nDjm47PZ58oanq7y4ccS2wjPh42Qm0B0yipu/1fmC8eS1SmaXmk28F89BLtuL6uOCtR1spe+lZtlQ==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6" "@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-heading": { "node_modules/@tiptap/extension-heading": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.9.1.tgz",
"integrity": "sha512-bgx9vptVFi5yFkIw1OI53J7+xJ71Or3SOe/Q8eSpZv53DlaKpL/TzKw8Z54t1PrI2rJ6H9vrLtkvixJvBZH1Ug==", "integrity": "sha512-SjZowzLixOFaCrV2cMaWi1mp8REK0zK1b3OcVx7bCZfVSmsOETJyrAIUpCKA8o60NwF7pwhBg0MN8oXlNKMeFw==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6" "@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-history": { "node_modules/@tiptap/extension-history": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.9.1.tgz",
"integrity": "sha512-tPTzAmPGqMX5Bd5H8lzRpmsaMvB9DvI5Dy2za/VQuFtxgXmDiFVgHRkRXIuluSkPTuANu84XBOQ0cBijqY8x4w==", "integrity": "sha512-wp9qR1NM+LpvyLZFmdNaAkDq0d4jDJ7z7Fz7icFQPu31NVxfQYO3IXNmvJDCNu8hFAbImpA5aG8MBuwzRo0H9w==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6", "@tiptap/core": "^2.7.0",
"@tiptap/pm": "^2.6.6" "@tiptap/pm": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-horizontal-rule": { "node_modules/@tiptap/extension-horizontal-rule": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.9.1.tgz",
"integrity": "sha512-cFEfv7euDpuLSe8exY8buwxkreKBAZY9Hn3EetKhPcLQo+ut5Y24chZTxFyf9b+Y0wz3UhOhLTZSz7fTobLqBA==", "integrity": "sha512-ydUhABeaBI1CoJp+/BBqPhXINfesp1qMNL/jiDcMsB66fsD4nOyphpAJT7FaRFZFtQVF06+nttBtFZVkITQVqg==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6", "@tiptap/core": "^2.7.0",
"@tiptap/pm": "^2.6.6" "@tiptap/pm": "^2.7.0"
}
},
"node_modules/@tiptap/extension-image": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.9.1.tgz",
"integrity": "sha512-aGqJnsuS8oagIhsx7wetm8jw4NEDsOV0OSx4FQ4VPlUqWlnzK0N+erFKKJmXTdAxL8PGzoPSlITFH63MV3eV3Q==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-italic": { "node_modules/@tiptap/extension-italic": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.9.1.tgz",
"integrity": "sha512-t7ZPsXqa8nJZZ/6D0rQyZ/KsvzLaSihC6hBTjUQ77CeDGV9PhDWjIcBW4OrvwraJDBd12ETBeQ2CkULJOgH+lQ==", "integrity": "sha512-VkNA6Vz96+/+7uBlsgM7bDXXx4b62T1fDam/3UKifA72aD/fZckeWrbT7KrtdUbzuIniJSbA0lpTs5FY29+86Q==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6" "@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-link": { "node_modules/@tiptap/extension-link": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.9.1.tgz",
"integrity": "sha512-NJSR5Yf/dI3do0+Mr6e6nkbxRQcqbL7NOPxo5Xw8VaKs2Oe8PX+c7hyqN3GZgn6uEbZdbVi1xjAniUokouwpFg==", "integrity": "sha512-yG+e3e8cCCN9dZjX4ttEe3e2xhh58ryi3REJV4MdiEkOT9QF75Bl5pUbMIS4tQ8HkOr04QBFMHKM12kbSxg1BA==",
"license": "MIT",
"dependencies": { "dependencies": {
"linkifyjs": "^4.1.0" "linkifyjs": "^4.1.0"
}, },
@ -1703,78 +1737,97 @@
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6", "@tiptap/core": "^2.7.0",
"@tiptap/pm": "^2.6.6" "@tiptap/pm": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-list-item": { "node_modules/@tiptap/extension-list-item": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.9.1.tgz",
"integrity": "sha512-k+oEzZu2cgVKqPqOP1HzASOKLpTEV9m7mRVPAbuaaX8mSyvIgD6f+JUx9PvgYv//D918wk98LMoRBFX53tDJ4w==", "integrity": "sha512-6O4NtYNR5N2Txi4AC0/4xMRJq9xd4+7ShxCZCDVL0WDVX37IhaqMO7LGQtA6MVlYyNaX4W1swfdJaqrJJ5HIUw==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6" "@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-ordered-list": { "node_modules/@tiptap/extension-ordered-list": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.9.1.tgz",
"integrity": "sha512-AJwyfLXIi7iUGnK5twJbwdVVpQyh7fU6OK75h1AwDztzsOcoPcxtffDlZvUOd4ZtwuyhkzYqVkeI0f+abTWZTw==", "integrity": "sha512-6J9jtv1XP8dW7/JNSH/K4yiOABc92tBJtgCsgP8Ep4+fjfjdj4HbjS1oSPWpgItucF2Fp/VF8qg55HXhjxHjTw==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6" "@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-paragraph": { "node_modules/@tiptap/extension-paragraph": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.9.1.tgz",
"integrity": "sha512-fD/onCr16UQWx+/xEmuFC2MccZZ7J5u4YaENh8LMnAnBXf78iwU7CAcmuc9rfAEO3qiLoYGXgLKiHlh2ZfD4wA==", "integrity": "sha512-JOmT0xd4gd3lIhLwrsjw8lV+ZFROKZdIxLi0Ia05XSu4RLrrvWj0zdKMSB+V87xOWfSB3Epo95zAvnPox5Q16A==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6" "@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-strike": { "node_modules/@tiptap/extension-strike": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.9.1.tgz",
"integrity": "sha512-Ze8KhGk+wzSJSJRl5fbhTI6AvPu2LmcHYeO3pMEH8u4gV5WTXfmKJVStEIAzkoqvwEQVWzXvy8nDgsFQHiojPg==", "integrity": "sha512-V5aEXdML+YojlPhastcu7w4biDPwmzy/fWq0T2qjfu5Te/THcqDmGYVBKESBm5x6nBy5OLkanw2O+KHu2quDdg==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6" "@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/extension-text": { "node_modules/@tiptap/extension-text": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.9.1.tgz",
"integrity": "sha512-e84uILnRzNzcwK1DVQNpXVmBG1Cq3BJipTOIDl1LHifOok7MBjhI/X+/NR0bd3N2t6gmDTWi63+4GuJ5EeDmsg==", "integrity": "sha512-3wo9uCrkLVLQFgbw2eFU37QAa1jq1/7oExa+FF/DVxdtHRS9E2rnUZ8s2hat/IWzvPUHXMwo3Zg2XfhoamQpCA==",
"license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6" "@tiptap/core": "^2.7.0"
}
},
"node_modules/@tiptap/extension-text-style": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.9.1.tgz",
"integrity": "sha512-LAxc0SeeiPiAVBwksczeA7BJSZb6WtVpYhy5Esvy9K0mK5kttB4KxtnXWeQzMIJZQbza65yftGKfQlexf/Y7yg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^2.7.0"
} }
}, },
"node_modules/@tiptap/pm": { "node_modules/@tiptap/pm": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.9.1.tgz",
"integrity": "sha512-56FGLPn3fwwUlIbLs+BO21bYfyqP9fKyZQbQyY0zWwA/AG2kOwoXaRn7FOVbjP6CylyWpFJnpRRmgn694QKHEg==", "integrity": "sha512-mvV86fr7kEuDYEApQ2uMPCKL2uagUE0BsXiyyz3KOkY1zifyVm1fzdkscb24Qy1GmLzWAIIihA+3UHNRgYdOlQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"prosemirror-changeset": "^2.2.1", "prosemirror-changeset": "^2.2.1",
"prosemirror-collab": "^1.3.1", "prosemirror-collab": "^1.3.1",
"prosemirror-commands": "^1.5.2", "prosemirror-commands": "^1.6.0",
"prosemirror-dropcursor": "^1.8.1", "prosemirror-dropcursor": "^1.8.1",
"prosemirror-gapcursor": "^1.3.2", "prosemirror-gapcursor": "^1.3.2",
"prosemirror-history": "^1.4.1", "prosemirror-history": "^1.4.1",
@ -1782,14 +1835,14 @@
"prosemirror-keymap": "^1.2.2", "prosemirror-keymap": "^1.2.2",
"prosemirror-markdown": "^1.13.0", "prosemirror-markdown": "^1.13.0",
"prosemirror-menu": "^1.2.4", "prosemirror-menu": "^1.2.4",
"prosemirror-model": "^1.22.2", "prosemirror-model": "^1.22.3",
"prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-basic": "^1.2.3",
"prosemirror-schema-list": "^1.4.1", "prosemirror-schema-list": "^1.4.1",
"prosemirror-state": "^1.4.3", "prosemirror-state": "^1.4.3",
"prosemirror-tables": "^1.4.0", "prosemirror-tables": "^1.4.0",
"prosemirror-trailing-node": "^2.0.9", "prosemirror-trailing-node": "^3.0.0",
"prosemirror-transform": "^1.9.0", "prosemirror-transform": "^1.10.0",
"prosemirror-view": "^1.33.9" "prosemirror-view": "^1.34.3"
}, },
"funding": { "funding": {
"type": "github", "type": "github",
@ -1797,13 +1850,15 @@
} }
}, },
"node_modules/@tiptap/react": { "node_modules/@tiptap/react": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.9.1.tgz",
"integrity": "sha512-AUmdb/J1O/vCO2b8LL68ctcZr9a3931BwX4fUUZ1kCrCA5lTj2xz0rjeAtpxEdzLnR+Z7q96vB7vf7bPYOUAew==", "integrity": "sha512-LQJ34ZPfXtJF36SZdcn4Fiwsl2WxZ9YRJI87OLnsjJ45O+gV/PfBzz/4ap+LF8LOS0AbbGhTTjBOelPoNm+aYA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@tiptap/extension-bubble-menu": "^2.6.6", "@tiptap/extension-bubble-menu": "^2.9.1",
"@tiptap/extension-floating-menu": "^2.6.6", "@tiptap/extension-floating-menu": "^2.9.1",
"@types/use-sync-external-store": "^0.0.6", "@types/use-sync-external-store": "^0.0.6",
"fast-deep-equal": "^3",
"use-sync-external-store": "^1.2.2" "use-sync-external-store": "^1.2.2"
}, },
"funding": { "funding": {
@ -1811,8 +1866,8 @@
"url": "https://github.com/sponsors/ueberdosis" "url": "https://github.com/sponsors/ueberdosis"
}, },
"peerDependencies": { "peerDependencies": {
"@tiptap/core": "^2.6.6", "@tiptap/core": "^2.7.0",
"@tiptap/pm": "^2.6.6", "@tiptap/pm": "^2.7.0",
"react": "^17.0.0 || ^18.0.0", "react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0" "react-dom": "^17.0.0 || ^18.0.0"
} }
@ -1823,30 +1878,32 @@
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==" "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="
}, },
"node_modules/@tiptap/starter-kit": { "node_modules/@tiptap/starter-kit": {
"version": "2.6.6", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.6.6.tgz", "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.9.1.tgz",
"integrity": "sha512-zb9xIg3WjG9AsJoyWrfqx5SL9WH7/HTdkB79jFpWtOF/Kaigo7fHFmhs2FsXtJMJlcdMTO2xeRuCYHt5ozXlhg==", "integrity": "sha512-nsw6UF/7wDpPfHRhtGOwkj1ipIEiWZS1VGw+c14K61vM1CNj0uQ4jogbHwHZqN1dlL5Hh+FCqUHDPxG6ECbijg==",
"license": "MIT",
"dependencies": { "dependencies": {
"@tiptap/core": "^2.6.6", "@tiptap/core": "^2.9.1",
"@tiptap/extension-blockquote": "^2.6.6", "@tiptap/extension-blockquote": "^2.9.1",
"@tiptap/extension-bold": "^2.6.6", "@tiptap/extension-bold": "^2.9.1",
"@tiptap/extension-bullet-list": "^2.6.6", "@tiptap/extension-bullet-list": "^2.9.1",
"@tiptap/extension-code": "^2.6.6", "@tiptap/extension-code": "^2.9.1",
"@tiptap/extension-code-block": "^2.6.6", "@tiptap/extension-code-block": "^2.9.1",
"@tiptap/extension-document": "^2.6.6", "@tiptap/extension-document": "^2.9.1",
"@tiptap/extension-dropcursor": "^2.6.6", "@tiptap/extension-dropcursor": "^2.9.1",
"@tiptap/extension-gapcursor": "^2.6.6", "@tiptap/extension-gapcursor": "^2.9.1",
"@tiptap/extension-hard-break": "^2.6.6", "@tiptap/extension-hard-break": "^2.9.1",
"@tiptap/extension-heading": "^2.6.6", "@tiptap/extension-heading": "^2.9.1",
"@tiptap/extension-history": "^2.6.6", "@tiptap/extension-history": "^2.9.1",
"@tiptap/extension-horizontal-rule": "^2.6.6", "@tiptap/extension-horizontal-rule": "^2.9.1",
"@tiptap/extension-italic": "^2.6.6", "@tiptap/extension-italic": "^2.9.1",
"@tiptap/extension-list-item": "^2.6.6", "@tiptap/extension-list-item": "^2.9.1",
"@tiptap/extension-ordered-list": "^2.6.6", "@tiptap/extension-ordered-list": "^2.9.1",
"@tiptap/extension-paragraph": "^2.6.6", "@tiptap/extension-paragraph": "^2.9.1",
"@tiptap/extension-strike": "^2.6.6", "@tiptap/extension-strike": "^2.9.1",
"@tiptap/extension-text": "^2.6.6", "@tiptap/extension-text": "^2.9.1",
"@tiptap/pm": "^2.6.6" "@tiptap/extension-text-style": "^2.9.1",
"@tiptap/pm": "^2.9.1"
}, },
"funding": { "funding": {
"type": "github", "type": "github",
@ -3208,8 +3265,7 @@
"node_modules/fast-deep-equal": { "node_modules/fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
"dev": true
}, },
"node_modules/fast-glob": { "node_modules/fast-glob": {
"version": "3.3.2", "version": "3.3.2",
@ -3862,6 +3918,18 @@
"markdown-it": "bin/markdown-it.mjs" "markdown-it": "bin/markdown-it.mjs"
} }
}, },
"node_modules/marked": {
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/marked/-/marked-14.1.3.tgz",
"integrity": "sha512-ZibJqTULGlt9g5k4VMARAktMAjXoVnnr+Y3aCqW1oDftcV4BA3UmrBifzXoZyenHRk75csiPu9iwsTj4VNBT0g==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/mdurl": { "node_modules/mdurl": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
@ -4519,11 +4587,12 @@
} }
}, },
"node_modules/prosemirror-trailing-node": { "node_modules/prosemirror-trailing-node": {
"version": "2.0.9", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.9.tgz", "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz",
"integrity": "sha512-YvyIn3/UaLFlFKrlJB6cObvUhmwFNZVhy1Q8OpW/avoTbD/Y7H5EcjK4AZFKhmuS6/N6WkGgt7gWtBWDnmFvHg==", "integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@remirror/core-constants": "^2.0.2", "@remirror/core-constants": "3.0.0",
"escape-string-regexp": "^4.0.0" "escape-string-regexp": "^4.0.0"
}, },
"peerDependencies": { "peerDependencies": {
@ -4536,6 +4605,7 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"license": "MIT",
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@ -4552,9 +4622,10 @@
} }
}, },
"node_modules/prosemirror-view": { "node_modules/prosemirror-view": {
"version": "1.34.1", "version": "1.35.0",
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.34.1.tgz", "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.35.0.tgz",
"integrity": "sha512-KS2xmqrAM09h3SLu1S2pNO/ZoIP38qkTJ6KFd7+BeSfmX/ek0n5yOfGuiTZjFNTC8GOsEIUa1tHxt+2FMu3yWQ==", "integrity": "sha512-Umtbh22fmUlpZpRTiOVXA0PpdRZeYEeXQsLp51VfnMhjkJrqJ0n8APinIZrRAD5Jr3UxH8FnOaUqRylSuMsqHA==",
"license": "MIT",
"dependencies": { "dependencies": {
"prosemirror-model": "^1.20.0", "prosemirror-model": "^1.20.0",
"prosemirror-state": "^1.0.0", "prosemirror-state": "^1.0.0",
@ -5043,6 +5114,7 @@
"version": "6.3.7", "version": "6.3.7",
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@popperjs/core": "^2.9.0" "@popperjs/core": "^2.9.0"
} }

View File

@ -14,10 +14,11 @@
"@nostr-dev-kit/ndk": "2.10.0", "@nostr-dev-kit/ndk": "2.10.0",
"@nostr-dev-kit/ndk-cache-dexie": "2.5.1", "@nostr-dev-kit/ndk-cache-dexie": "2.5.1",
"@reduxjs/toolkit": "2.2.6", "@reduxjs/toolkit": "2.2.6",
"@tiptap/core": "2.6.6", "@tiptap/core": "2.9.1",
"@tiptap/extension-link": "2.6.6", "@tiptap/extension-image": "^2.9.1",
"@tiptap/react": "2.6.6", "@tiptap/extension-link": "2.9.1",
"@tiptap/starter-kit": "2.6.6", "@tiptap/react": "2.9.1",
"@tiptap/starter-kit": "2.9.1",
"@types/react-helmet": "^6.1.11", "@types/react-helmet": "^6.1.11",
"axios": "1.7.3", "axios": "1.7.3",
"bech32": "2.0.0", "bech32": "2.0.0",
@ -28,6 +29,7 @@
"file-saver": "2.0.5", "file-saver": "2.0.5",
"fslightbox-react": "1.7.6", "fslightbox-react": "1.7.6",
"lodash": "4.17.21", "lodash": "4.17.21",
"marked": "^14.1.3",
"nostr-login": "1.5.2", "nostr-login": "1.5.2",
"nostr-tools": "2.7.1", "nostr-tools": "2.7.1",
"papaparse": "5.4.1", "papaparse": "5.4.1",

View File

@ -1,37 +1,33 @@
import { Link } from 'react-router-dom'
import { BlogCardDetails } from 'types'
import { getBlogPageRoute } from 'routes'
import '../styles/cardBlogs.css' import '../styles/cardBlogs.css'
import placeholder from '../assets/img/DEGMods Placeholder Img.png'
type BlogCardProps = { type BlogCardProps = Partial<BlogCardDetails>
backgroundLink: string
} export const BlogCard = ({ title, image, nsfw, naddr }: BlogCardProps) => {
if (!naddr) return null
export const BlogCard = ({ backgroundLink }: BlogCardProps) => {
return ( return (
<a className='cardBlogMainWrapperLink' href='blog-inner.html'> <Link to={getBlogPageRoute(naddr)} className='cardBlogMainWrapperLink'>
<div <div
className='cardBlogMain' className='cardBlogMain'
style={{ style={{
background: `url("${backgroundLink}") center / cover no-repeat` background: `url("${
image ? image : placeholder
}") center / cover no-repeat`
}} }}
> >
<div <div className='cardBlogMainInside'>
className='cardBlogMainInside' <h3 className='cardBlogMainInsideTitle'>{title}</h3>
> {nsfw && (
<h3 <div className='IBMSMSMBSSTagsTag IBMSMSMBSSTagsTagNSFW IBMSMSMBSSTagsTagNSFWCard IBMSMSMBSSTagsTagNSFWCardAlt'>
style={{ <p>NSFW</p>
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
WebkitLineClamp: 2,
fontSize: '20px',
lineHeight: 1.5,
color: 'rgba(255, 255, 255, 0.75)',
textShadow: '0 0 8px rgba(0, 0, 0, 0.25)'
}}
>
This is a blog title, the best blog title in the world!
</h3>
</div> </div>
</div>{' '} )}
</a> </div>
</div>
</Link>
) )
} }

View File

@ -1,4 +1,5 @@
import Link from '@tiptap/extension-link' import Link from '@tiptap/extension-link'
import Image from '@tiptap/extension-image'
import { Editor, EditorContent, useEditor } from '@tiptap/react' import { Editor, EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit' import StarterKit from '@tiptap/starter-kit'
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
@ -127,7 +128,15 @@ type RichTextEditorProps = {
const RichTextEditor = ({ content, updateContent }: RichTextEditorProps) => { const RichTextEditor = ({ content, updateContent }: RichTextEditorProps) => {
const editor = useEditor({ const editor = useEditor({
extensions: [StarterKit, Link], extensions: [
StarterKit,
Link,
Image.configure({
HTMLAttributes: {
class: 'IBMSMSMBSSPostImg'
}
})
],
onUpdate: ({ editor }) => { onUpdate: ({ editor }) => {
// Update the state when the editor content changes // Update the state when the editor content changes
updateContent(editor.getHTML()) updateContent(editor.getHTML())
@ -181,6 +190,17 @@ const MenuBar = ({ editor }: MenuBarProps) => {
const unsetLink = () => editor.chain().focus().unsetLink().run() const unsetLink = () => editor.chain().focus().unsetLink().run()
const setImage = () => {
let url = prompt('URL')
if (url) {
if (!/^(http|https):\/\//i.test(url)) {
url = `https://${url}`
}
return editor.chain().focus().setImage({ src: url }).run()
}
return false
}
const buttons: MenuBarButtonProps[] = [ const buttons: MenuBarButtonProps[] = [
{ {
label: 'Bold', label: 'Bold',
@ -211,7 +231,7 @@ const MenuBar = ({ editor }: MenuBarProps) => {
{ {
label: 'Paragraph', label: 'Paragraph',
isActive: editor.isActive('paragraph'), isActive: editor.isActive('paragraph'),
onClick: () => editor.chain().focus().toggleStrike().run() onClick: () => editor.chain().focus().setParagraph().run()
}, },
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
...[1, 2, 3, 4, 5, 6].map((level: any) => ({ ...[1, 2, 3, 4, 5, 6].map((level: any) => ({
@ -244,6 +264,11 @@ const MenuBar = ({ editor }: MenuBarProps) => {
isActive: editor.isActive('link'), isActive: editor.isActive('link'),
onClick: editor.isActive('link') ? unsetLink : setLink onClick: editor.isActive('link') ? unsetLink : setLink
}, },
{
label: 'Image',
isActive: editor.isActive('image'),
onClick: setImage
},
{ {
label: 'Horizontal rule', label: 'Horizontal rule',
onClick: () => editor.chain().focus().setHorizontalRule().run() onClick: () => editor.chain().focus().setHorizontalRule().run()

View File

@ -0,0 +1,42 @@
import { Addressable } from 'types'
import { abbreviateNumber } from 'utils'
import { Zap } from './Zap'
import { Reactions } from './Reactions'
type InteractionsProps = {
addressable: Addressable
commentCount: number
}
export const Interactions = ({
addressable,
commentCount
}: InteractionsProps) => {
return (
<div className='IBMSMSplitMainBigSideSec'>
<div className='IBMSMSMBSS_Details'>
<a style={{ textDecoration: 'unset', color: 'unset' }}>
<div className='IBMSMSMBSS_Details_Card IBMSMSMBSS_D_CComments'>
<div className='IBMSMSMBSS_Details_CardVisual'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSS_Details_CardVisualIcon'
>
<path d='M256 31.1c-141.4 0-255.1 93.12-255.1 208c0 49.62 21.35 94.98 56.97 130.7c-12.5 50.37-54.27 95.27-54.77 95.77c-2.25 2.25-2.875 5.734-1.5 8.734c1.249 3 4.021 4.766 7.271 4.766c66.25 0 115.1-31.76 140.6-51.39c32.63 12.25 69.02 19.39 107.4 19.39c141.4 0 255.1-93.13 255.1-207.1S397.4 31.1 256 31.1zM127.1 271.1c-17.75 0-32-14.25-32-31.1s14.25-32 32-32s32 14.25 32 32S145.7 271.1 127.1 271.1zM256 271.1c-17.75 0-31.1-14.25-31.1-31.1s14.25-32 31.1-32s31.1 14.25 31.1 32S273.8 271.1 256 271.1zM383.1 271.1c-17.75 0-32-14.25-32-31.1s14.25-32 32-32s32 14.25 32 32S401.7 271.1 383.1 271.1z'></path>
</svg>
</div>
<p className='IBMSMSMBSS_Details_CardText'>
{abbreviateNumber(commentCount)}
</p>
</div>
</a>
<Zap addressable={addressable} />
<Reactions addressable={addressable} />
</div>
</div>
)
}

View File

@ -0,0 +1,86 @@
import { formatDate } from 'date-fns'
type PublishDetailsProps = {
published_at: number
edited_at: number
site: string
}
export const PublishDetails = ({
published_at,
edited_at,
site
}: PublishDetailsProps) => {
return (
<div className='IBMSMSplitMainBigSideSec'>
<div className='IBMSMSMBSSPost_PostDetails'>
<div
data-bs-toggle='tooltip'
data-bs-placement='left'
className='IBMSMSMBSSPost_PDElement'
title='Publish date'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSSPost_PDElementIcon'
data-bs-toggle='tooltip'
data-bss-tooltip
aria-label='Publish date'
>
<path d='M480 32H128C110.3 32 96 46.33 96 64v336C96 408.8 88.84 416 80 416S64 408.8 64 400V96H32C14.33 96 0 110.3 0 128v288c0 35.35 28.65 64 64 64h384c35.35 0 64-28.65 64-64V64C512 46.33 497.7 32 480 32zM272 416h-96C167.2 416 160 408.8 160 400C160 391.2 167.2 384 176 384h96c8.836 0 16 7.162 16 16C288 408.8 280.8 416 272 416zM272 320h-96C167.2 320 160 312.8 160 304C160 295.2 167.2 288 176 288h96C280.8 288 288 295.2 288 304C288 312.8 280.8 320 272 320zM432 416h-96c-8.836 0-16-7.164-16-16c0-8.838 7.164-16 16-16h96c8.836 0 16 7.162 16 16C448 408.8 440.8 416 432 416zM432 320h-96C327.2 320 320 312.8 320 304C320 295.2 327.2 288 336 288h96C440.8 288 448 295.2 448 304C448 312.8 440.8 320 432 320zM448 208C448 216.8 440.8 224 432 224h-256C167.2 224 160 216.8 160 208v-96C160 103.2 167.2 96 176 96h256C440.8 96 448 103.2 448 112V208z' />
</svg>
<p className='IBMSMSMBSSPost_PDElementText'>
{formatDate(
(published_at !== -1 ? published_at : edited_at) * 1000,
'dd/MM/yyyy hh:mm:ss aa'
)}
</p>
</div>
<div
data-bs-toggle='tooltip'
data-bs-placement='left'
className='IBMSMSMBSSPost_PDElement'
title='Last modified'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSSPost_PDElementIcon'
>
<path d='M362.7 19.32C387.7-5.678 428.3-5.678 453.3 19.32L492.7 58.75C517.7 83.74 517.7 124.3 492.7 149.3L444.3 197.7L314.3 67.72L362.7 19.32zM421.7 220.3L188.5 453.4C178.1 463.8 165.2 471.5 151.1 475.6L30.77 511C22.35 513.5 13.24 511.2 7.03 504.1C.8198 498.8-1.502 489.7 .976 481.2L36.37 360.9C40.53 346.8 48.16 333.9 58.57 323.5L291.7 90.34L421.7 220.3z' />
</svg>
<p className='IBMSMSMBSSPost_PDElementText'>
{formatDate(edited_at * 1000, 'dd/MM/yyyy hh:mm:ss aa')}
</p>
</div>
<a
data-bs-toggle='tooltip'
data-bs-placement='left'
className='IBMSMSMBSSPost_PDElement IBMSMSMBSSPost_PDElementLink'
href='#'
title='Published on'
target='_blank'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 -64 640 640'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSSPost_PDElementIcon'
>
<path d='M172.5 131.1C228.1 75.51 320.5 75.51 376.1 131.1C426.1 181.1 433.5 260.8 392.4 318.3L391.3 319.9C381 334.2 361 337.6 346.7 327.3C332.3 317 328.9 297 339.2 282.7L340.3 281.1C363.2 249 359.6 205.1 331.7 177.2C300.3 145.8 249.2 145.8 217.7 177.2L105.5 289.5C73.99 320.1 73.99 372 105.5 403.5C133.3 431.4 177.3 435 209.3 412.1L210.9 410.1C225.3 400.7 245.3 404 255.5 418.4C265.8 432.8 262.5 452.8 248.1 463.1L246.5 464.2C188.1 505.3 110.2 498.7 60.21 448.8C3.741 392.3 3.741 300.7 60.21 244.3L172.5 131.1zM467.5 380C411 436.5 319.5 436.5 263 380C213 330 206.5 251.2 247.6 193.7L248.7 192.1C258.1 177.8 278.1 174.4 293.3 184.7C307.7 194.1 311.1 214.1 300.8 229.3L299.7 230.9C276.8 262.1 280.4 306.9 308.3 334.8C339.7 366.2 390.8 366.2 422.3 334.8L534.5 222.5C566 191 566 139.1 534.5 108.5C506.7 80.63 462.7 76.99 430.7 99.9L429.1 101C414.7 111.3 394.7 107.1 384.5 93.58C374.2 79.2 377.5 59.21 391.9 48.94L393.5 47.82C451 6.731 529.8 13.25 579.8 63.24C636.3 119.7 636.3 211.3 579.8 267.7L467.5 380z' />
</svg>
<p className='IBMSMSMBSSPost_PDElementText'>{site}</p>
</a>
</div>
</div>
)
}

View File

@ -1,11 +1,11 @@
import { useReactions } from 'hooks' import { useReactions } from 'hooks'
import { ModDetails } from 'types' import { Addressable } from 'types'
type ReactionsProps = { type ReactionsProps = {
modDetails: ModDetails addressable: Addressable
} }
export const Reactions = ({ modDetails }: ReactionsProps) => { export const Reactions = ({ addressable }: ReactionsProps) => {
const { const {
isDataLoaded, isDataLoaded,
likesCount, likesCount,
@ -14,9 +14,9 @@ export const Reactions = ({ modDetails }: ReactionsProps) => {
hasReactedPositively, hasReactedPositively,
hasReactedNegatively hasReactedNegatively
} = useReactions({ } = useReactions({
pubkey: modDetails.author, pubkey: addressable.author,
eTag: modDetails.id, eTag: addressable.id,
aTag: modDetails.aTag aTag: addressable.aTag
}) })
if (!isDataLoaded) return null if (!isDataLoaded) return null

View File

@ -7,14 +7,14 @@ import {
} from 'hooks' } from 'hooks'
import { useState } from 'react' import { useState } from 'react'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { ModDetails } from 'types' import { Addressable } from 'types'
import { abbreviateNumber } from 'utils' import { abbreviateNumber } from 'utils'
type ZapProps = { type ZapProps = {
modDetails: ModDetails addressable: Addressable
} }
export const Zap = ({ modDetails }: ZapProps) => { export const Zap = ({ addressable }: ZapProps) => {
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
const [totalZappedAmount, setTotalZappedAmount] = useState(0) const [totalZappedAmount, setTotalZappedAmount] = useState(0)
const [hasZapped, setHasZapped] = useState(false) const [hasZapped, setHasZapped] = useState(false)
@ -26,9 +26,9 @@ export const Zap = ({ modDetails }: ZapProps) => {
useDidMount(() => { useDidMount(() => {
getTotalZapAmount( getTotalZapAmount(
modDetails.author, addressable.author,
modDetails.id, addressable.id,
modDetails.aTag, addressable.aTag,
userState.user?.pubkey as string userState.user?.pubkey as string
) )
.then((res) => { .then((res) => {
@ -70,9 +70,9 @@ export const Zap = ({ modDetails }: ZapProps) => {
</div> </div>
{isOpen && ( {isOpen && (
<ZapSplit <ZapSplit
pubkey={modDetails.author} pubkey={addressable.author}
eventId={modDetails.id} eventId={addressable.id}
aTag={modDetails.aTag} aTag={addressable.aTag}
setTotalZapAmount={setTotalZappedAmount} setTotalZapAmount={setTotalZappedAmount}
setHasZapped={setHasZapped} setHasZapped={setHasZapped}
handleClose={() => setIsOpen(false)} handleClose={() => setIsOpen(false)}

View File

@ -12,7 +12,7 @@ import { useComments } from 'hooks/useComments'
export const ModCard = React.memo((props: ModDetails) => { export const ModCard = React.memo((props: ModDetails) => {
const [totalZappedAmount, setTotalZappedAmount] = useState(0) const [totalZappedAmount, setTotalZappedAmount] = useState(0)
const [commentCount, setCommentCount] = useState(0) const [commentCount, setCommentCount] = useState(0)
const { commentEvents } = useComments(props) const { commentEvents } = useComments(props.author, props.aTag)
const { likesCount, disLikesCount } = useReactions({ const { likesCount, disLikesCount } = useReactions({
pubkey: props.author, pubkey: props.author,
eTag: props.id, eTag: props.id,

View File

@ -21,9 +21,9 @@ import { Link } from 'react-router-dom'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { getProfilePageRoute } from 'routes' import { getProfilePageRoute } from 'routes'
import { import {
Addressable,
CommentEvent, CommentEvent,
CommentEventStatus, CommentEventStatus,
ModDetails,
UserProfile UserProfile
} from 'types/index.ts' } from 'types/index.ts'
import { abbreviateNumber, hexToNpub, log, LogType, now } from 'utils' import { abbreviateNumber, hexToNpub, log, LogType, now } from 'utils'
@ -44,13 +44,16 @@ type FilterOptions = {
} }
type Props = { type Props = {
modDetails: ModDetails addressable: Addressable
setCommentCount: Dispatch<SetStateAction<number>> setCommentCount: Dispatch<SetStateAction<number>>
} }
export const Comments = ({ modDetails, setCommentCount }: Props) => { export const Comments = ({ addressable, setCommentCount }: Props) => {
const { ndk, publish } = useNDKContext() const { ndk, publish } = useNDKContext()
const { commentEvents, setCommentEvents } = useComments(modDetails) const { commentEvents, setCommentEvents } = useComments(
addressable.author,
addressable.aTag
)
const [filterOptions, setFilterOptions] = useState<FilterOptions>({ const [filterOptions, setFilterOptions] = useState<FilterOptions>({
sort: SortByEnum.Latest, sort: SortByEnum.Latest,
author: AuthorFilterEnum.All_Comments author: AuthorFilterEnum.All_Comments
@ -84,9 +87,9 @@ export const Comments = ({ modDetails, setCommentCount }: Props) => {
kind: kinds.ShortTextNote, kind: kinds.ShortTextNote,
created_at: now(), created_at: now(),
tags: [ tags: [
['e', modDetails.id], ['e', addressable.id],
['a', modDetails.aTag], ['a', addressable.aTag],
['p', modDetails.author] ['p', addressable.author]
] ]
} }
@ -176,7 +179,7 @@ export const Comments = ({ modDetails, setCommentCount }: Props) => {
let filteredComments = commentEvents let filteredComments = commentEvents
if (filterOptions.author === AuthorFilterEnum.Creator_Comments) { if (filterOptions.author === AuthorFilterEnum.Creator_Comments) {
filteredComments = filteredComments.filter( filteredComments = filteredComments.filter(
(comment) => comment.pubkey === modDetails.author (comment) => comment.pubkey === addressable.author
) )
} }
@ -187,7 +190,7 @@ export const Comments = ({ modDetails, setCommentCount }: Props) => {
} }
return filteredComments return filteredComments
}, [commentEvents, filterOptions, modDetails.author]) }, [commentEvents, filterOptions, addressable.author])
return ( return (
<div className='IBMSMSMBSSCommentsWrapper'> <div className='IBMSMSMBSSCommentsWrapper'>

View File

@ -7,22 +7,30 @@ import {
NDKSubscriptionCacheUsage NDKSubscriptionCacheUsage
} from '@nostr-dev-kit/ndk' } from '@nostr-dev-kit/ndk'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { CommentEvent, ModDetails, UserRelaysType } from 'types' import { CommentEvent, UserRelaysType } from 'types'
import { log, LogType, timeout } from 'utils' import { log, LogType, timeout } from 'utils'
import { useNDKContext } from './useNDKContext' import { useNDKContext } from './useNDKContext'
export const useComments = (mod: ModDetails) => { export const useComments = (
author: string | undefined,
aTag: string | undefined
) => {
const { ndk } = useNDKContext() const { ndk } = useNDKContext()
const [commentEvents, setCommentEvents] = useState<CommentEvent[]>([]) const [commentEvents, setCommentEvents] = useState<CommentEvent[]>([])
useEffect(() => { useEffect(() => {
if (!(author && aTag)) {
// Author and aTag are required
return
}
let subscription: NDKSubscription // Define the subscription variable here for cleanup let subscription: NDKSubscription // Define the subscription variable here for cleanup
const setupSubscription = async () => { const setupSubscription = async () => {
// Find the mod author's relays. // Find the mod author's relays.
const authorReadRelays = await Promise.race([ const authorReadRelays = await Promise.race([
getRelayListForUser(mod.author, ndk), getRelayListForUser(author, ndk),
timeout(10 * 1000) // add a 10 sec timeout timeout(10 * 1000) // add a 10 sec timeout
]) ])
.then((ndkRelayList) => { .then((ndkRelayList) => {
@ -33,7 +41,7 @@ export const useComments = (mod: ModDetails) => {
log( log(
true, true,
LogType.Error, LogType.Error,
`An error occurred in fetching user's (${mod.author}) ${UserRelaysType.Read}`, `An error occurred in fetching user's (${author}) ${UserRelaysType.Read}`,
err err
) )
return [] as string[] return [] as string[]
@ -41,7 +49,7 @@ export const useComments = (mod: ModDetails) => {
const filter: NDKFilter = { const filter: NDKFilter = {
kinds: [NDKKind.Text], kinds: [NDKKind.Text],
'#a': [mod.aTag] '#a': [aTag]
} }
const relayUrls = new Set<string>() const relayUrls = new Set<string>()
@ -92,7 +100,7 @@ export const useComments = (mod: ModDetails) => {
subscription.stop() subscription.stop()
} }
} }
}, [mod.aTag, mod.author, ndk]) }, [aTag, author, ndk])
return { return {
commentEvents, commentEvents,

View File

@ -1,18 +1,47 @@
import { useState } from 'react'
import { useLoaderData } from 'react-router-dom'
import StarterKit from '@tiptap/starter-kit'
import Link from '@tiptap/extension-link'
import Image from '@tiptap/extension-image'
import { EditorContent, useEditor } from '@tiptap/react'
import DOMPurify from 'dompurify'
import { marked } from 'marked'
import { LoadingSpinner } from 'components/LoadingSpinner' import { LoadingSpinner } from 'components/LoadingSpinner'
import { ProfileSection } from 'components/ProfileSection' import { ProfileSection } from 'components/ProfileSection'
import { useLoaderData } from 'react-router-dom' import { Comments } from 'components/comment'
import { BlogDetails } from 'types' import { Addressable, BlogDetails } from 'types'
import placeholder from '../../assets/img/DEGMods Placeholder Img.png'
import { PublishDetails } from 'components/Internal/PublishDetails'
import { Interactions } from 'components/Internal/Interactions'
export const BlogPage = () => { export const BlogPage = () => {
const data = useLoaderData() as Partial<BlogDetails> const data = useLoaderData() as Partial<BlogDetails>
const [commentCount, setCommentCount] = useState(0)
if (!data) return <LoadingSpinner desc={'Loading...'} /> const html = marked.parse(data?.content || '', { async: false })
const sanitized = DOMPurify.sanitize(html)
const editor = useEditor({
content: sanitized,
extensions: [
StarterKit,
Link,
Image.configure({
inline: true,
HTMLAttributes: {
class: 'IBMSMSMBSSPostImg'
}
})
],
editable: false
})
return ( return (
<div className='InnerBodyMain'> <div className='InnerBodyMain'>
<div className='ContainerMain'> <div className='ContainerMain'>
<div className='IBMSecMainGroup IBMSecMainGroupAlt'> <div className='IBMSecMainGroup IBMSecMainGroupAlt'>
<div className='IBMSMSplitMain'> <div className='IBMSMSplitMain'>
{!data ? (
<LoadingSpinner desc={'Loading...'} />
) : (
<div className='IBMSMSplitMainBigSide'> <div className='IBMSMSplitMainBigSide'>
<div className='IBMSMSplitMainBigSideSec'> <div className='IBMSMSplitMainBigSideSec'>
{/* <div className="IBMSMSMBSSModFor"> {/* <div className="IBMSMSMBSSModFor">
@ -37,82 +66,44 @@ export const BlogPage = () => {
<div <div
className='IBMSMSMBSSPostPicture' className='IBMSMSMBSSPostPicture'
style={{ style={{
background: `url("https://image.nostr.build/d6af39fb1d47feaf09831ddf9d447ccc435ba10fcbb9b6d6e800390f6bbac851.png") center / cover no-repeat` background: `url("${
data.image !== '' ? data.image : placeholder
}") center / cover no-repeat`
}} }}
></div> ></div>
<div className='IBMSMSMBSSPostInside'> <div className='IBMSMSMBSSPostInside'>
<div className='IBMSMSMBSSPostTitle'> <div className='IBMSMSMBSSPostTitle'>
<h1 className='IBMSMSMBSSPostTitleHeading'>Heading</h1> <h1 className='IBMSMSMBSSPostTitleHeading'>
{data.title}
</h1>
</div>
<div className='IBMSMSMBSSPostBody'>
<EditorContent editor={editor} />
</div> </div>
<div className='IBMSMSMBSSPostBody'></div>
<div className='IBMSMSMBSSTags'> <div className='IBMSMSMBSSTags'>
{data.nsfw && (
<div className='IBMSMSMBSSTagsTag IBMSMSMBSSTagsTagNSFW'> <div className='IBMSMSMBSSTagsTag IBMSMSMBSSTagsTagNSFW'>
<p>NSFW</p> <p>NSFW</p>
</div> </div>
<a className='IBMSMSMBSSTagsTag'>Tag 1</a> )}
<a className='IBMSMSMBSSTagsTag'>Tag 2</a> {data.tTags &&
<a className='IBMSMSMBSSTagsTag'>Tag 3</a> data.tTags.map((t) => (
</div> <a key={t} className='IBMSMSMBSSTagsTag'>
</div> {t}
</div>
{/* <div className="IBMSMSplitMainBigSideSec">
<div className="IBMSMSMBSS_Details">
<a href="#commentsArea" style="text-decoration: unset;color: unset;">
<div className="IBMSMSMBSS_Details_Card IBMSMSMBSS_D_CComments">
<div className="IBMSMSMBSS_Details_CardVisual"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" className="IBMSMSMBSS_Details_CardVisualIcon">
<path d="M256 31.1c-141.4 0-255.1 93.12-255.1 208c0 49.62 21.35 94.98 56.97 130.7c-12.5 50.37-54.27 95.27-54.77 95.77c-2.25 2.25-2.875 5.734-1.5 8.734c1.249 3 4.021 4.766 7.271 4.766c66.25 0 115.1-31.76 140.6-51.39c32.63 12.25 69.02 19.39 107.4 19.39c141.4 0 255.1-93.13 255.1-207.1S397.4 31.1 256 31.1zM127.1 271.1c-17.75 0-32-14.25-32-31.1s14.25-32 32-32s32 14.25 32 32S145.7 271.1 127.1 271.1zM256 271.1c-17.75 0-31.1-14.25-31.1-31.1s14.25-32 31.1-32s31.1 14.25 31.1 32S273.8 271.1 256 271.1zM383.1 271.1c-17.75 0-32-14.25-32-31.1s14.25-32 32-32s32 14.25 32 32S401.7 271.1 383.1 271.1z"></path>
</svg></div>
<p className="IBMSMSMBSS_Details_CardText">420</p>
</div>
</a> </a>
<div id="reactBolt" className="IBMSMSMBSS_Details_Card IBMSMSMBSS_D_CBolt"> ))}
<div className="IBMSMSMBSS_Details_CardVisual"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-64 0 512 512" width="1em" height="1em" fill="currentColor" className="IBMSMSMBSS_Details_CardVisualIcon">
<path d="M240.5 224H352C365.3 224 377.3 232.3 381.1 244.7C386.6 257.2 383.1 271.3 373.1 280.1L117.1 504.1C105.8 513.9 89.27 514.7 77.19 505.9C65.1 497.1 60.7 481.1 66.59 467.4L143.5 288H31.1C18.67 288 6.733 279.7 2.044 267.3C-2.645 254.8 .8944 240.7 10.93 231.9L266.9 7.918C278.2-1.92 294.7-2.669 306.8 6.114C318.9 14.9 323.3 30.87 317.4 44.61L240.5 224z"></path>
</svg></div>
<p className="IBMSMSMBSS_Details_CardText">69k</p>
<div className="IBMSMSMBSSCL_CAElementLoadWrapper">
<div className="IBMSMSMBSSCL_CAElementLoad"></div>
</div>
</div>
<div id="reactUp" className="IBMSMSMBSS_Details_Card IBMSMSMBSS_D_CReactUp IBMSMSMBSS_D_CRUActive">
<div className="IBMSMSMBSS_Details_CardVisual"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" className="IBMSMSMBSS_Details_CardVisualIcon">
<path d="M0 190.9V185.1C0 115.2 50.52 55.58 119.4 44.1C164.1 36.51 211.4 51.37 244 84.02L256 96L267.1 84.02C300.6 51.37 347 36.51 392.6 44.1C461.5 55.58 512 115.2 512 185.1V190.9C512 232.4 494.8 272.1 464.4 300.4L283.7 469.1C276.2 476.1 266.3 480 256 480C245.7 480 235.8 476.1 228.3 469.1L47.59 300.4C17.23 272.1 .0003 232.4 .0003 190.9L0 190.9z"></path>
</svg></div>
<p className="IBMSMSMBSS_Details_CardText">4.2k</p>
<div className="IBMSMSMBSSCL_CAElementLoadWrapper">
<div className="IBMSMSMBSSCL_CAElementLoad"></div>
</div>
</div>
<div id="reactDown" className="IBMSMSMBSS_Details_Card IBMSMSMBSS_D_CReactDown">
<div className="IBMSMSMBSS_Details_CardVisual"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" className="IBMSMSMBSS_Details_CardVisualIcon">
<path d="M512 440.1C512 479.9 479.7 512 439.1 512H71.92C32.17 512 0 479.8 0 440c0-35.88 26.19-65.35 60.56-70.85C43.31 356 32 335.4 32 312C32 272.2 64.25 240 104 240h13.99C104.5 228.2 96 211.2 96 192c0-35.38 28.56-64 63.94-64h16C220.1 128 256 92.12 256 48c0-17.38-5.784-33.35-15.16-46.47C245.8 .7754 250.9 0 256 0c53 0 96 43 96 96c0 11.25-2.288 22-5.913 32h5.879C387.3 128 416 156.6 416 192c0 19.25-8.59 36.25-22.09 48H408C447.8 240 480 272.2 480 312c0 23.38-11.38 44.01-28.63 57.14C485.7 374.6 512 404.3 512 440.1z"></path>
</svg></div>
<p className="IBMSMSMBSS_Details_CardText">69</p>
<div className="IBMSMSMBSSCL_CAElementLoadWrapper">
<div className="IBMSMSMBSSCL_CAElementLoad"></div>
</div> </div>
</div> </div>
</div> </div>
</div> <Interactions
<div className="IBMSMSplitMainBigSideSec"> addressable={data as Addressable}
<div className="IBMSMSMBSSPost_PostDetails"> commentCount={commentCount}
<div data-bs-toggle="tooltip" data-bss-tooltip="" data-bs-placement="left" className="IBMSMSMBSSPost_PDElement" title="Publish date"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" className="IBMSMSMBSSPost_PDElementIcon" title="Publish date"> />
<path d="M480 32H128C110.3 32 96 46.33 96 64v336C96 408.8 88.84 416 80 416S64 408.8 64 400V96H32C14.33 96 0 110.3 0 128v288c0 35.35 28.65 64 64 64h384c35.35 0 64-28.65 64-64V64C512 46.33 497.7 32 480 32zM272 416h-96C167.2 416 160 408.8 160 400C160 391.2 167.2 384 176 384h96c8.836 0 16 7.162 16 16C288 408.8 280.8 416 272 416zM272 320h-96C167.2 320 160 312.8 160 304C160 295.2 167.2 288 176 288h96C280.8 288 288 295.2 288 304C288 312.8 280.8 320 272 320zM432 416h-96c-8.836 0-16-7.164-16-16c0-8.838 7.164-16 16-16h96c8.836 0 16 7.162 16 16C448 408.8 440.8 416 432 416zM432 320h-96C327.2 320 320 312.8 320 304C320 295.2 327.2 288 336 288h96C440.8 288 448 295.2 448 304C448 312.8 440.8 320 432 320zM448 208C448 216.8 440.8 224 432 224h-256C167.2 224 160 216.8 160 208v-96C160 103.2 167.2 96 176 96h256C440.8 96 448 103.2 448 112V208z"></path> <PublishDetails
</svg> published_at={data.published_at || 0}
<p className="IBMSMSMBSSPost_PDElementText">01/11/2024</p> edited_at={data.edited_at || 0}
</div> site={data.rTag || 'N/A'}
<div data-bs-toggle="tooltip" data-bss-tooltip="" data-bs-placement="left" className="IBMSMSMBSSPost_PDElement" title="Last modified"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" className="IBMSMSMBSSPost_PDElementIcon"> />
<path d="M362.7 19.32C387.7-5.678 428.3-5.678 453.3 19.32L492.7 58.75C517.7 83.74 517.7 124.3 492.7 149.3L444.3 197.7L314.3 67.72L362.7 19.32zM421.7 220.3L188.5 453.4C178.1 463.8 165.2 471.5 151.1 475.6L30.77 511C22.35 513.5 13.24 511.2 7.03 504.1C.8198 498.8-1.502 489.7 .976 481.2L36.37 360.9C40.53 346.8 48.16 333.9 58.57 323.5L291.7 90.34L421.7 220.3z"></path>
</svg>
<p className="IBMSMSMBSSPost_PDElementText">24/11/2024</p>
</div><a data-bs-toggle="tooltip" data-bss-tooltip="" data-bs-placement="left" className="IBMSMSMBSSPost_PDElement IBMSMSMBSSPost_PDElementLink" href="#" title="Published on" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -64 640 640" width="1em" height="1em" fill="currentColor" className="IBMSMSMBSSPost_PDElementIcon">
<path d="M172.5 131.1C228.1 75.51 320.5 75.51 376.1 131.1C426.1 181.1 433.5 260.8 392.4 318.3L391.3 319.9C381 334.2 361 337.6 346.7 327.3C332.3 317 328.9 297 339.2 282.7L340.3 281.1C363.2 249 359.6 205.1 331.7 177.2C300.3 145.8 249.2 145.8 217.7 177.2L105.5 289.5C73.99 320.1 73.99 372 105.5 403.5C133.3 431.4 177.3 435 209.3 412.1L210.9 410.1C225.3 400.7 245.3 404 255.5 418.4C265.8 432.8 262.5 452.8 248.1 463.1L246.5 464.2C188.1 505.3 110.2 498.7 60.21 448.8C3.741 392.3 3.741 300.7 60.21 244.3L172.5 131.1zM467.5 380C411 436.5 319.5 436.5 263 380C213 330 206.5 251.2 247.6 193.7L248.7 192.1C258.1 177.8 278.1 174.4 293.3 184.7C307.7 194.1 311.1 214.1 300.8 229.3L299.7 230.9C276.8 262.1 280.4 306.9 308.3 334.8C339.7 366.2 390.8 366.2 422.3 334.8L534.5 222.5C566 191 566 139.1 534.5 108.5C506.7 80.63 462.7 76.99 430.7 99.9L429.1 101C414.7 111.3 394.7 107.1 384.5 93.58C374.2 79.2 377.5 59.21 391.9 48.94L393.5 47.82C451 6.731 529.8 13.25 579.8 63.24C636.3 119.7 636.3 211.3 579.8 267.7L467.5 380z"></path>
</svg>
<p className="IBMSMSMBSSPost_PDElementText">degmods.com</p>
</a>
</div>
</div>
</div> */}
{/* <div className="IBMSMSplitMainBigSideSec"> {/* <div className="IBMSMSplitMainBigSideSec">
<div className="IBMSMSMBSSPostsWrapper"> <div className="IBMSMSMBSSPostsWrapper">
<h4 className="IBMSMSMBSSPostsTitle">Latest POSTER-NAME Posts</h4> <h4 className="IBMSMSMBSSPostsTitle">Latest POSTER-NAME Posts</h4>
@ -137,88 +128,16 @@ export const BlogPage = () => {
</a></div> </a></div>
</div> </div>
</div> */} </div> */}
{/* <div className="IBMSMSplitMainBigSideSec"> <div className='IBMSMSplitMainBigSideSec'>
<div className="IBMSMSMBSSCommentsWrapper"> <Comments
<h4 className="IBMSMSMBSSTitle">Comments</h4> addressable={data as Addressable}
<div id="ArticleComments-1" className="IBMSMSMBSSComments"> setCommentCount={setCommentCount}
<div className="IBMSMSMBSSCommentsCreation"> />
<div className="IBMSMSMBSSCC_Top"><textarea id="commentBox-1" className="IBMSMSMBSSCC_Top_Box"></textarea></div>
<div className="IBMSMSMBSSCC_Bottom"><a className="IBMSMSMBSSCC_BottomButton">Comment<div className="IBMSMSMBSSCL_CAElementLoadWrapper">
<div className="IBMSMSMBSSCL_CAElementLoad"></div>
</div></a></div>
</div>
<div className="CommentsToggle"><button className="btn btnMain CommentsToggleBtn CommentsToggleActive" type="button">All Comments</button><button className="btn btnMain CommentsToggleBtn" type="button">Creator Comments</button></div>
<div className="IBMSMSMBSSCommentsList">
<div className="IBMSMSMBSSCL_Comment">
<div className="IBMSMSMBSSCL_CommentTop">
<div className="IBMSMSMBSSCL_CommentTopPPWrapper"><a className="IBMSMSMBSSCL_CommentTopPP" href="profile.html" style="background: url(&quot;assets/img/media-cache%20(4).png&quot;) center / cover no-repeat;"></a></div>
<div className="IBMSMSMBSSCL_CommentTopDetailsWrapper">
<div className="IBMSMSMBSSCL_CommentTopDetails"><a className="IBMSMSMBSSCL_CTD_Name" href="profile.html">FreakoverseFreakoverseFreakoverse</a><a className="IBMSMSMBSSCL_CTD_Address" href="profile.html">npub18n4ysp43ux5c98fs6h9c57qpr4p8r3j8f6e32v0vj8egzy878aqqyzzk9r</a></div>
<div className="IBMSMSMBSSCL_CommentActionsDetails"><a className="IBMSMSMBSSCL_CADTime" href="feed-note.html">8:45 PM</a><a className="IBMSMSMBSSCL_CADDate" href="feed-note.html">02/05/2024</a></div>
</div>
</div>
<div className="IBMSMSMBSSCL_CommentBottom">
<p className="IBMSMSMBSSCL_CBText">Yo this article was insane to read!</p>
</div>
<div className="IBMSMSMBSSCL_CommentActions">
<div className="IBMSMSMBSSCL_CommentActionsInside">
<div className="IBMSMSMBSSCL_CAElement IBMSMSMBSSCL_CAEUp IBMSMSMBSSCL_CAEUpActive"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" className="IBMSMSMBSSCL_CAElementIcon">
<!--! Font Awesome Free 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. -->
<path d="M0 190.9V185.1C0 115.2 50.52 55.58 119.4 44.1C164.1 36.51 211.4 51.37 244 84.02L256 96L267.1 84.02C300.6 51.37 347 36.51 392.6 44.1C461.5 55.58 512 115.2 512 185.1V190.9C512 232.4 494.8 272.1 464.4 300.4L283.7 469.1C276.2 476.1 266.3 480 256 480C245.7 480 235.8 476.1 228.3 469.1L47.59 300.4C17.23 272.1 .0003 232.4 .0003 190.9L0 190.9z"></path>
</svg>
<p className="IBMSMSMBSSCL_CAElementText">52</p>
<div className="IBMSMSMBSSCL_CAElementLoadWrapper">
<div className="IBMSMSMBSSCL_CAElementLoad"></div>
</div>
</div>
<div className="IBMSMSMBSSCL_CAElement IBMSMSMBSSCL_CAEDown"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" className="IBMSMSMBSSCL_CAElementIcon">
<!--! Font Awesome Free 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. -->
<path d="M512 440.1C512 479.9 479.7 512 439.1 512H71.92C32.17 512 0 479.8 0 440c0-35.88 26.19-65.35 60.56-70.85C43.31 356 32 335.4 32 312C32 272.2 64.25 240 104 240h13.99C104.5 228.2 96 211.2 96 192c0-35.38 28.56-64 63.94-64h16C220.1 128 256 92.12 256 48c0-17.38-5.784-33.35-15.16-46.47C245.8 .7754 250.9 0 256 0c53 0 96 43 96 96c0 11.25-2.288 22-5.913 32h5.879C387.3 128 416 156.6 416 192c0 19.25-8.59 36.25-22.09 48H408C447.8 240 480 272.2 480 312c0 23.38-11.38 44.01-28.63 57.14C485.7 374.6 512 404.3 512 440.1z"></path>
</svg>
<p className="IBMSMSMBSSCL_CAElementText">4</p>
<div className="IBMSMSMBSSCL_CAElementLoadWrapper">
<div className="IBMSMSMBSSCL_CAElementLoad"></div>
</div>
</div>
<div className="IBMSMSMBSSCL_CAElement IBMSMSMBSSCL_CAERepost IBMSMSMBSSCL_CAERepostActive"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -64 640 640" width="1em" height="1em" fill="currentColor" className="IBMSMSMBSSCL_CAElementIcon">
<!--! Font Awesome Free 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. -->
<path d="M614.2 334.8C610.5 325.8 601.7 319.1 592 319.1H544V176C544 131.9 508.1 96 464 96h-128c-17.67 0-32 14.31-32 32s14.33 32 32 32h128C472.8 160 480 167.2 480 176v143.1h-48c-9.703 0-18.45 5.844-22.17 14.82s-1.656 19.29 5.203 26.16l80 80.02C499.7 445.7 505.9 448 512 448s12.28-2.344 16.97-7.031l80-80.02C615.8 354.1 617.9 343.8 614.2 334.8zM304 352h-128C167.2 352 160 344.8 160 336V192h48c9.703 0 18.45-5.844 22.17-14.82s1.656-19.29-5.203-26.16l-80-80.02C140.3 66.34 134.1 64 128 64S115.7 66.34 111 71.03l-80 80.02C24.17 157.9 22.11 168.2 25.83 177.2S38.3 192 48 192H96V336C96 380.1 131.9 416 176 416h128c17.67 0 32-14.31 32-32S321.7 352 304 352z"></path>
</svg>
<p className="IBMSMSMBSSCL_CAElementText">6</p>
<div className="IBMSMSMBSSCL_CAElementLoadWrapper">
<div className="IBMSMSMBSSCL_CAElementLoad"></div>
</div>
</div>
<div className="IBMSMSMBSSCL_CAElement IBMSMSMBSSCL_CAEBolt IBMSMSMBSSCL_CAEBoltActive"><svg xmlns="http://www.w3.org/2000/svg" viewBox="-64 0 512 512" width="1em" height="1em" fill="currentColor" className="IBMSMSMBSSCL_CAElementIcon">
<!--! Font Awesome Free 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. -->
<path d="M240.5 224H352C365.3 224 377.3 232.3 381.1 244.7C386.6 257.2 383.1 271.3 373.1 280.1L117.1 504.1C105.8 513.9 89.27 514.7 77.19 505.9C65.1 497.1 60.7 481.1 66.59 467.4L143.5 288H31.1C18.67 288 6.733 279.7 2.044 267.3C-2.645 254.8 .8944 240.7 10.93 231.9L266.9 7.918C278.2-1.92 294.7-2.669 306.8 6.114C318.9 14.9 323.3 30.87 317.4 44.61L240.5 224z"></path>
</svg>
<p className="IBMSMSMBSSCL_CAElementText">500K</p>
<div className="IBMSMSMBSSCL_CAElementLoadWrapper">
<div className="IBMSMSMBSSCL_CAElementLoad"></div>
</div>
</div>
<div className="IBMSMSMBSSCL_CAElement IBMSMSMBSSCL_CAEReplies"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="1em" height="1em" fill="currentColor" className="IBMSMSMBSSCL_CAElementIcon">
<!--! Font Awesome Free 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. -->
<path d="M256 32C114.6 32 .0272 125.1 .0272 240c0 49.63 21.35 94.98 56.97 130.7c-12.5 50.37-54.27 95.27-54.77 95.77c-2.25 2.25-2.875 5.734-1.5 8.734C1.979 478.2 4.75 480 8 480c66.25 0 115.1-31.76 140.6-51.39C181.2 440.9 217.6 448 256 448c141.4 0 255.1-93.13 255.1-208S397.4 32 256 32z"></path>
</svg>
<p className="IBMSMSMBSSCL_CAElementText">12</p>
<p className="IBMSMSMBSSCL_CAElementText">Replies</p>
</div>
<div className="IBMSMSMBSSCL_CAElement IBMSMSMBSSCL_CAEReply">
<p className="IBMSMSMBSSCL_CAElementText">Reply</p>
</div> </div>
</div> </div>
</div> </div>
</div> )}
</div> {!!data?.author && <ProfileSection pubkey={data.author} />}
</div>
</div>
</div>
</div> */}
</div>
</div>
{!!data.author && <ProfileSection pubkey={data.author} />}
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,16 +1,10 @@
import { filterForEventsTaggingId, NDKEvent } from '@nostr-dev-kit/ndk' import { filterForEventsTaggingId } from '@nostr-dev-kit/ndk'
import { NDKContextType } from 'contexts/NDKContext' import { NDKContextType } from 'contexts/NDKContext'
import { LoaderFunctionArgs, redirect } from 'react-router-dom' import { LoaderFunctionArgs, redirect } from 'react-router-dom'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { appRoutes } from 'routes' import { appRoutes } from 'routes'
import { BlogDetails } from 'types' import { log, LogType } from 'utils'
import { import { extractBlogDetails } from 'utils/blog'
getFirstTagValue,
getFirstTagValueAsInt,
getTagValue,
log,
LogType
} from 'utils'
export const blogRouteLoader = export const blogRouteLoader =
(ndkContext: NDKContextType) => (ndkContext: NDKContextType) =>
@ -23,21 +17,20 @@ export const blogRouteLoader =
try { try {
const filter = filterForEventsTaggingId(naddr) const filter = filterForEventsTaggingId(naddr)
if (!filter) { if (!filter) {
log(true, LogType.Error, 'Unable to create filter from blog naddr.') log(true, LogType.Error, 'Unable to create filter from blog naddr.')
return redirect(appRoutes.blogs) return redirect(appRoutes.blogs)
} }
const event = await ndkContext.fetchEvent(filter) const event = await ndkContext.fetchEvent(filter)
console.log(event) if (!event) {
if (event) { log(true, LogType.Error, 'Unable to fetch the blog event.')
const blogDetails = extractBlogDetails(event) return null
console.log(blogDetails)
return blogDetails
} }
return null const blogDetails = extractBlogDetails(event)
return blogDetails
} catch (error) { } catch (error) {
log( log(
true, true,
@ -45,26 +38,7 @@ export const blogRouteLoader =
'An error occurred in fetching blog details from relays', 'An error occurred in fetching blog details from relays',
error error
) )
toast.error('An error occurred in fetching mod details from relays') toast.error('An error occurred in fetching blog details from relays')
return redirect(appRoutes.blogs) return redirect(appRoutes.blogs)
} }
} }
function extractBlogDetails(event: NDKEvent): Partial<BlogDetails> {
return {
title: getFirstTagValue(event, 'title'),
content: event.content,
summary: getFirstTagValue(event, 'summary'),
image: getFirstTagValue(event, 'image'),
nsfw: getFirstTagValue(event, 'nsfw') === 'true',
id: event.id,
author: event.pubkey,
published_at: getFirstTagValueAsInt(event, 'published_at'),
edited_at: event.created_at,
rTag: getFirstTagValue(event, 'r') || 'N/A',
dTag: getFirstTagValue(event, 'd'),
aTag: getFirstTagValue(event, 'a'),
tTags: getTagValue(event, 't') || []
}
}

View File

@ -1,144 +0,0 @@
import { BlogCard } from '../components/BlogCard'
import '../styles/filters.css'
import '../styles/pagination.css'
import '../styles/search.css'
import '../styles/styles.css'
import placeholder from '../assets/img/DEGMods Placeholder Img.png'
export const BlogsPage = () => {
return (
<div className='InnerBodyMain'>
<div className='ContainerMain'>
<div className='IBMSecMainGroup IBMSecMainGroupAlt'>
<div className='IBMSecMain'>
<div className='SearchMainWrapper'>
<div className='IBMSMTitleMain'>
<h2 className='IBMSMTitleMainHeading'>Blogs (WIP)</h2>
</div>
<div className='SearchMain'>
<div className='SearchMainInside'>
<div className='SearchMainInsideWrapper'>
<input type='text' className='SMIWInput' />
<button className='btn btnMain SMIWButton' type='button'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M500.3 443.7l-119.7-119.7c27.22-40.41 40.65-90.9 33.46-144.7C401.8 87.79 326.8 13.32 235.2 1.723C99.01-15.51-15.51 99.01 1.724 235.2c11.6 91.64 86.08 166.7 177.6 178.9c53.8 7.189 104.3-6.236 144.7-33.46l119.7 119.7c15.62 15.62 40.95 15.62 56.57 0C515.9 484.7 515.9 459.3 500.3 443.7zM79.1 208c0-70.58 57.42-128 128-128s128 57.42 128 128c0 70.58-57.42 128-128 128S79.1 278.6 79.1 208z'></path>
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
<div className='IBMSecMain'>
<div className='FiltersMain'>
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
Latest
</button>
<div className='dropdown-menu dropdownMainMenu'>
<a className='dropdown-item dropdownMainMenuItem' href='#'>
Latest
</a>
<a className='dropdown-item dropdownMainMenuItem' href='#'>
Oldest
</a>
<a className='dropdown-item dropdownMainMenuItem' href='#'>
Best Rated
</a>
<a className='dropdown-item dropdownMainMenuItem' href='#'>
Worst Rated
</a>
</div>
</div>
</div>
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
Hide NSFW
</button>
<div className='dropdown-menu dropdownMainMenu'>
<a className='dropdown-item dropdownMainMenuItem' href='#'>
Hide NSFW
</a>
<a className='dropdown-item dropdownMainMenuItem' href='#'>
Show NSFW
<br />
</a>
<a className='dropdown-item dropdownMainMenuItem' href='#'>
Only show NSFW
</a>
</div>
</div>
</div>
</div>
</div>
<div className='IBMSecMain IBMSMListWrapper'>
<div className='IBMSMList'>
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
</div>
</div>
<div className='IBMSecMain'>
<div className='PaginationMain'>
<div className='PaginationMainInside'>
<a
className='PaginationMainInsideBox PaginationMainInsideBoxArrows'
href='#'
>
<i className='fas fa-chevron-left'></i>
</a>
<div className='PaginationMainInsideBoxGroup'>
<a className='PaginationMainInsideBox PMIBActive' href='#'>
<p>1</p>{' '}
</a>
<a className='PaginationMainInsideBox' href='#'>
<p>2</p>{' '}
</a>
<a className='PaginationMainInsideBox' href='#'>
<p>3</p>
</a>
<p className='PaginationMainInsideBox PMIBDots'>...</p>
<a className='PaginationMainInsideBox' href='#'>
<p>8</p>
</a>
</div>
<a
className='PaginationMainInsideBox PaginationMainInsideBoxArrows'
href='#'
>
<i className='fas fa-chevron-right'></i>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
)
}

205
src/pages/blogs/index.tsx Normal file
View File

@ -0,0 +1,205 @@
import { useMemo, useRef, useState } from 'react'
import { useLoaderData, useSearchParams } from 'react-router-dom'
import { useLocalStorage } from 'hooks'
import { BlogCardDetails, NSFWFilter, SortBy } from 'types'
import { SearchInput } from '../../components/SearchInput'
import { BlogCard } from '../../components/BlogCard'
import '../../styles/filters.css'
import '../../styles/pagination.css'
import '../../styles/search.css'
import '../../styles/styles.css'
export const BlogsPage = () => {
const blogs = useLoaderData() as Partial<BlogCardDetails>[] | undefined
const [filterOptions, setFilterOptions] = useLocalStorage('filter-blog', {
sort: SortBy.Latest,
nsfw: NSFWFilter.Hide_NSFW
})
// Search
const searchTermRef = useRef<HTMLInputElement>(null)
const [searchParams, setSearchParams] = useSearchParams()
const [searchTerm, setSearchTerm] = useState(searchParams.get('q') || '')
const handleSearch = () => {
const value = searchTermRef.current?.value || '' // Access the input value from the ref
setSearchTerm(value)
if (value) {
searchParams.set('q', value)
} else {
searchParams.delete('q')
}
setSearchParams(searchParams, {
replace: true
})
}
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
handleSearch()
}
}
// Filter
const filteredBlogs = useMemo(() => {
const filterNsfwFn = (blog: Partial<BlogCardDetails>) => {
switch (filterOptions.nsfw) {
case NSFWFilter.Hide_NSFW:
return !blog.nsfw
case NSFWFilter.Only_NSFW:
return blog.nsfw
default:
return blog
}
}
let filtered = blogs?.filter(filterNsfwFn) || []
const lowerCaseSearchTerm = searchTerm.toLowerCase()
if (searchTerm !== '') {
const filterSearchTermFn = (blog: Partial<BlogCardDetails>) =>
(blog.title || '').toLowerCase().includes(lowerCaseSearchTerm) ||
(blog.summary || '').toLowerCase().includes(lowerCaseSearchTerm) ||
(blog.content || '').toLowerCase().includes(lowerCaseSearchTerm) ||
(blog.tTags || []).findIndex((tag) =>
tag.toLowerCase().includes(lowerCaseSearchTerm)
) > -1
filtered = filtered.filter(filterSearchTermFn)
}
if (filterOptions.sort === SortBy.Latest) {
filtered.sort((a, b) =>
a.published_at && b.published_at ? b.published_at - a.published_at : 0
)
} else if (filterOptions.sort === SortBy.Oldest) {
filtered.sort((a, b) =>
a.published_at && b.published_at ? a.published_at - b.published_at : 0
)
}
return filtered
}, [blogs, searchTerm, filterOptions.sort, filterOptions.nsfw])
return (
<div className='InnerBodyMain'>
<div className='ContainerMain'>
<div className='IBMSecMainGroup IBMSecMainGroupAlt'>
<div className='IBMSecMain'>
<div className='SearchMainWrapper'>
<div className='IBMSMTitleMain'>
<h2 className='IBMSMTitleMainHeading'>Blogs</h2>
</div>
<SearchInput
ref={searchTermRef}
handleKeyDown={handleKeyDown}
handleSearch={handleSearch}
/>
</div>
</div>
<div className='IBMSecMain'>
<div className='FiltersMain'>
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{filterOptions.sort}
</button>
<div className='dropdown-menu dropdownMainMenu'>
{Object.values(SortBy).map((item, index) => (
<div
key={`sortByItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
sort: item
}))
}
>
{item}
</div>
))}
</div>
</div>
</div>
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{filterOptions.nsfw}
</button>
<div className='dropdown-menu dropdownMainMenu'>
{Object.values(NSFWFilter).map((item, index) => (
<div
key={`nsfwFilterItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
nsfw: item
}))
}
>
{item}
</div>
))}
</div>
</div>
</div>
</div>
<div className='IBMSecMain IBMSMListWrapper'>
<div className='IBMSMList'>
{filteredBlogs &&
filteredBlogs.map((b) => <BlogCard key={b.id} {...b} />)}
</div>
</div>
<div className='IBMSecMain'>
<div className='PaginationMain'>
<div className='PaginationMainInside'>
<a
className='PaginationMainInsideBox PaginationMainInsideBoxArrows'
href='#'
>
<i className='fas fa-chevron-left'></i>
</a>
<div className='PaginationMainInsideBoxGroup'>
<a className='PaginationMainInsideBox PMIBActive' href='#'>
<p>1</p>{' '}
</a>
<a className='PaginationMainInsideBox' href='#'>
<p>2</p>{' '}
</a>
<a className='PaginationMainInsideBox' href='#'>
<p>3</p>
</a>
<p className='PaginationMainInsideBox PMIBDots'>...</p>
<a className='PaginationMainInsideBox' href='#'>
<p>8</p>
</a>
</div>
<a
className='PaginationMainInsideBox PaginationMainInsideBoxArrows'
href='#'
>
<i className='fas fa-chevron-right'></i>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}

37
src/pages/blogs/loader.ts Normal file
View File

@ -0,0 +1,37 @@
import { NDKFilter } from '@nostr-dev-kit/ndk'
import { NDKContextType } from 'contexts/NDKContext'
import { kinds } from 'nostr-tools'
import { toast } from 'react-toastify'
import { log, LogType, npubToHex } from 'utils'
import { extractBlogCardDetails } from 'utils/blog'
export const blogsRouteLoader = (ndkContext: NDKContextType) => async () => {
try {
const blogNpubs = import.meta.env.VITE_BLOG_NPUBS.split(',')
const blogHexkeys = blogNpubs
.map(npubToHex)
.filter((hexkey) => hexkey !== null)
const filter: NDKFilter = {
authors: blogHexkeys,
kinds: [kinds.LongFormArticle]
}
const events = await ndkContext.fetchEvents(filter)
if (!events) {
log(true, LogType.Error, 'Unable to fetch the blog events.')
return null
}
return events.map(extractBlogCardDetails).filter((e) => e.naddr)
} catch (error) {
log(
true,
LogType.Error,
'An error occurred in fetching blog details from relays',
error
)
toast.error('An error occurred in fetching blog details from relays')
return null
}
}

View File

@ -119,10 +119,10 @@ export const HomePage = () => {
<h2 className='IBMSMTitleMainHeading'>Blog Posts (WIP)</h2> <h2 className='IBMSMTitleMainHeading'>Blog Posts (WIP)</h2>
</div> </div>
<div className='IBMSMList'> <div className='IBMSMList'>
<BlogCard backgroundLink={placeholder} /> <BlogCard image={placeholder} />
<BlogCard backgroundLink={placeholder} /> <BlogCard image={placeholder} />
<BlogCard backgroundLink={placeholder} /> <BlogCard image={placeholder} />
<BlogCard backgroundLink={placeholder} /> <BlogCard image={placeholder} />
</div> </div>
<div className='IBMSMAction'> <div className='IBMSMAction'>

View File

@ -2,7 +2,6 @@ import { NDKFilter, NDKKind } from '@nostr-dev-kit/ndk'
import Link from '@tiptap/extension-link' import Link from '@tiptap/extension-link'
import { EditorContent, useEditor } from '@tiptap/react' import { EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit' import StarterKit from '@tiptap/starter-kit'
import { formatDate } from 'date-fns'
import FsLightbox from 'fslightbox-react' import FsLightbox from 'fslightbox-react'
import { nip19, UnsignedEvent } from 'nostr-tools' import { nip19, UnsignedEvent } from 'nostr-tools'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
@ -31,7 +30,6 @@ import '../../styles/tags.css'
import '../../styles/write.css' import '../../styles/write.css'
import { DownloadUrl, ModDetails, UserRelaysType } from '../../types' import { DownloadUrl, ModDetails, UserRelaysType } from '../../types'
import { import {
abbreviateNumber,
copyTextToClipboard, copyTextToClipboard,
downloadFile, downloadFile,
extractModData, extractModData,
@ -43,11 +41,11 @@ import {
sendDMUsingRandomKey, sendDMUsingRandomKey,
signAndPublish signAndPublish
} from '../../utils' } from '../../utils'
import { Comments } from './internal/comment' import { Comments } from '../../components/comment'
import { Reactions } from './internal/reactions'
import { Zap } from './internal/zap'
import { CheckboxField } from 'components/Inputs' import { CheckboxField } from 'components/Inputs'
import placeholder from '../../assets/img/DEGMods Placeholder Img.png' import placeholder from '../../assets/img/DEGMods Placeholder Img.png'
import { PublishDetails } from 'components/Internal/PublishDetails'
import { Interactions } from 'components/Internal/Interactions'
export const ModPage = () => { export const ModPage = () => {
const { naddr } = useParams() const { naddr } = useParams()
@ -143,7 +141,7 @@ export const ModPage = () => {
nsfw={modData.nsfw} nsfw={modData.nsfw}
/> />
<Interactions <Interactions
modDetails={modData} addressable={modData}
commentCount={commentCount} commentCount={commentCount}
/> />
<PublishDetails <PublishDetails
@ -197,15 +195,15 @@ export const ModPage = () => {
Creator's Blog Posts (WIP) Creator's Blog Posts (WIP)
</h4> </h4>
<div className='IBMSMList IBMSMListAlt'> <div className='IBMSMList IBMSMListAlt'>
<BlogCard backgroundLink={placeholder} /> <BlogCard image={placeholder} />
<BlogCard backgroundLink={placeholder} /> <BlogCard image={placeholder} />
<BlogCard backgroundLink={placeholder} /> <BlogCard image={placeholder} />
</div> </div>
</div> </div>
</div> </div>
<div className='IBMSMSplitMainBigSideSec'> <div className='IBMSMSplitMainBigSideSec'>
<Comments <Comments
modDetails={modData} addressable={modData}
setCommentCount={setCommentCount} setCommentCount={setCommentCount}
/> />
</div> </div>
@ -972,126 +970,6 @@ const Body = ({
) )
} }
type InteractionsProps = {
modDetails: ModDetails
commentCount: number
}
const Interactions = ({ modDetails, commentCount }: InteractionsProps) => {
return (
<div className='IBMSMSplitMainBigSideSec'>
<div className='IBMSMSMBSS_Details'>
<a style={{ textDecoration: 'unset', color: 'unset' }}>
<div className='IBMSMSMBSS_Details_Card IBMSMSMBSS_D_CComments'>
<div className='IBMSMSMBSS_Details_CardVisual'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSS_Details_CardVisualIcon'
>
<path d='M256 31.1c-141.4 0-255.1 93.12-255.1 208c0 49.62 21.35 94.98 56.97 130.7c-12.5 50.37-54.27 95.27-54.77 95.77c-2.25 2.25-2.875 5.734-1.5 8.734c1.249 3 4.021 4.766 7.271 4.766c66.25 0 115.1-31.76 140.6-51.39c32.63 12.25 69.02 19.39 107.4 19.39c141.4 0 255.1-93.13 255.1-207.1S397.4 31.1 256 31.1zM127.1 271.1c-17.75 0-32-14.25-32-31.1s14.25-32 32-32s32 14.25 32 32S145.7 271.1 127.1 271.1zM256 271.1c-17.75 0-31.1-14.25-31.1-31.1s14.25-32 31.1-32s31.1 14.25 31.1 32S273.8 271.1 256 271.1zM383.1 271.1c-17.75 0-32-14.25-32-31.1s14.25-32 32-32s32 14.25 32 32S401.7 271.1 383.1 271.1z'></path>
</svg>
</div>
<p className='IBMSMSMBSS_Details_CardText'>
{abbreviateNumber(commentCount)}
</p>
</div>
</a>
<Zap modDetails={modDetails} />
<Reactions modDetails={modDetails} />
</div>
</div>
)
}
type PublishDetailsProps = {
published_at: number
edited_at: number
site: string
}
const PublishDetails = ({
published_at,
edited_at,
site
}: PublishDetailsProps) => {
return (
<div className='IBMSMSplitMainBigSideSec'>
<div className='IBMSMSMBSSPost_PostDetails'>
<div
data-bs-toggle='tooltip'
data-bs-placement='left'
className='IBMSMSMBSSPost_PDElement'
title='Publish date'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSSPost_PDElementIcon'
data-bs-toggle='tooltip'
data-bss-tooltip
aria-label='Publish date'
>
<path d='M480 32H128C110.3 32 96 46.33 96 64v336C96 408.8 88.84 416 80 416S64 408.8 64 400V96H32C14.33 96 0 110.3 0 128v288c0 35.35 28.65 64 64 64h384c35.35 0 64-28.65 64-64V64C512 46.33 497.7 32 480 32zM272 416h-96C167.2 416 160 408.8 160 400C160 391.2 167.2 384 176 384h96c8.836 0 16 7.162 16 16C288 408.8 280.8 416 272 416zM272 320h-96C167.2 320 160 312.8 160 304C160 295.2 167.2 288 176 288h96C280.8 288 288 295.2 288 304C288 312.8 280.8 320 272 320zM432 416h-96c-8.836 0-16-7.164-16-16c0-8.838 7.164-16 16-16h96c8.836 0 16 7.162 16 16C448 408.8 440.8 416 432 416zM432 320h-96C327.2 320 320 312.8 320 304C320 295.2 327.2 288 336 288h96C440.8 288 448 295.2 448 304C448 312.8 440.8 320 432 320zM448 208C448 216.8 440.8 224 432 224h-256C167.2 224 160 216.8 160 208v-96C160 103.2 167.2 96 176 96h256C440.8 96 448 103.2 448 112V208z' />
</svg>
<p className='IBMSMSMBSSPost_PDElementText'>
{formatDate(
(published_at !== -1 ? published_at : edited_at) * 1000,
'dd/MM/yyyy hh:mm:ss aa'
)}
</p>
</div>
<div
data-bs-toggle='tooltip'
data-bs-placement='left'
className='IBMSMSMBSSPost_PDElement'
title='Last modified'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSSPost_PDElementIcon'
>
<path d='M362.7 19.32C387.7-5.678 428.3-5.678 453.3 19.32L492.7 58.75C517.7 83.74 517.7 124.3 492.7 149.3L444.3 197.7L314.3 67.72L362.7 19.32zM421.7 220.3L188.5 453.4C178.1 463.8 165.2 471.5 151.1 475.6L30.77 511C22.35 513.5 13.24 511.2 7.03 504.1C.8198 498.8-1.502 489.7 .976 481.2L36.37 360.9C40.53 346.8 48.16 333.9 58.57 323.5L291.7 90.34L421.7 220.3z' />
</svg>
<p className='IBMSMSMBSSPost_PDElementText'>
{formatDate(edited_at * 1000, 'dd/MM/yyyy hh:mm:ss aa')}
</p>
</div>
<a
data-bs-toggle='tooltip'
data-bs-placement='left'
className='IBMSMSMBSSPost_PDElement IBMSMSMBSSPost_PDElementLink'
href='#'
title='Published on'
target='_blank'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 -64 640 640'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSSPost_PDElementIcon'
>
<path d='M172.5 131.1C228.1 75.51 320.5 75.51 376.1 131.1C426.1 181.1 433.5 260.8 392.4 318.3L391.3 319.9C381 334.2 361 337.6 346.7 327.3C332.3 317 328.9 297 339.2 282.7L340.3 281.1C363.2 249 359.6 205.1 331.7 177.2C300.3 145.8 249.2 145.8 217.7 177.2L105.5 289.5C73.99 320.1 73.99 372 105.5 403.5C133.3 431.4 177.3 435 209.3 412.1L210.9 410.1C225.3 400.7 245.3 404 255.5 418.4C265.8 432.8 262.5 452.8 248.1 463.1L246.5 464.2C188.1 505.3 110.2 498.7 60.21 448.8C3.741 392.3 3.741 300.7 60.21 244.3L172.5 131.1zM467.5 380C411 436.5 319.5 436.5 263 380C213 330 206.5 251.2 247.6 193.7L248.7 192.1C258.1 177.8 278.1 174.4 293.3 184.7C307.7 194.1 311.1 214.1 300.8 229.3L299.7 230.9C276.8 262.1 280.4 306.9 308.3 334.8C339.7 366.2 390.8 366.2 422.3 334.8L534.5 222.5C566 191 566 139.1 534.5 108.5C506.7 80.63 462.7 76.99 430.7 99.9L429.1 101C414.7 111.3 394.7 107.1 384.5 93.58C374.2 79.2 377.5 59.21 391.9 48.94L393.5 47.82C451 6.731 529.8 13.25 579.8 63.24C636.3 119.7 636.3 211.3 579.8 267.7L467.5 380z' />
</svg>
<p className='IBMSMSMBSSPost_PDElementText'>{site}</p>
</a>
</div>
</div>
)
}
const Download = ({ const Download = ({
url, url,
hash, hash,

View File

@ -1,7 +1,7 @@
import { NDKContextType } from 'contexts/NDKContext' import { NDKContextType } from 'contexts/NDKContext'
import { ActionFunctionArgs, redirect } from 'react-router-dom' import { ActionFunctionArgs, redirect } from 'react-router-dom'
import { getBlogPageRoute } from 'routes' import { getBlogPageRoute } from 'routes'
import { BlogFormErrors, BlogFormSubmit } from 'types' import { BlogFormErrors, BlogEventSubmitForm } from 'types'
import { import {
isReachable, isReachable,
isValidImageUrl, isValidImageUrl,
@ -19,7 +19,7 @@ import { store } from 'store'
export const writeRouteAction = export const writeRouteAction =
(ndkContext: NDKContextType) => (ndkContext: NDKContextType) =>
async ({ params, request }: ActionFunctionArgs) => { async ({ request }: ActionFunctionArgs) => {
// Get the current state // Get the current state
const userState = store.getState().user const userState = store.getState().user
let hexPubkey: string let hexPubkey: string
@ -47,7 +47,7 @@ export const writeRouteAction =
const formData = await request.formData() const formData = await request.formData()
// Parse the the data // Parse the the data
const formSubmit = parseFormData<BlogFormSubmit>(formData) const formSubmit = parseFormData<BlogEventSubmitForm>(formData)
// Check for errors // Check for errors
const formErrors = await validateFormData(formSubmit) const formErrors = await validateFormData(formSubmit)
@ -110,7 +110,7 @@ export const writeRouteAction =
)}` )}`
) )
const naddr = nip19.naddrEncode({ const naddr = nip19.naddrEncode({
identifier: aTag, identifier: uuid,
pubkey: signedEvent.pubkey, pubkey: signedEvent.pubkey,
kind: signedEvent.kind, kind: signedEvent.kind,
relays: publishedOnRelays relays: publishedOnRelays
@ -125,7 +125,7 @@ export const writeRouteAction =
} }
const validateFormData = async ( const validateFormData = async (
formData: Partial<BlogFormSubmit> formData: Partial<BlogEventSubmitForm>
): Promise<BlogFormErrors> => { ): Promise<BlogFormErrors> => {
const errors: BlogFormErrors = {} const errors: BlogFormErrors = {}

View File

@ -20,6 +20,7 @@ import { writeRouteAction } from '../pages/write/action'
import { BlogsPage } from 'pages/blogs' import { BlogsPage } from 'pages/blogs'
import { BlogPage } from 'pages/blog' import { BlogPage } from 'pages/blog'
import { blogRouteLoader } from 'pages/blog/loader' import { blogRouteLoader } from 'pages/blog/loader'
import { blogsRouteLoader } from 'pages/blogs/loader'
export const appRoutes = { export const appRoutes = {
index: '/', index: '/',
@ -29,8 +30,8 @@ export const appRoutes = {
mods: '/mods', mods: '/mods',
mod: '/mod/:naddr', mod: '/mod/:naddr',
about: '/about', about: '/about',
blogs: '/blogs', blogs: '/blog',
blog: '/blogs/:naddr', blog: '/blog/:naddr',
submitMod: '/submit-mod', submitMod: '/submit-mod',
editMod: '/edit-mod/:naddr', editMod: '/edit-mod/:naddr',
write: '/write', write: '/write',
@ -90,7 +91,8 @@ export const routerWithNdkContext = (context: NDKContextType) =>
}, },
{ {
path: appRoutes.blogs, path: appRoutes.blogs,
element: <BlogsPage /> element: <BlogsPage />,
loader: blogsRouteLoader(context)
}, },
{ {
path: appRoutes.blog, path: appRoutes.blog,

View File

@ -1,11 +1,13 @@
export interface BlogDetails { export interface BlogForm {
title: string title: string
content: string content: string
summary: string
image: string image: string
nsfw: boolean summary: string
tags: string tags: string
nsfw: boolean
}
export interface BlogDetails extends BlogForm {
id: string id: string
author: string author: string
published_at: number published_at: number
@ -16,20 +18,12 @@ export interface BlogDetails {
tTags: string[] tTags: string[]
} }
export interface BlogFormSubmit export interface BlogEventSubmitForm extends Omit<BlogForm, 'nsfw'> {
extends Omit<
BlogDetails,
| 'nsfw'
| 'id'
| 'author'
| 'published_at'
| 'edited_at'
| 'rTag'
| 'dTag'
| 'aTag'
| 'tTag'
> {
nsfw: string nsfw: string
} }
export interface BlogFormErrors extends Partial<BlogFormSubmit> {} export interface BlogFormErrors extends Partial<BlogEventSubmitForm> {}
export interface BlogCardDetails extends BlogDetails {
naddr: string
}

View File

@ -7,3 +7,9 @@ export interface SignedEvent {
id: string id: string
sig: string sig: string
} }
export interface Addressable {
author: string
id: string
aTag: string
}

38
src/utils/blog.ts Normal file
View File

@ -0,0 +1,38 @@
import { NDKEvent } from '@nostr-dev-kit/ndk'
import { BlogCardDetails, BlogDetails } from 'types'
import { getFirstTagValue, getFirstTagValueAsInt, getTagValue } from './nostr'
import { kinds, nip19 } from 'nostr-tools'
export const extractBlogDetails = (event: NDKEvent): Partial<BlogDetails> => ({
title: getFirstTagValue(event, 'title'),
content: event.content,
summary: getFirstTagValue(event, 'summary'),
image: getFirstTagValue(event, 'image'),
nsfw: getFirstTagValue(event, 'nsfw') === 'true',
id: event.id,
author: event.pubkey,
published_at: getFirstTagValueAsInt(event, 'published_at'),
edited_at: event.created_at,
rTag: getFirstTagValue(event, 'r') || 'N/A',
dTag: getFirstTagValue(event, 'd'),
aTag: getFirstTagValue(event, 'a'),
tTags: getTagValue(event, 't') || []
})
export const extractBlogCardDetails = (
event: NDKEvent
): Partial<BlogCardDetails> => {
const blogDetails = extractBlogDetails(event)
return {
...blogDetails,
naddr: blogDetails.dTag
? nip19.naddrEncode({
identifier: blogDetails.dTag,
kind: kinds.LongFormArticle,
pubkey: event.pubkey
})
: undefined
}
}

1
src/vite-env.d.ts vendored
View File

@ -6,6 +6,7 @@ interface ImportMetaEnv {
readonly VITE_REPORTING_NPUB: string readonly VITE_REPORTING_NPUB: string
readonly VITE_FALLBACK_MOD_IMAGE: string readonly VITE_FALLBACK_MOD_IMAGE: string
readonly VITE_FALLBACK_GAME_IMAGE: string readonly VITE_FALLBACK_GAME_IMAGE: string
readonly VITE_BLOG_NPUBS: string
// more env variables... // more env variables...
} }