diff --git a/layouts/nav/nav.tsx b/layouts/nav/nav.tsx
index b2ff91e8..070100d7 100644
--- a/layouts/nav/nav.tsx
+++ b/layouts/nav/nav.tsx
@@ -20,7 +20,7 @@ import { useRouter } from "next/router"
import { ReserveUrls } from "src/data/reserve-urls"
import Link from "next/link"
import DarkMode from "components/dark-mode"
-import AccountMenu from "components/account-menu"
+import AccountMenu from "components/user-menu"
const navLinks = [{
title: '主页',
diff --git a/layouts/nav/post-nav.tsx b/layouts/nav/post-nav.tsx
index a651e092..32b8b943 100644
--- a/layouts/nav/post-nav.tsx
+++ b/layouts/nav/post-nav.tsx
@@ -15,7 +15,7 @@ import { useViewportScroll } from "framer-motion"
import React from "react"
import { SearchIcon } from "@chakra-ui/icons"
import DarkMode from "components/dark-mode"
-import AccountMenu from "components/account-menu"
+import AccountMenu from "components/user-menu"
import { FaGithub, FaTwitter, FaUserPlus } from "react-icons/fa"
diff --git a/layouts/nav/vertical-nav.tsx b/layouts/nav/vertical-nav.tsx
index 6b88320a..4cbbd3bc 100644
--- a/layouts/nav/vertical-nav.tsx
+++ b/layouts/nav/vertical-nav.tsx
@@ -23,7 +23,7 @@ import {
import { ReserveUrls } from "src/data/reserve-urls"
import Link from "next/link"
import DarkMode from "components/dark-mode"
- import AccountMenu from "components/account-menu"
+ import AccountMenu from "components/user-menu"
import { getSvgIcon } from "components/svg-icon"
const navLinks = [{
diff --git a/pages/bookmarks.tsx b/pages/bookmarks.tsx
new file mode 100644
index 00000000..8f4abcb6
--- /dev/null
+++ b/pages/bookmarks.tsx
@@ -0,0 +1,122 @@
+import {
+ chakra, Flex, Heading, HStack, Text, VStack, Menu,
+ MenuButton,
+ MenuList,
+ MenuItem,
+ IconButton,
+ Divider,
+ Wrap,
+ Image,
+ useColorModeValue
+ } from "@chakra-ui/react"
+ import SEO from "components/seo"
+ import siteConfig from "configs/site-config"
+ import PageContainer1 from "layouts/page-container1"
+ import React, { useEffect, useState } from "react"
+ import { HomeSidebar } from 'pages/index'
+ import Card from "components/card"
+ import { config } from "configs/config"
+ import { getSvgIcon } from "components/svg-icon"
+ import { Tag } from "src/types/tag"
+ import { requestApi } from "utils/axios/request"
+ import TagCard from 'src/components/tags/tag-card'
+import { Post } from "src/types/posts"
+import Posts from "components/posts/posts"
+import { find } from "lodash"
+import userCustomTheme from "theme/user-custom"
+import Empty from "components/empty"
+
+
+
+ const BookmarksPage = () => {
+ const [filter, setFilter]:[Tag,any] = useState({id:-1})
+ const [tags, setTags]: [Tag[], any] = useState([])
+ const [rawPosts,setRawPosts]: [Post[],any] = useState([])
+ const [posts,setPosts]: [Post[],any] = useState([])
+
+ useEffect(() => {
+ getBookmarkPosts()
+ }, [])
+
+ useEffect(() => {
+ filterPosts()
+ }, [filter])
+
+ const getBookmarkPosts = async() => {
+ const res = await requestApi.get(`/bookmark/posts`)
+ setRawPosts(res.data)
+ setPosts(res.data)
+ const ts = [{id:-1,title:'All Tags',icon: 'https://cdn.hashnode.com/res/hashnode/image/upload/v1605105898259/3vuMFM8qM.png?w=200&h=200&fit=crop&crop=entropy&auto=compress&auto=compress'}]
+ res.data.forEach(post => {
+ post.rawTags?.forEach(tag => {
+ if (!find(ts, t => t.id === tag.id)) {
+ ts.push(tag)
+ }
+ })
+ })
+
+ setTags(ts)
+ }
+
+ const filterPosts = () => {
+ if (filter.id === -1) {
+ setPosts(rawPosts)
+ return
+ }
+ const newPosts = []
+ rawPosts.forEach(post => {
+ post.rawTags?.forEach(tag => {
+ if (tag.id === filter.id) {
+ newPosts.push(post)
+ }
+ })
+ })
+
+ setPosts(newPosts)
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+ Bookmarks
+ All the discussions, stories and comments you have bookmarked on {config.appName}.
+
+
+
+
+
+ {
+ tags.map(t =>
+ setFilter(t)}>
+
+ {t.title}
+ )
+ }
+
+
+ {posts.length !== 0
+ ?
+
+ :
+
+ }
+
+
+
+
+
+ >
+ )
+ }
+
+ export default BookmarksPage
+
+
\ No newline at end of file
diff --git a/pages/index.tsx b/pages/index.tsx
index 83ea1ab8..ffd60fe5 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -68,7 +68,7 @@ const HomePage = () => {
-
+
diff --git a/server/internal/api/posts.go b/server/internal/api/posts.go
index 60dd522e..ad88c461 100644
--- a/server/internal/api/posts.go
+++ b/server/internal/api/posts.go
@@ -58,3 +58,16 @@ func GetHomePosts(c *gin.Context) {
c.JSON(http.StatusOK, common.RespSuccess(posts))
}
+
+func GetBookmarkPosts(c *gin.Context) {
+ filter := c.Param("filter")
+ user := user.CurrentUser(c)
+
+ posts, err := story.BookmarkPosts(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/server.go b/server/internal/server.go
index b489b1d0..16a2d653 100644
--- a/server/internal/server.go
+++ b/server/internal/server.go
@@ -81,6 +81,7 @@ func (s *Server) Start() error {
r.GET("/session", IsLogin(), api.GetSession)
r.POST("/bookmark/:storyID", IsLogin(), api.Bookmark)
+ r.GET("/bookmark/posts", IsLogin(), api.GetBookmarkPosts)
err := router.Run(config.Data.Server.Addr)
if err != nil {
diff --git a/server/internal/story/posts.go b/server/internal/story/posts.go
index f150e402..02087e61 100644
--- a/server/internal/story/posts.go
+++ b/server/internal/story/posts.go
@@ -7,6 +7,7 @@ import (
"sort"
"strings"
+ "github.com/imdotdev/im.dev/server/internal/tags"
"github.com/imdotdev/im.dev/server/pkg/db"
"github.com/imdotdev/im.dev/server/pkg/e"
"github.com/imdotdev/im.dev/server/pkg/models"
@@ -69,6 +70,46 @@ func TagPosts(user *models.User, tagID int64) (models.Posts, *e.Error) {
return posts, nil
}
+func BookmarkPosts(user *models.User, filter string) (models.Posts, *e.Error) {
+ // get post ids
+ rows, err := db.Conn.Query("select story_id from bookmarks where user_id=?", user.ID)
+ if err != nil {
+ logger.Warn("get bookmarks 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)
+
+ for _, post := range posts {
+ ts, err := tags.GetStoryTags(post.ID)
+ if err != nil {
+ logger.Warn("get story tags error", "error", err)
+ continue
+ }
+
+ post.RawTags = ts
+ }
+
+ sort.Sort(posts)
+ return posts, nil
+}
+
func getPosts(user *models.User, rows *sql.Rows) models.Posts {
posts := make(models.Posts, 0)
for rows.Next() {
diff --git a/server/internal/tags/tags.go b/server/internal/tags/tags.go
index 9418f757..beb5658e 100644
--- a/server/internal/tags/tags.go
+++ b/server/internal/tags/tags.go
@@ -133,3 +133,41 @@ func GetTag(id int64, name string) (*models.Tag, *e.Error) {
tag.SetCover()
return tag, nil
}
+
+func GetSimpleTag(id int64, name string) (*models.Tag, *e.Error) {
+ tag := &models.Tag{}
+ err := db.Conn.QueryRow("SELECT id,title,icon from tags where id=? or name=?", id, name).Scan(
+ &tag.ID, &tag.Title, &tag.Icon,
+ )
+ if err != nil {
+ if err == sql.ErrNoRows {
+ return nil, e.New(http.StatusNotFound, e.NotFound)
+ }
+ logger.Warn("get tag error", "error", err)
+ return nil, e.New(http.StatusInternalServerError, e.Internal)
+ }
+
+ return tag, nil
+}
+
+func GetStoryTags(storyID string) ([]*models.Tag, error) {
+ ids := make([]int64, 0)
+ rows, err := db.Conn.Query("SELECT tag_id FROM tag_post WHERE post_id=?", storyID)
+ if err != nil {
+ return nil, err
+ }
+
+ rawTags := make([]*models.Tag, 0)
+ for rows.Next() {
+ var id int64
+ err = rows.Scan(&id)
+ ids = append(ids, id)
+
+ rawTag, err := GetSimpleTag(id, "")
+ if err == nil {
+ rawTags = append(rawTags, rawTag)
+ }
+ }
+
+ return rawTags, nil
+}
diff --git a/server/pkg/models/tag.go b/server/pkg/models/tag.go
index 7691297c..dbd392d5 100644
--- a/server/pkg/models/tag.go
+++ b/server/pkg/models/tag.go
@@ -4,15 +4,15 @@ import "time"
type Tag struct {
ID int64 `json:"id"`
- Creator int64 `json:"creator"`
+ Creator int64 `json:"creator,omitempty"`
Title string `json:"title"`
- Name string `json:"name"`
- Md string `json:"md"`
- Cover string `json:"cover"`
+ Name string `json:"name,omitempty"`
+ Md string `json:"md,omitempty"`
+ Cover string `json:"cover,omitempty"`
Icon string `json:"icon"`
- PostCount int `json:"postCount"`
- Created time.Time `json:"created"`
- Updated time.Time `json:"updated"`
+ PostCount int `json:"postCount,omitempty"`
+ Created time.Time `json:"created,omitempty"`
+ Updated time.Time `json:"updated,omitempty"`
}
func (t *Tag) SetCover() {
diff --git a/src/components/posts/posts.tsx b/src/components/posts/posts.tsx
index 57a2ed8f..5e827abe 100644
--- a/src/components/posts/posts.tsx
+++ b/src/components/posts/posts.tsx
@@ -16,11 +16,22 @@ export const Posts = (props: Props) => {
const { posts,card=PostCard,showFooter=true} = props
const postBorderColor = useColorModeValue(userCustomTheme.borderColor.light, userCustomTheme.borderColor.dark)
const Card = card
+ const showBorder = i => {
+ if (i < posts.length -1) {
+ return true
+ }
+
+ if (showFooter) {
+ return true
+ } else {
+ return false
+ }
+ }
return (
<>
{posts.map((post,i) =>
-
+
)}
diff --git a/src/components/posts/simple-post-card.tsx b/src/components/posts/simple-post-card.tsx
index e46d9044..241a1adb 100644
--- a/src/components/posts/simple-post-card.tsx
+++ b/src/components/posts/simple-post-card.tsx
@@ -3,7 +3,7 @@ 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 UnicornLike from "./like"
import { FaHeart, FaRegBookmark, FaRegComment, FaRegHeart } from "react-icons/fa"
import SvgButton from "components/svg-button"
diff --git a/src/components/tags/tag-card.tsx b/src/components/tags/tag-card.tsx
index 0fb7746a..12b5d4eb 100644
--- a/src/components/tags/tag-card.tsx
+++ b/src/components/tags/tag-card.tsx
@@ -22,7 +22,7 @@ export const TagCard= (props:Props) =>{
{tag.title}
- {tag.md}
+ {tag.md}
diff --git a/src/components/account-menu.tsx b/src/components/user-menu.tsx
similarity index 93%
rename from src/components/account-menu.tsx
rename to src/components/user-menu.tsx
index 0899aef6..aeb0fcdf 100644
--- a/src/components/account-menu.tsx
+++ b/src/components/user-menu.tsx
@@ -19,7 +19,7 @@ import { isAdmin, isEditor } from "utils/role"
import { logout } from "utils/session"
import Link from "next/link"
-export const AccountMenu = () => {
+export const UserMenu = () => {
const session: Session = useSession()
const router = useRouter()
@@ -57,7 +57,7 @@ export const AccountMenu = () => {
{isEditor(session.user.role) && } >创作中心}
{isAdmin(session.user.role) && } >管理员}
- }>书签收藏
+ }>书签收藏
}>偏好设置
@@ -77,4 +77,4 @@ export const AccountMenu = () => {
)
}
-export default AccountMenu
+export default UserMenu
diff --git a/theme.ts b/theme.ts
index f2e700e0..5569de20 100644
--- a/theme.ts
+++ b/theme.ts
@@ -22,7 +22,9 @@ const customTheme = extendTheme({
"0 0 0 1px rgba(16,22,26,.1), 0 4px 8px rgba(16,22,26,.2), 0 18px 46px 6px rgba(16,22,26,.2)",
},
styles: {
- global: (props) => ({
+ global: (props) => {
+ console.log(props)
+ return ({
'.hover-bg:hover': {
background: mode(userCustomTheme.hoverBg.light,userCustomTheme.hoverBg.dark )(props),
borderRadius: '6px'
@@ -35,6 +37,11 @@ const customTheme = extendTheme({
boxShadow: 'rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px',
borderRadius: '6px'
},
+ '.tag-bg': {
+ background: mode(props.theme.colors.cyan['100'],'rgba(157, 236, 249, 0.16)' )(props),
+ color: mode(props.theme.colors.cyan['800'],props.theme.colors.cyan['200'] )(props),
+ borderRadius: '6px'
+ },
body: {
background: mode("white","gray.800" )(props),
color: mode("gray.700", "whiteAlpha.900")(props),
@@ -51,7 +58,7 @@ const customTheme = extendTheme({
},
...markdownEditor(props),
...markdownRender(props)
- }),
+ })},
},
textStyles: {
heading: {
diff --git a/theme/user-custom.js b/theme/user-custom.js
index 5fe64e04..80045a51 100644
--- a/theme/user-custom.js
+++ b/theme/user-custom.js
@@ -8,7 +8,7 @@ const userCustomTheme = {
hoverBg: {
light: theme.colors.gray['100'],
dark: theme.colors.whiteAlpha['200']
- },
+ }
}
export default userCustomTheme
\ No newline at end of file