diff --git a/package.json b/package.json index 049ff641..a6897e35 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "react": "^17.0.1", "react-dom": "^17.0.1", "react-icons": "^4.1.0", + "short-number": "^1.0.7", "validator": "^13.5.2" }, "devDependencies": { diff --git a/pages/[username]/[post_id].tsx b/pages/[username]/[post_id].tsx index 25bc29b7..c8a03ca6 100644 --- a/pages/[username]/[post_id].tsx +++ b/pages/[username]/[post_id].tsx @@ -1,4 +1,4 @@ -import { Box, chakra, Divider, Flex, Heading, HStack, IconButton, Image, VStack } from "@chakra-ui/react" +import { Box, chakra, Divider, Flex, Heading, HStack, IconButton, Image, storageKey, VStack } from "@chakra-ui/react" import Comments from "components/comments/comments" import Container from "components/container" import LikeButton from "components/posts/unicorn-like" @@ -23,13 +23,14 @@ import { Tag } from "src/types/tag" import { requestApi } from "utils/axios/request" import UnicornLike from "components/posts/unicorn-like" import SvgButton from "components/svg-button" +import Bookmark from "components/posts/bookmark" +import PostSidebar from "components/posts/post-sidebar" const PostPage = () => { const router = useRouter() const id = router.query.post_id const [post, setPost]: [Post, any] = useState(null) - const [comments,setComments]: [Comment[],any] = useState([]) - const session = useSession() + const [comments, setComments]: [Comment[], any] = useState([]) useEffect(() => { if (id) { getData() @@ -41,9 +42,9 @@ const PostPage = () => { if (router && router.asPath.indexOf("#comments") > -1) { setTimeout(() => { location.href = "#comments" - },100) + }, 100) } - },[router]) + }, [router]) const getData = async () => { const res = await requestApi.get(`/post/${id}`) @@ -52,20 +53,6 @@ const PostPage = () => { getComments(res.data.id) } - const onLike = async () => { - await requestApi.post(`/story/like/${post.id}`) - const p = cloneDeep(post) - - if (post.liked) { - p.likes += -1 - p.liked = false - } else { - p.likes += 1 - p.liked = true - } - setPost(p) - } - const getComments = async (id) => { const res = await requestApi.get(`/story/comments/${id}`) setComments(res.data) @@ -81,7 +68,7 @@ const PostPage = () => { {post && <> - + {post.title} @@ -92,84 +79,12 @@ const PostPage = () => { - {post.rawTags.map(tag => )} + {post.rawTags.map(tag => )} - - - {/* */} - - - - } - /> - } - /> - - - - getComments(post.id)}/> + getComments(post.id)} /> - - - - {/* */} - {/* */} - - {/* */} - - - - - location.href="#comments"} - /> - - - {post.creatorId === session?.user.id && - router.push(`${ReserveUrls.Editor}/post/${post.id}`)} - icon="edit" - /> - } - - - - - + + @@ -182,3 +97,4 @@ const PostPage = () => { export default PostPage + diff --git a/pages/[username]/index.tsx b/pages/[username]/index.tsx index 3572ddea..f1449e0e 100644 --- a/pages/[username]/index.tsx +++ b/pages/[username]/index.tsx @@ -21,6 +21,7 @@ import userCustomTheme from "theme/user-custom" import Posts from "components/posts/posts" import Link from "next/link" import Empty from "components/empty" +import Count from "components/count" const UserPage = () => { const router = useRouter() @@ -60,8 +61,8 @@ const UserPage = () => { {user.nickname} {user.tagline && {user.tagline}} - Followers 0 - Following 0 + Followers + Following {session?.user.id === user.id ? : } diff --git a/server/internal/api/editor.go b/server/internal/api/editor.go deleted file mode 100644 index fc76cb28..00000000 --- a/server/internal/api/editor.go +++ /dev/null @@ -1,50 +0,0 @@ -package api - -import ( - "net/http" - - "github.com/gin-gonic/gin" - "github.com/imdotdev/im.dev/server/internal/story" - "github.com/imdotdev/im.dev/server/internal/user" - "github.com/imdotdev/im.dev/server/pkg/common" - "github.com/imdotdev/im.dev/server/pkg/e" -) - -func GetEditorPosts(c *gin.Context) { - user := user.CurrentUser(c) - ars, err := story.UserPosts(int64(user.ID)) - if err != nil { - c.JSON(err.Status, common.RespError(err.Message)) - return - } - - c.JSON(http.StatusOK, common.RespSuccess(ars)) -} - -func GetEditorPost(c *gin.Context) { - id := c.Param("id") - if id == "" { - c.JSON(http.StatusBadRequest, common.RespError(e.ParamInvalid)) - return - } - - user := user.CurrentUser(c) - creator, err := story.GetPostCreator(id) - if err != nil { - c.JSON(err.Status, common.RespError(err.Message)) - return - } - - if user.ID != creator { - c.JSON(http.StatusForbidden, common.RespError(e.NoPermission)) - return - } - - ar, err := story.GetPost(id, "") - if err != nil { - c.JSON(err.Status, common.RespError(err.Message)) - return - } - - c.JSON(http.StatusOK, common.RespSuccess(ar)) -} diff --git a/server/internal/api/posts.go b/server/internal/api/posts.go new file mode 100644 index 00000000..60dd522e --- /dev/null +++ b/server/internal/api/posts.go @@ -0,0 +1,60 @@ +package api + +import ( + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + "github.com/imdotdev/im.dev/server/internal/story" + "github.com/imdotdev/im.dev/server/internal/user" + "github.com/imdotdev/im.dev/server/pkg/common" +) + +func GetEditorPosts(c *gin.Context) { + user := user.CurrentUser(c) + ars, err := story.UserPosts(user, int64(user.ID)) + if err != nil { + c.JSON(err.Status, common.RespError(err.Message)) + return + } + + c.JSON(http.StatusOK, common.RespSuccess(ars)) +} + +func GetUserPosts(c *gin.Context) { + userID, _ := strconv.ParseInt(c.Param("userID"), 10, 64) + + user := user.CurrentUser(c) + + posts, err := story.UserPosts(user, userID) + if err != nil { + c.JSON(err.Status, common.RespError(err.Message)) + return + } + + c.JSON(http.StatusOK, common.RespSuccess(posts)) +} + +func GetTagPosts(c *gin.Context) { + tagID, _ := strconv.ParseInt(c.Param("id"), 10, 64) + user := user.CurrentUser(c) + posts, err := story.TagPosts(user, tagID) + if err != nil { + c.JSON(err.Status, common.RespError(err.Message)) + return + } + + c.JSON(http.StatusOK, common.RespSuccess(posts)) +} + +func GetHomePosts(c *gin.Context) { + filter := c.Param("filter") + user := user.CurrentUser(c) + posts, err := story.HomePosts(user, filter) + if err != nil { + c.JSON(err.Status, common.RespError(err.Message)) + return + } + + c.JSON(http.StatusOK, common.RespSuccess(posts)) +} diff --git a/server/internal/api/story.go b/server/internal/api/story.go index f3195d34..fa68817b 100644 --- a/server/internal/api/story.go +++ b/server/internal/api/story.go @@ -2,7 +2,6 @@ package api import ( "net/http" - "strconv" "github.com/gin-gonic/gin" "github.com/imdotdev/im.dev/server/internal/story" @@ -51,18 +50,16 @@ func DeletePost(c *gin.Context) { func GetPost(c *gin.Context) { id := c.Param("id") + user := user.CurrentUser(c) ar, err := story.GetPost(id, "") if err != nil { c.JSON(err.Status, common.RespError(err.Message)) return } - user := user.CurrentUser(c) - if user == nil { - ar.Liked = false - } else { + if user != nil { ar.Liked = story.GetLiked(ar.ID, user.ID) - + ar.Bookmarked, _ = story.Bookmarked(user.ID, ar.ID) } c.JSON(http.StatusOK, common.RespSuccess(ar)) @@ -85,59 +82,44 @@ func LikeStory(c *gin.Context) { c.JSON(http.StatusOK, common.RespSuccess(nil)) } -func GetUserPosts(c *gin.Context) { - userID, _ := strconv.ParseInt(c.Param("userID"), 10, 64) +func Bookmark(c *gin.Context) { + storyID := c.Param("storyID") - posts, err := story.UserPosts(userID) + user := user.CurrentUser(c) + + err := story.Bookmark(user.ID, storyID) if err != nil { c.JSON(err.Status, common.RespError(err.Message)) return } - user := user.CurrentUser(c) - if user != nil { - for _, post := range posts { - post.Liked = story.GetLiked(post.ID, user.ID) - } - } - - c.JSON(http.StatusOK, common.RespSuccess(posts)) + c.JSON(http.StatusOK, common.RespSuccess(nil)) } -func GetTagPosts(c *gin.Context) { - tagID, _ := strconv.ParseInt(c.Param("id"), 10, 64) +func GetEditorPost(c *gin.Context) { + id := c.Param("id") + if id == "" { + c.JSON(http.StatusBadRequest, common.RespError(e.ParamInvalid)) + return + } - posts, err := story.TagPosts(tagID) + user := user.CurrentUser(c) + creator, err := story.GetPostCreator(id) if err != nil { c.JSON(err.Status, common.RespError(err.Message)) return } - user := user.CurrentUser(c) - if user != nil { - for _, post := range posts { - post.Liked = story.GetLiked(post.ID, user.ID) - } + if user.ID != creator { + c.JSON(http.StatusForbidden, common.RespError(e.NoPermission)) + return } - c.JSON(http.StatusOK, common.RespSuccess(posts)) -} - -func GetHomePosts(c *gin.Context) { - filter := c.Param("filter") - - posts, err := story.HomePosts(filter) + ar, err := story.GetPost(id, "") if err != nil { c.JSON(err.Status, common.RespError(err.Message)) return } - user := user.CurrentUser(c) - if user != nil { - for _, post := range posts { - post.Liked = story.GetLiked(post.ID, user.ID) - } - } - - c.JSON(http.StatusOK, common.RespSuccess(posts)) + c.JSON(http.StatusOK, common.RespSuccess(ar)) } diff --git a/server/internal/server.go b/server/internal/server.go index 2659c196..b489b1d0 100644 --- a/server/internal/server.go +++ b/server/internal/server.go @@ -79,6 +79,9 @@ func (s *Server) Start() error { r.GET("/home/posts/:filter", api.GetHomePosts) r.GET("/session", IsLogin(), api.GetSession) + + r.POST("/bookmark/:storyID", IsLogin(), api.Bookmark) + err := router.Run(config.Data.Server.Addr) if err != nil { logger.Crit("start backend server error", "error", err) diff --git a/server/internal/storage/sql_tables.go b/server/internal/storage/sql_tables.go index bc09474e..27241a82 100644 --- a/server/internal/storage/sql_tables.go +++ b/server/internal/storage/sql_tables.go @@ -141,4 +141,13 @@ var sqlTables = map[string]string{ count INTEGER DEFAULT 0 ); `, + + "bookmarks": `CREATE TABLE IF NOT EXISTS bookmarks ( + user_id INTEGER, + story_id VARCHAR(255), + created DATETIME + ); + CREATE INDEX IF NOT EXISTS bookmarks_userid + ON bookmarks (user_id); + `, } diff --git a/server/internal/story/bookmark.go b/server/internal/story/bookmark.go new file mode 100644 index 00000000..55d482fd --- /dev/null +++ b/server/internal/story/bookmark.go @@ -0,0 +1,52 @@ +package story + +import ( + "database/sql" + "net/http" + + "github.com/imdotdev/im.dev/server/pkg/db" + "github.com/imdotdev/im.dev/server/pkg/e" +) + +func Bookmark(userID int64, storyID string) *e.Error { + storyExist := Exist(storyID) + if !storyExist { + return e.New(http.StatusNotFound, e.NotFound) + } + + bookmarked, err := Bookmarked(userID, storyID) + if err != nil { + return e.New(http.StatusInternalServerError, e.Internal) + } + + if !bookmarked { + _, err = db.Conn.Exec("insert into bookmarks (user_id,story_id) values (?,?)", userID, storyID) + if err != nil { + logger.Warn("add bookmark error", "error", err) + return e.New(http.StatusInternalServerError, e.Internal) + } + } else { + _, err = db.Conn.Exec("delete from bookmarks where user_id=? and story_id=?", userID, storyID) + if err != nil { + logger.Warn("delete bookmark error", "error", err) + return e.New(http.StatusInternalServerError, e.Internal) + } + } + + return nil +} + +func Bookmarked(userID int64, storyID string) (bool, error) { + var nid string + err := db.Conn.QueryRow("select story_id from bookmarks where user_id=? and story_id=?", userID, storyID).Scan(&nid) + if err != nil && err != sql.ErrNoRows { + logger.Warn("get bookmarked error", "error", err) + return false, err + } + + if err == sql.ErrNoRows { + return false, nil + } + + return true, nil +} diff --git a/server/internal/story/post.go b/server/internal/story/post.go index c53f3cdf..c7081284 100644 --- a/server/internal/story/post.go +++ b/server/internal/story/post.go @@ -4,7 +4,6 @@ import ( "database/sql" "fmt" "net/http" - "sort" "strings" "time" "unicode/utf8" @@ -20,81 +19,6 @@ import ( "github.com/imdotdev/im.dev/server/pkg/utils" ) -func UserPosts(uid int64) (models.Posts, *e.Error) { - ars := make(models.Posts, 0) - rows, err := db.Conn.Query("select id,slug,title,url,cover,brief,likes,views,created,updated from posts where creator=?", uid) - if err != nil && err != sql.ErrNoRows { - logger.Warn("get user posts error", "error", err) - return ars, e.New(http.StatusInternalServerError, e.Internal) - } - - creator := &models.UserSimple{ID: uid} - creator.Query() - for rows.Next() { - ar := &models.Post{} - err := rows.Scan(&ar.ID, &ar.Slug, &ar.Title, &ar.URL, &ar.Cover, &ar.Brief, &ar.Likes, &ar.Views, &ar.Created, &ar.Updated) - if err != nil { - logger.Warn("scan post error", "error", err) - continue - } - - ar.Creator = creator - - ar.Comments = GetCommentCount(ar.ID) - ars = append(ars, ar) - } - - sort.Sort(ars) - return ars, nil -} - -func TagPosts(tagID int64) (models.Posts, *e.Error) { - ars := make(models.Posts, 0) - - // get post ids - rows, err := db.Conn.Query("select post_id from tag_post where tag_id=?", tagID) - if err != nil { - logger.Warn("get user posts error", "error", err) - return ars, e.New(http.StatusInternalServerError, e.Internal) - } - - postIDs := make([]string, 0) - for rows.Next() { - var id string - rows.Scan(&id) - postIDs = append(postIDs, id) - } - - ids := strings.Join(postIDs, "','") - - q := fmt.Sprintf("select id,slug,title,url,cover,brief,likes,views,creator,created,updated from posts where id in ('%s')", ids) - rows, err = db.Conn.Query(q) - if err != nil && err != sql.ErrNoRows { - logger.Warn("get user posts error", "error", err) - return ars, e.New(http.StatusInternalServerError, e.Internal) - } - - for rows.Next() { - ar := &models.Post{} - err := rows.Scan(&ar.ID, &ar.Slug, &ar.Title, &ar.URL, &ar.Cover, &ar.Brief, &ar.Likes, &ar.Views, &ar.CreatorID, &ar.Created, &ar.Updated) - if err != nil { - logger.Warn("scan post error", "error", err) - continue - } - - creator := &models.UserSimple{ID: ar.CreatorID} - creator.Query() - - ar.Creator = creator - - ar.Comments = GetCommentCount(ar.ID) - ars = append(ars, ar) - } - - sort.Sort(ars) - return ars, nil -} - func SubmitPost(c *gin.Context) (map[string]string, *e.Error) { user := user.CurrentUser(c) @@ -249,6 +173,7 @@ func GetPost(id string, slug string) (*models.Post, *e.Error) { logger.Warn("update post view count error", "error", err) } + //get bookmared return ar, nil } @@ -266,35 +191,6 @@ func GetPostCreator(id string) (int64, *e.Error) { return uid, nil } -func HomePosts(filter string) (models.Posts, *e.Error) { - ars := make(models.Posts, 0) - rows, err := db.Conn.Query("select id,slug,title,url,cover,brief,likes,views,creator,created,updated from posts") - if err != nil && err != sql.ErrNoRows { - logger.Warn("get user posts error", "error", err) - return ars, e.New(http.StatusInternalServerError, e.Internal) - } - - for rows.Next() { - ar := &models.Post{} - err := rows.Scan(&ar.ID, &ar.Slug, &ar.Title, &ar.URL, &ar.Cover, &ar.Brief, &ar.Likes, &ar.Views, &ar.CreatorID, &ar.Created, &ar.Updated) - if err != nil { - logger.Warn("scan post error", "error", err) - continue - } - - creator := &models.UserSimple{ID: ar.CreatorID} - creator.Query() - - ar.Creator = creator - - ar.Comments = GetCommentCount(ar.ID) - ars = append(ars, ar) - } - - sort.Sort(ars) - return ars, nil -} - func postExist(id string) bool { var nid string err := db.Conn.QueryRow("SELECT id from posts WHERE id=?", id).Scan(&nid) diff --git a/server/internal/story/posts.go b/server/internal/story/posts.go new file mode 100644 index 00000000..f150e402 --- /dev/null +++ b/server/internal/story/posts.go @@ -0,0 +1,101 @@ +package story + +import ( + "database/sql" + "fmt" + "net/http" + "sort" + "strings" + + "github.com/imdotdev/im.dev/server/pkg/db" + "github.com/imdotdev/im.dev/server/pkg/e" + "github.com/imdotdev/im.dev/server/pkg/models" +) + +func HomePosts(user *models.User, filter string) (models.Posts, *e.Error) { + + rows, err := db.Conn.Query("select id,slug,title,url,cover,brief,likes,views,creator,created,updated from posts") + if err != nil && err != sql.ErrNoRows { + logger.Warn("get user posts error", "error", err) + return nil, e.New(http.StatusInternalServerError, e.Internal) + } + + posts := getPosts(user, rows) + sort.Sort(posts) + + return posts, nil +} + +func UserPosts(user *models.User, uid int64) (models.Posts, *e.Error) { + rows, err := db.Conn.Query("select id,slug,title,url,cover,brief,likes,views,creator,created,updated from posts where creator=?", uid) + if err != nil && err != sql.ErrNoRows { + logger.Warn("get user posts error", "error", err) + return nil, e.New(http.StatusInternalServerError, e.Internal) + } + + posts := getPosts(user, rows) + + sort.Sort(posts) + return posts, nil +} + +func TagPosts(user *models.User, tagID int64) (models.Posts, *e.Error) { + // get post ids + rows, err := db.Conn.Query("select post_id from tag_post where tag_id=?", tagID) + if err != nil { + logger.Warn("get user posts error", "error", err) + return nil, e.New(http.StatusInternalServerError, e.Internal) + } + + postIDs := make([]string, 0) + for rows.Next() { + var id string + rows.Scan(&id) + postIDs = append(postIDs, id) + } + + ids := strings.Join(postIDs, "','") + + q := fmt.Sprintf("select id,slug,title,url,cover,brief,likes,views,creator,created,updated from posts where id in ('%s')", ids) + rows, err = db.Conn.Query(q) + if err != nil && err != sql.ErrNoRows { + logger.Warn("get user posts error", "error", err) + return nil, e.New(http.StatusInternalServerError, e.Internal) + } + + posts := getPosts(user, rows) + + sort.Sort(posts) + return posts, nil +} + +func getPosts(user *models.User, rows *sql.Rows) models.Posts { + posts := make(models.Posts, 0) + for rows.Next() { + ar := &models.Post{} + err := rows.Scan(&ar.ID, &ar.Slug, &ar.Title, &ar.URL, &ar.Cover, &ar.Brief, &ar.Likes, &ar.Views, &ar.CreatorID, &ar.Created, &ar.Updated) + if err != nil { + logger.Warn("scan post error", "error", err) + continue + } + + // 获取作者信息 + creator := &models.UserSimple{ID: ar.CreatorID} + creator.Query() + ar.Creator = creator + + // 获取评论信息 + ar.Comments = GetCommentCount(ar.ID) + + // 获取当前登录用户的like + if user != nil { + ar.Liked = GetLiked(ar.ID, user.ID) + } + + // 获取当前登录用户的bookmark + ar.Bookmarked, _ = Bookmarked(user.ID, ar.ID) + posts = append(posts, ar) + } + + return posts +} diff --git a/server/pkg/models/post.go b/server/pkg/models/post.go index 481a5950..88e8bb5a 100644 --- a/server/pkg/models/post.go +++ b/server/pkg/models/post.go @@ -9,24 +9,25 @@ const ( ) type Post struct { - ID string `json:"id"` - Creator *UserSimple `json:"creator"` - CreatorID int64 `json:"creatorId"` - Title string `json:"title"` - Slug string `json:"slug"` - Md string `json:"md"` - URL string `json:"url"` - Cover string `json:"cover"` - Brief string `json:"brief"` - Tags []int64 `json:"tags"` - RawTags []*Tag `json:"rawTags"` - Likes int `json:"likes"` - Liked bool `json:"liked"` - Comments int `json:"comments"` - Views int `json:"views"` - Status int `json:"status"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` + ID string `json:"id"` + Creator *UserSimple `json:"creator"` + CreatorID int64 `json:"creatorId"` + Title string `json:"title"` + Slug string `json:"slug"` + Md string `json:"md"` + URL string `json:"url"` + Cover string `json:"cover"` + Brief string `json:"brief"` + Tags []int64 `json:"tags"` + RawTags []*Tag `json:"rawTags"` + Likes int `json:"likes"` + Liked bool `json:"liked"` + Comments int `json:"comments"` + Views int `json:"views"` + Bookmarked bool `json:"bookmarked"` + Status int `json:"status"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` } type Posts []*Post diff --git a/src/components/comments/comment.tsx b/src/components/comments/comment.tsx index 645fe20d..9efcb953 100644 --- a/src/components/comments/comment.tsx +++ b/src/components/comments/comment.tsx @@ -5,7 +5,7 @@ import Card from "components/card" import { getUserName } from "utils/user" import moment from 'moment' import { MarkdownRender } from "components/markdown-editor/render" -import HeartLike from "components/posts/heart-like" +import Like from "components/posts/like" import { FaRegEdit, FaRegFlag, FaRegTrashAlt, FaReply, FaTrash } from "react-icons/fa" import { User } from "src/types/session" import CommentEditor from "./editor" @@ -40,11 +40,6 @@ export const CommentCard = (props: Props) => { onChange() } - const likeComment = async (id) => { - await requestApi.post(`/story/like/${id}`) - onChange() - } - return ( <>{ editorVisible ? (user && {setEditorVisible(false);changeComment(md)}} onCancel={() => setEditorVisible(false)} menu={false}/>) : @@ -66,7 +61,7 @@ export const CommentCard = (props: Props) => { - likeComment(comment.id)} /> + {user && { - likeReply(comment.id)} /> + {user && { + return ( + {shortNumber(props.count)} + ) +} + +export default Count diff --git a/src/components/posts/bookmark.tsx b/src/components/posts/bookmark.tsx new file mode 100644 index 00000000..96c22020 --- /dev/null +++ b/src/components/posts/bookmark.tsx @@ -0,0 +1,35 @@ +import { chakra, HStack, IconButton, Image, Tooltip, useColorMode, useColorModeValue } from "@chakra-ui/react"; +import SvgButton from "components/svg-button"; +import { useState } from "react"; +import { FaHeart, FaRegHeart } from "react-icons/fa"; +import { requestApi } from "utils/axios/request"; + +interface Props { + storyID: string + bookmarked: boolean + height?: string +} + +const Bookmark = (props: Props) => { + const {storyID, height="1.4rem"} = props + + const [bookmarked,setBookmarked] = useState(props.bookmarked) + const bookmark = async () => { + await requestApi.post(`/bookmark/${storyID}`) + setBookmarked(!bookmarked) + } + + return ( + + ) +} + +export default Bookmark \ No newline at end of file diff --git a/src/components/posts/heart-like.tsx b/src/components/posts/heart-like.tsx deleted file mode 100644 index 4f953d41..00000000 --- a/src/components/posts/heart-like.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { chakra, HStack, IconButton, Image, Tooltip, useColorMode, useColorModeValue } from "@chakra-ui/react"; -import { FaHeart, FaRegHeart } from "react-icons/fa"; - -interface Props { - count: number - onClick: any - liked: boolean -} -const UnicornLike = (props: Props) => { - const label = "I like it" - return ( - - - {props.liked? } - onClick={props.onClick} - fontSize="20px" - /> : - } - onClick={props.onClick} - fontSize="20px" - />} - - - {props.count} - - ) -} - -export default UnicornLike \ No newline at end of file diff --git a/src/components/posts/like.tsx b/src/components/posts/like.tsx new file mode 100644 index 00000000..145dc297 --- /dev/null +++ b/src/components/posts/like.tsx @@ -0,0 +1,61 @@ +import { chakra, HStack, IconButton, Tooltip} from "@chakra-ui/react"; +import Count from "components/count"; +import { useState } from "react"; +import { FaHeart, FaRegHeart } from "react-icons/fa"; +import { requestApi } from "utils/axios/request"; + +interface Props { + storyID: string + count: number + liked: boolean + fontSize?: string + spacing?: string +} +const Like = (props: Props) => { + const {fontSize="20px",spacing="0"} = props + const label = "I like it" + + const [liked,setLiked] = useState(props.liked) + const [count,setCount] = useState(props.count) + const like = async () => { + await requestApi.post(`/story/like/${props.storyID}`) + + + if (liked) { + setCount(count-1) + } else { + setCount(count+1) + } + + setLiked(!liked) + } + + return ( + + + {liked? } + onClick={like} + fontSize={fontSize} + /> : + } + onClick={like} + fontSize={fontSize} + />} + + + + + ) +} + +export default Like \ No newline at end of file diff --git a/src/components/posts/no-more-posts.tsx b/src/components/posts/no-more-posts.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/components/posts/post-card.tsx b/src/components/posts/post-card.tsx index db117c09..980808f7 100644 --- a/src/components/posts/post-card.tsx +++ b/src/components/posts/post-card.tsx @@ -3,9 +3,11 @@ import { Box, chakra, Flex, Heading, HStack, Image, Text, useMediaQuery, VStack import { Post } from "src/types/posts" import PostAuthor from "./post-author" import Link from "next/link" -import UnicornLike from "./heart-like" -import { FaHeart, FaRegBookmark, FaRegComment, FaRegHeart } from "react-icons/fa" -import SvgButton from "components/svg-button" +import Like from "./like" +import { FaHeart, FaRegHeart } from "react-icons/fa" +import Bookmark from "./bookmark" +import { getSvgIcon } from "components/svg-icon" +import Count from "components/count" interface Props { post: Post @@ -30,24 +32,17 @@ export const PostCard = (props: Props) => { - - {post.liked ? - - : - } - {post.likes} - - + - - - {post.comments} - + + {getSvgIcon("comments", "1.3rem")} + + - + ) diff --git a/src/components/posts/post-sidebar.tsx b/src/components/posts/post-sidebar.tsx new file mode 100644 index 00000000..83960e92 --- /dev/null +++ b/src/components/posts/post-sidebar.tsx @@ -0,0 +1,57 @@ +import React from "react" +import { Box, BoxProps, useColorModeValue, VStack } from "@chakra-ui/react" +import { Post } from "src/types/posts" +import useSession from "hooks/use-session" +import Like from "./like" +import Bookmark from "./bookmark" +import SvgButton from "components/svg-button" +import { useRouter } from "next/router" +import { ReserveUrls } from "src/data/reserve-urls" + +interface Props { + post: Post + vertical?: boolean +} + +export const PostSidebar = (props: Props) => { + const {post,vertical = true} = props + const session = useSession() + const router = useRouter() + return ( + + + + + + + + + + location.href = "#comments"} + /> + + + {post.creatorId === session?.user.id && + router.push(`${ReserveUrls.Editor}/post/${post.id}`)} + icon="edit" + /> + } + + + ) +} + +export default PostSidebar diff --git a/src/components/svg-button.tsx b/src/components/svg-button.tsx index 9b773109..5ec8cbbe 100644 --- a/src/components/svg-button.tsx +++ b/src/components/svg-button.tsx @@ -2,25 +2,28 @@ import React from "react" import {chakra, PropsOf, IconButton } from "@chakra-ui/react" type Props = PropsOf & { - icon: 'bookmark' | 'edit' | 'share' + icon: any onClick: any height?: string } export const SvgButton= (props:Props) =>{ - const {icon,height="1.7rem",...rest} = props + const {icon,height="1.6rem",...rest} = props let iconSvg switch (icon) { + case "bookmarked": + iconSvg = + break; case "bookmark": iconSvg = break; case "share": - iconSvg = + iconSvg = break case "edit": - iconSvg = + iconSvg = break default: break; diff --git a/src/components/svg-icon.tsx b/src/components/svg-icon.tsx index 2e9871e5..c8828c23 100644 --- a/src/components/svg-icon.tsx +++ b/src/components/svg-icon.tsx @@ -1,6 +1,9 @@ export function getSvgIcon(name,height="1.4rem") { let svg switch (name) { + case "comments": + svg = + break case "hot": svg = break diff --git a/src/types/posts.ts b/src/types/posts.ts index f16f591c..15872510 100644 --- a/src/types/posts.ts +++ b/src/types/posts.ts @@ -23,4 +23,5 @@ export interface Post { likes? : number liked? : boolean comments? : number + bookmarked?: boolean } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 7a6842fc..cd0f790b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3735,6 +3735,11 @@ shell-quote@1.7.2: resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== +short-number@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/short-number/-/short-number-1.0.7.tgz#5ab8021b86f20a4bdd1bbe01a85c22ff1cd15514" + integrity sha512-e3cb811QXHiAH2H5rgZwXHsmg2+aG6XK0QyKF/+pyDIZFBSz/G8bkr8avdBrL5PVIfhIq8rlohjFvSB3azzpEQ== + signal-exit@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"