parent
2ba613d562
commit
5df56fbdae
9 changed files with 96 additions and 3 deletions
25
client/components/new-post/description/index.tsx
Normal file
25
client/components/new-post/description/index.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { ChangeEvent, memo } from "react"
|
||||
import { Input } from "@geist-ui/core"
|
||||
|
||||
import styles from "../post.module.css"
|
||||
|
||||
type props = {
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void
|
||||
description?: string
|
||||
}
|
||||
|
||||
const Description = ({ onChange, description }: props) => {
|
||||
return (
|
||||
<div className={styles.description}>
|
||||
<Input
|
||||
value={description}
|
||||
onChange={onChange}
|
||||
label="Description"
|
||||
maxLength={256}
|
||||
width="100%"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(Description)
|
|
@ -24,6 +24,7 @@ import EditDocumentList from "@components/edit-document-list"
|
|||
import { ChangeEvent } from "react"
|
||||
import DatePicker from "react-datepicker"
|
||||
import getTitleForPostCopy from "@lib/get-title-for-post-copy"
|
||||
import Description from "./description"
|
||||
|
||||
const Post = ({
|
||||
initialPost,
|
||||
|
@ -35,6 +36,7 @@ const Post = ({
|
|||
const { setToast } = useToasts()
|
||||
const router = useRouter()
|
||||
const [title, setTitle] = useState<string>()
|
||||
const [description, setDescription] = useState<string>()
|
||||
const [expiresAt, setExpiresAt] = useState<Date | null>(null)
|
||||
|
||||
const emptyDoc = useMemo(
|
||||
|
@ -62,6 +64,7 @@ const Post = ({
|
|||
)
|
||||
|
||||
setTitle(getTitleForPostCopy(initialPost.title))
|
||||
setDescription(initialPost.description)
|
||||
}
|
||||
}, [emptyDoc, initialPost])
|
||||
|
||||
|
@ -88,6 +91,7 @@ const Post = ({
|
|||
},
|
||||
body: JSON.stringify({
|
||||
title,
|
||||
description,
|
||||
files: docs,
|
||||
...data
|
||||
})
|
||||
|
@ -187,6 +191,13 @@ const Post = ({
|
|||
[setTitle]
|
||||
)
|
||||
|
||||
const onChangeDescription = useCallback(
|
||||
(e: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setDescription(e.target.value)
|
||||
},
|
||||
[setDescription]
|
||||
)
|
||||
|
||||
const updateDocTitle = useCallback(
|
||||
(i: number) => (title: string) => {
|
||||
setDocs((docs) =>
|
||||
|
@ -284,6 +295,7 @@ const Post = ({
|
|||
return (
|
||||
<div style={{ paddingBottom: 150 }}>
|
||||
<Title title={title} onChange={onChangeTitle} />
|
||||
<Description description={description} onChange={onChangeDescription} />
|
||||
<FileDropzone setDocs={uploadDocs} />
|
||||
<EditDocumentList
|
||||
onPaste={onPaste}
|
||||
|
|
|
@ -25,6 +25,12 @@
|
|||
margin-bottom: var(--gap);
|
||||
}
|
||||
|
||||
.description {
|
||||
width: 100%;
|
||||
margin-bottom: var(--gap);
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 650px) {
|
||||
.title {
|
||||
align-items: flex-start;
|
||||
|
|
|
@ -13,6 +13,12 @@
|
|||
gap: var(--gap-half);
|
||||
}
|
||||
|
||||
.oneline {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
.badges {
|
||||
flex-direction: column;
|
||||
|
|
|
@ -78,6 +78,12 @@ const ListItem = ({
|
|||
)}
|
||||
</Text>
|
||||
|
||||
{post.description && (
|
||||
<Text p className={styles.oneline}>
|
||||
{post.description}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
<div className={styles.badges}>
|
||||
<VisibilityBadge visibility={post.visibility} />
|
||||
<CreatedAgoBadge createdAt={post.createdAt} />
|
||||
|
|
|
@ -149,6 +149,11 @@ const PostPage = ({ post: initialPost, isProtected }: Props) => {
|
|||
</span>
|
||||
</span>
|
||||
</div>
|
||||
{post.description && (
|
||||
<div>
|
||||
<Text p>{post.description}</Text>
|
||||
</div>
|
||||
)}
|
||||
{/* {post.files.length > 1 && <FileTree files={post.files} />} */}
|
||||
{post.files?.map(({ id, content, title }: File) => (
|
||||
<DocumentComponent
|
||||
|
|
|
@ -55,6 +55,9 @@ export class Post extends Model {
|
|||
@Column
|
||||
title!: string
|
||||
|
||||
@Column
|
||||
description?: string
|
||||
|
||||
@BelongsToMany(() => User, () => PostAuthor)
|
||||
users?: User[]
|
||||
|
||||
|
|
12
server/src/migrations/08_description_posts.ts
Normal file
12
server/src/migrations/08_description_posts.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
"use strict"
|
||||
import { DataTypes } from "sequelize"
|
||||
import type { Migration } from "../database"
|
||||
|
||||
export const up: Migration = async ({ context: queryInterface }) =>
|
||||
queryInterface.addColumn("posts", "description", {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
})
|
||||
|
||||
export const down: Migration = async ({ context: queryInterface }) =>
|
||||
await queryInterface.removeColumn("posts", "description")
|
|
@ -31,6 +31,7 @@ posts.post(
|
|||
celebrate({
|
||||
body: {
|
||||
title: Joi.string().required(),
|
||||
description: Joi.string().optional().min(0).max(256),
|
||||
files: Joi.any().required(),
|
||||
visibility: Joi.string()
|
||||
.custom(postVisibilitySchema, "valid visibility")
|
||||
|
@ -66,6 +67,7 @@ posts.post(
|
|||
|
||||
const newPost = new Post({
|
||||
title: req.body.title,
|
||||
description: req.body.description,
|
||||
visibility: req.body.visibility,
|
||||
password: hashedPassword,
|
||||
expiresAt: req.body.expiresAt
|
||||
|
@ -126,7 +128,7 @@ posts.post(
|
|||
posts.get("/", secretKey, async (req, res, next) => {
|
||||
try {
|
||||
const posts = await Post.findAll({
|
||||
attributes: ["id", "title", "visibility", "createdAt"]
|
||||
attributes: ["id", "title", "description", "visibility", "createdAt"]
|
||||
})
|
||||
res.json(posts)
|
||||
} catch (e) {
|
||||
|
@ -159,7 +161,14 @@ posts.get("/mine", jwt, async (req: UserJwtRequest, res, next) => {
|
|||
attributes: ["id", "title", "visibility"]
|
||||
}
|
||||
],
|
||||
attributes: ["id", "title", "visibility", "createdAt", "expiresAt"]
|
||||
attributes: [
|
||||
"id",
|
||||
"title",
|
||||
"description",
|
||||
"visibility",
|
||||
"createdAt",
|
||||
"expiresAt"
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
|
@ -205,6 +214,7 @@ posts.get(
|
|||
where: {
|
||||
[Op.or]: [
|
||||
{ title: { [Op.like]: `%${q}%` } },
|
||||
{ description: { [Op.like]: `%${q}%` } },
|
||||
{ "$files.title$": { [Op.like]: `%${q}%` } },
|
||||
{ "$files.content$": { [Op.like]: `%${q}%` } }
|
||||
],
|
||||
|
@ -227,7 +237,14 @@ posts.get(
|
|||
attributes: ["id", "title", "visibility"]
|
||||
}
|
||||
],
|
||||
attributes: ["id", "title", "visibility", "createdAt", "deletedAt"],
|
||||
attributes: [
|
||||
"id",
|
||||
"title",
|
||||
"description",
|
||||
"visibility",
|
||||
"createdAt",
|
||||
"deletedAt"
|
||||
],
|
||||
order: [["createdAt", "DESC"]]
|
||||
})
|
||||
|
||||
|
@ -259,6 +276,7 @@ const fullPostSequelizeOptions = {
|
|||
attributes: [
|
||||
"id",
|
||||
"title",
|
||||
"description",
|
||||
"visibility",
|
||||
"createdAt",
|
||||
"updatedAt",
|
||||
|
|
Loading…
Reference in a new issue