style: fix missing list styles in markdown content. feat: update post
This commit is contained in:
+27
-1
@@ -3,8 +3,9 @@ import { v } from 'convex/values';
|
|||||||
import { mutation, query } from './_generated/server';
|
import { mutation, query } from './_generated/server';
|
||||||
import { userQuery } from './users';
|
import { userQuery } from './users';
|
||||||
|
|
||||||
export const createPost = mutation({
|
export const createOrUpdatePost = mutation({
|
||||||
args: {
|
args: {
|
||||||
|
id: v.optional(v.id('posts')),
|
||||||
clerkUserId: v.string(),
|
clerkUserId: v.string(),
|
||||||
content: v.array(v.string()),
|
content: v.array(v.string()),
|
||||||
parentPostId: v.optional(v.id('posts')),
|
parentPostId: v.optional(v.id('posts')),
|
||||||
@@ -25,6 +26,11 @@ export const createPost = mutation({
|
|||||||
return 'CANNOT_POST_ON_BEHALF_OF_ANOTHER_USER';
|
return 'CANNOT_POST_ON_BEHALF_OF_ANOTHER_USER';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (args.id) {
|
||||||
|
await ctx.db.patch(args.id, { content: args.content });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return await ctx.db.insert('posts', {
|
return await ctx.db.insert('posts', {
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
content: args.content,
|
content: args.content,
|
||||||
@@ -43,3 +49,23 @@ export const getUsersPostById = query({
|
|||||||
.paginate(args.paginationOpts);
|
.paginate(args.paginationOpts);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const deletePostById = mutation({
|
||||||
|
args: { id: v.id('posts') },
|
||||||
|
handler: async (ctx, args) => {
|
||||||
|
await ctx.db.delete(args.id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getPostById = query({
|
||||||
|
args: { id: v.id('posts') },
|
||||||
|
handler: async (ctx, args) => {
|
||||||
|
const post = await ctx.db.get(args.id);
|
||||||
|
|
||||||
|
if (post === null) {
|
||||||
|
return 'POST_NOT_FOUND';
|
||||||
|
}
|
||||||
|
|
||||||
|
return post;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import Editor from '@/components/Editor';
|
||||||
|
import { getConvexClient } from '@/lib/convex';
|
||||||
|
import { api } from '../../../../../convex/_generated/api';
|
||||||
|
import { Id } from '../../../../../convex/_generated/dataModel';
|
||||||
|
|
||||||
|
export default async function EditPostPage({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: { id: string };
|
||||||
|
}) {
|
||||||
|
const client = await getConvexClient();
|
||||||
|
const post = await client.query(api.posts.getPostById, {
|
||||||
|
id: params.id as Id<'posts'>,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (post === 'POST_NOT_FOUND') {
|
||||||
|
// TODO: Return actual not found component
|
||||||
|
return <h1>Not Found</h1>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className='flex flex-col w-full h-full'>
|
||||||
|
<Editor post={post} />
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
+23
-27
@@ -10,22 +10,20 @@ import { Compartment, EditorState, Text } from '@codemirror/state';
|
|||||||
import { EditorView, keymap } from '@codemirror/view';
|
import { EditorView, keymap } from '@codemirror/view';
|
||||||
import { basicSetup } from 'codemirror';
|
import { basicSetup } from 'codemirror';
|
||||||
import { useMutation } from 'convex/react';
|
import { useMutation } from 'convex/react';
|
||||||
import { useTheme } from 'next-themes';
|
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { FormEvent, useEffect, useState } from 'react';
|
import { FormEvent, useState } from 'react';
|
||||||
import { api } from '../../convex/_generated/api';
|
import { api } from '../../convex/_generated/api';
|
||||||
|
import { Doc } from '../../convex/_generated/dataModel';
|
||||||
import PostContent from './PostContent';
|
import PostContent from './PostContent';
|
||||||
|
|
||||||
export default function Editor() {
|
export default function Editor({ post }: { post?: Doc<'posts'> }) {
|
||||||
const { user, isSignedIn } = useUser();
|
const { user, isSignedIn } = useUser();
|
||||||
const { theme } = useTheme();
|
|
||||||
const editorTheme = new Compartment();
|
const editorTheme = new Compartment();
|
||||||
const [currentDoc, setCurrentDoc] = useState(['']);
|
const [currentDoc, setCurrentDoc] = useState(post?.content ?? ['']);
|
||||||
const createPost = useMutation(api.posts.createPost);
|
const createOrUpdatePost = useMutation(api.posts.createOrUpdatePost);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [creating, setCreating] = useState(false);
|
const [creatingOrUpdating, setCreatingOrUpdating] = useState(false);
|
||||||
|
|
||||||
const doc = [''];
|
|
||||||
const extensions = [
|
const extensions = [
|
||||||
basicSetup,
|
basicSetup,
|
||||||
markdown({ base: markdownLanguage, codeLanguages: languages }),
|
markdown({ base: markdownLanguage, codeLanguages: languages }),
|
||||||
@@ -38,20 +36,7 @@ export default function Editor() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const [mode, setMode] = useState<'write' | 'preview'>('write');
|
const [mode, setMode] = useState<'write' | 'preview'>('write');
|
||||||
const { editorRef, editorView } = useCodeMirror({ doc, extensions });
|
const { editorRef } = useCodeMirror({ doc: currentDoc, extensions });
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (editorView === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (theme === 'dark') {
|
|
||||||
editorView.dispatch({
|
|
||||||
effects: editorTheme.reconfigure([]),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [theme, editorView]);
|
|
||||||
|
|
||||||
async function handleSubmit(e: FormEvent<HTMLFormElement>) {
|
async function handleSubmit(e: FormEvent<HTMLFormElement>) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -61,9 +46,10 @@ export default function Editor() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCreating(true);
|
setCreatingOrUpdating(true);
|
||||||
|
|
||||||
const result = await createPost({
|
const result = await createOrUpdatePost({
|
||||||
|
id: post?._id,
|
||||||
clerkUserId: user.id,
|
clerkUserId: user.id,
|
||||||
content: currentDoc,
|
content: currentDoc,
|
||||||
});
|
});
|
||||||
@@ -80,7 +66,7 @@ export default function Editor() {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
setCreating(false);
|
setCreatingOrUpdating(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,10 +109,20 @@ export default function Editor() {
|
|||||||
<PostContent content={Text.of(currentDoc).toString()} />
|
<PostContent content={Text.of(currentDoc).toString()} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center justify-end p-4 pt-0'>
|
<div className='flex items-center justify-end p-4 pt-0 gap-4'>
|
||||||
|
<button
|
||||||
|
onClick={() => router.back()}
|
||||||
|
type='button'
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type='submit'
|
type='submit'
|
||||||
disabled={!currentDoc.join() || !currentDoc.join().trim() || creating}
|
disabled={
|
||||||
|
!currentDoc.join() ||
|
||||||
|
!currentDoc.join().trim() ||
|
||||||
|
creatingOrUpdating
|
||||||
|
}
|
||||||
className='py-1 px-4 bg-primary-accent text-white rounded-md disabled:opacity-50 flex items-center justify-center gap-2'
|
className='py-1 px-4 bg-primary-accent text-white rounded-md disabled:opacity-50 flex items-center justify-center gap-2'
|
||||||
>
|
>
|
||||||
Post
|
Post
|
||||||
|
|||||||
@@ -411,6 +411,14 @@
|
|||||||
padding-left: 2em;
|
padding-left: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.markdown-content ul {
|
||||||
|
list-style: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-content ol {
|
||||||
|
list-style: decimal
|
||||||
|
}
|
||||||
|
|
||||||
.markdown-content ol ol,
|
.markdown-content ol ol,
|
||||||
.markdown-content ul ol {
|
.markdown-content ul ol {
|
||||||
list-style-type: lower-roman;
|
list-style-type: lower-roman;
|
||||||
|
|||||||
+14
-2
@@ -1,13 +1,18 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
import { PostWithUserDto } from '@/app/types';
|
import { PostWithUserDto } from '@/app/types';
|
||||||
import { Text } from '@codemirror/state';
|
import { Text } from '@codemirror/state';
|
||||||
|
import { useMutation } from 'convex/react';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { BiDotsHorizontalRounded } from 'react-icons/bi';
|
import { BiDotsHorizontalRounded } from 'react-icons/bi';
|
||||||
import { GoDotFill } from 'react-icons/go';
|
import { GoDotFill } from 'react-icons/go';
|
||||||
|
import { api } from '../../convex/_generated/api';
|
||||||
import PostContent from './PostContent';
|
import PostContent from './PostContent';
|
||||||
|
|
||||||
export default function Post({ post }: { post: PostWithUserDto }) {
|
export default function Post({ post }: { post: PostWithUserDto }) {
|
||||||
|
const deletePostById = useMutation(api.posts.deletePostById);
|
||||||
const modalRef = useRef<HTMLDivElement>(null);
|
const modalRef = useRef<HTMLDivElement>(null);
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
const username = post.user.clerkUsername ?? post.user._id;
|
const username = post.user.clerkUsername ?? post.user._id;
|
||||||
@@ -42,8 +47,15 @@ export default function Post({ post }: { post: PostWithUserDto }) {
|
|||||||
return () => document.removeEventListener('click', handleClickOutside);
|
return () => document.removeEventListener('click', handleClickOutside);
|
||||||
}, [modalOpen]);
|
}, [modalOpen]);
|
||||||
|
|
||||||
function handleDeleteClick() {
|
async function handleDeleteClick() {
|
||||||
throw new Error('Function not implemented.');
|
setModalOpen(false);
|
||||||
|
const result = confirm('Are you sure you want to delete this post?');
|
||||||
|
|
||||||
|
if (result === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await deletePostById({ id: post._id });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user