diff --git a/src/pages/feed/action.ts b/src/pages/feed/action.ts
new file mode 100644
index 0000000..1905c2c
--- /dev/null
+++ b/src/pages/feed/action.ts
@@ -0,0 +1,85 @@
+import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'
+import { NDKContextType } from 'contexts/NDKContext'
+import { ActionFunctionArgs, redirect } from 'react-router-dom'
+import { toast } from 'react-toastify'
+import { getFeedNotePageRoute } from 'routes'
+import { store } from 'store'
+import { NoteSubmitForm, NoteSubmitFormErrors } from 'types'
+import { log, LogType, now } from 'utils'
+
+export const feedPostRouteAction =
+ (ndkContext: NDKContextType) =>
+ async ({ request }: ActionFunctionArgs) => {
+ const userState = store.getState().user
+ let hexPubkey: string
+ if (userState.auth && userState.user?.pubkey) {
+ hexPubkey = userState.user.pubkey as string
+ } else {
+ try {
+ hexPubkey = (await window.nostr?.getPublicKey()) as string
+ } catch (error) {
+ if (error instanceof Error) {
+ log(true, LogType.Error, 'Failed to get public key.', error)
+ }
+
+ toast.error('Failed to get public key.')
+ return null
+ }
+ }
+
+ if (!hexPubkey) {
+ toast.error('Could not get pubkey')
+ return null
+ }
+
+ const formSubmit = (await request.json()) as NoteSubmitForm
+ const formErrors = validateFormData(formSubmit)
+
+ if (Object.keys(formErrors).length) return formErrors
+
+ const content = decodeURIComponent(formSubmit.content!)
+ const currentTimeStamp = now()
+
+ const ndkEvent = new NDKEvent(ndkContext.ndk, {
+ kind: NDKKind.Text,
+ created_at: currentTimeStamp,
+ content: content,
+ tags: [],
+ pubkey: hexPubkey
+ })
+
+ try {
+ await ndkEvent.generateTags()
+
+ if (formSubmit.nsfw) ndkEvent.tags.push(['L', 'content-warning'])
+
+ ndkEvent.tags.push(['L', 'source'])
+ ndkEvent.tags.push(['l', window.location.host, 'source'])
+ ndkEvent.tags.push(['client', 'DEG Mods'])
+
+ await ndkEvent.sign()
+ const note1 = ndkEvent.encode()
+ const publishedOnRelays = await ndkEvent.publish()
+ if (publishedOnRelays.size === 0) {
+ toast.error('Failed to publish note on any relay')
+ return null
+ } else {
+ toast.success('Note published successfully')
+ return redirect(getFeedNotePageRoute(note1))
+ }
+ } catch (error) {
+ log(true, LogType.Error, 'Failed to publish note', error)
+ toast.error('Failed to publish note')
+ return null
+ }
+ }
+
+const validateFormData = (formSubmit: NoteSubmitForm): NoteSubmitFormErrors => {
+ const errors: NoteSubmitFormErrors = {}
+
+ if (!formSubmit.content.trim()) {
+ errors.content = 'Content is required'
+ }
+
+ return errors
+}
diff --git a/src/pages/feed/index.tsx b/src/pages/feed/index.tsx
index a2bef4c..1cc3d65 100644
--- a/src/pages/feed/index.tsx
+++ b/src/pages/feed/index.tsx
@@ -4,9 +4,12 @@ import { FeedTabBlogs } from './FeedTabBlogs'
import { FeedTabMods } from './FeedTabMods'
import { FeedTabPosts } from './FeedTabPosts'
import { FeedFilter } from 'components/Filters/FeedFilter'
+import { Outlet, useParams } from 'react-router-dom'
export const FeedPage = () => {
- const [tab, setTab] = useState(0)
+ const { note } = useParams()
+ // Open posts tab if note is present
+ const [tab, setTab] = useState(note ? 2 : 0)
return (
<>
@@ -17,6 +20,8 @@ export const FeedPage = () => {
{tab === 0 && }
{tab === 1 && }
{tab === 2 && }
+
+
>
)
}
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
index 066c487..bd90666 100644
--- a/src/routes/index.tsx
+++ b/src/routes/index.tsx
@@ -33,6 +33,7 @@ import { BackupPage } from 'pages/backup'
import { SupportersPage } from 'pages/supporters'
import { commentsLoader } from 'loaders/comment'
import { CommentsPopup } from 'components/comment/CommentsPopup'
+import { feedPostRouteAction } from 'pages/feed/action'
export const appRoutes = {
home: '/',
@@ -56,6 +57,7 @@ export const appRoutes = {
settingsAdmin: '/settings-admin',
profile: '/profile/:nprofile?',
feed: '/feed',
+ note: '/feed/:note',
notifications: '/notifications',
backup: '/backup',
supporters: '/supporters'
@@ -76,6 +78,9 @@ export const getBlogPageRoute = (eventId: string) =>
export const getProfilePageRoute = (nprofile: string) =>
appRoutes.profile.replace(':nprofile', nprofile)
+export const getFeedNotePageRoute = (note: string) =>
+ appRoutes.note.replace(':note', note)
+
export const routerWithNdkContext = (context: NDKContextType) =>
createBrowserRouter([
{
@@ -199,7 +204,15 @@ export const routerWithNdkContext = (context: NDKContextType) =>
{
path: appRoutes.feed,
element: ,
- loader: feedPageLoader(context)
+ loader: feedPageLoader(context),
+ action: feedPostRouteAction(context),
+ children: [
+ {
+ path: ':note',
+ element: ,
+ loader: commentsLoader(context)
+ }
+ ]
},
{
path: appRoutes.notifications,
diff --git a/src/types/index.ts b/src/types/index.ts
index 431b336..3d4c6f5 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -8,3 +8,4 @@ export * from './category'
export * from './popup'
export * from './errors'
export * from './comments'
+export * from './note'
diff --git a/src/types/note.ts b/src/types/note.ts
new file mode 100644
index 0000000..5e2f179
--- /dev/null
+++ b/src/types/note.ts
@@ -0,0 +1,6 @@
+export interface NoteSubmitForm {
+ content: string
+ nsfw: boolean
+}
+
+export interface NoteSubmitFormErrors extends Partial {}